"""
Configuration de base pour SQLAlchemy.

Ce module contient la configuration de la base de données, la création du moteur,
et les utilitaires pour les sessions.
"""

from contextlib import contextmanager
from typing import Generator

from sqlalchemy import create_engine
from sqlalchemy.engine import Engine
from sqlalchemy.orm import DeclarativeBase, Session, sessionmaker

# Configuration par défaut (peut être surchargée par variables d'environnement)
DEFAULT_DATABASE_URL = "sqlite:///pipeline_mco_pmsi.db"


class Base(DeclarativeBase):
    """Classe de base pour tous les modèles SQLAlchemy."""

    pass


# Engine global (sera initialisé par get_engine)
_engine: Engine | None = None
_session_factory: sessionmaker[Session] | None = None


def get_engine(database_url: str | None = None, echo: bool = False) -> Engine:
    """
    Obtient ou crée le moteur SQLAlchemy.

    Args:
        database_url: URL de connexion à la base de données
        echo: Si True, affiche les requêtes SQL

    Returns:
        Engine SQLAlchemy
    """
    global _engine

    if _engine is None:
        url = database_url or DEFAULT_DATABASE_URL
        _engine = create_engine(url, echo=echo, pool_pre_ping=True)

    return _engine


def get_session_factory(engine: Engine | None = None) -> sessionmaker[Session]:
    """
    Obtient ou crée la factory de sessions.

    Args:
        engine: Engine SQLAlchemy (utilise l'engine global si None)

    Returns:
        Session factory
    """
    global _session_factory

    if _session_factory is None:
        eng = engine or get_engine()
        _session_factory = sessionmaker(bind=eng, expire_on_commit=False)

    return _session_factory


@contextmanager
def get_session(engine: Engine | None = None) -> Generator[Session, None, None]:
    """
    Context manager pour obtenir une session de base de données.

    Args:
        engine: Engine SQLAlchemy (utilise l'engine global si None)

    Yields:
        Session SQLAlchemy

    Example:
        >>> with get_session() as session:
        ...     stay = session.query(StayDB).first()
    """
    factory = get_session_factory(engine)
    session = factory()
    try:
        yield session
        session.commit()
    except Exception:
        session.rollback()
        raise
    finally:
        session.close()


def create_all_tables(engine: Engine | None = None) -> None:
    """
    Crée toutes les tables dans la base de données.

    Args:
        engine: Engine SQLAlchemy (utilise l'engine global si None)
    """
    eng = engine or get_engine()
    Base.metadata.create_all(eng)


def drop_all_tables(engine: Engine | None = None) -> None:
    """
    Supprime toutes les tables de la base de données.

    ATTENTION: Cette fonction supprime toutes les données!

    Args:
        engine: Engine SQLAlchemy (utilise l'engine global si None)
    """
    eng = engine or get_engine()
    Base.metadata.drop_all(eng)


def get_db() -> Generator[Session, None, None]:
    """
    Dépendance FastAPI pour obtenir une session de base de données.

    Yields:
        Session SQLAlchemy

    Example:
        >>> @app.get("/items")
        >>> def read_items(db: Session = Depends(get_db)):
        ...     return db.query(Item).all()
    """
    factory = get_session_factory()
    session = factory()
    try:
        yield session
    finally:
        session.close()
