"""
scrylo/db/session.py
---------------------
Engine creation, session factory, and FastAPI dependency.
Uses SQLite at ~/.scrylo/scrylo.db — zero infrastructure required.
"""

from __future__ import annotations

from contextlib import contextmanager
from typing import Generator

from sqlalchemy import create_engine, event
from sqlalchemy.orm import Session, sessionmaker

from scrylo.core.config import DB_PATH
from scrylo.db.models import Base

# ── Engine ────────────────────────────────────────────────────────────────
# check_same_thread=False required for FastAPI async context
engine = create_engine(
    f"sqlite:///{DB_PATH}",
    connect_args={"check_same_thread": False},
    echo=False,
)

# Enable WAL mode for better concurrent read performance
@event.listens_for(engine, "connect")
def set_sqlite_pragma(dbapi_connection, connection_record):
    cursor = dbapi_connection.cursor()
    cursor.execute("PRAGMA journal_mode=WAL")
    cursor.execute("PRAGMA foreign_keys=ON")
    cursor.close()


SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)


def init_db() -> None:
    """
    Create all tables if they don't exist, then run additive migrations.
    Safe to call multiple times — idempotent.
    """
    Base.metadata.create_all(bind=engine)
    # Additive migrations — adds new columns to existing DBs
    try:
        from scrylo.db.migrate import run_migrations
        run_migrations()
    except Exception:
        pass


def get_db() -> Generator[Session, None, None]:
    """
    FastAPI dependency — yields a session, always closes it after the request.

    Usage in a route:
        @router.get("/leads")
        def list_leads(db: Session = Depends(get_db)):
            ...
    """
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()


@contextmanager
def db_session() -> Generator[Session, None, None]:
    """
    Context manager for use outside FastAPI (CLI, scripts, tests).

    Usage:
        with db_session() as db:
            db.add(lead)
            db.commit()
    """
    db = SessionLocal()
    try:
        yield db
        db.commit()
    except Exception:
        db.rollback()
        raise
    finally:
        db.close()
