"""Database runtime ownership, schema bootstrap, and session access.

V1 moves database resource ownership to explicit runtime initialization so
startup/shutdown behavior is predictable and lifespan-managed.
"""

import contextlib
import logging
from collections.abc import Generator
from dataclasses import dataclass

from sqlalchemy import inspect, text
from sqlalchemy.engine import Engine
from sqlmodel import Session, SQLModel, create_engine

from transcription.config import Settings, get_settings

logger = logging.getLogger(__name__)


@dataclass(frozen=True)
class DatabaseRuntime:
    """Process-level database runtime resources."""

    engine: Engine


_runtime: DatabaseRuntime | None = None


def _build_engine(settings: Settings) -> Engine:
    connect_args: dict[str, object] = {}
    if settings.database_url.startswith("sqlite"):
        connect_args["check_same_thread"] = False
    return create_engine(
        settings.database_url,
        echo=False,
        connect_args=connect_args,
    )


def initialize_database_runtime(*, settings: Settings | None = None) -> DatabaseRuntime:
    """Initialize and cache the process database runtime once."""
    global _runtime

    if _runtime is not None:
        return _runtime

    runtime_settings = settings or get_settings()
    _runtime = DatabaseRuntime(engine=_build_engine(runtime_settings))
    return _runtime


def get_database_runtime() -> DatabaseRuntime:
    """Return initialized database runtime, creating it if needed."""
    if _runtime is None:
        return initialize_database_runtime()
    return _runtime


def dispose_database_runtime() -> None:
    """Dispose process database runtime resources."""
    global _runtime
    if _runtime is not None:
        _runtime.engine.dispose()
    _runtime = None


def should_bootstrap_schema(settings: Settings) -> bool:
    """Return whether startup should auto-create schema for this environment."""
    if settings.bootstrap_schema_on_startup is not None:
        return settings.bootstrap_schema_on_startup
    return settings.environment in {"development", "test"}


def create_all(*, engine: Engine | None = None) -> None:
    """Create all tables on the selected engine."""
    # Import models so SQLModel metadata is fully registered before bootstrap.
    from transcription import models as _models  # noqa: F401

    active_engine = engine or get_database_runtime().engine
    SQLModel.metadata.create_all(active_engine)
    _ensure_sqlite_compat_columns(active_engine)


def _ensure_sqlite_compat_columns(engine: Engine) -> None:
    """Apply lightweight dev/test SQLite compatibility column patches.

    This keeps local bootstrap resilient when models evolve but no full
    migration tooling is in place yet.
    """
    if engine.url.get_backend_name() != "sqlite":
        return

    inspector = inspect(engine)
    table_names = set(inspector.get_table_names())
    if "job" not in table_names:
        return

    columns = {column["name"] for column in inspector.get_columns("job")}
    if "retry_count" not in columns:
        with engine.begin() as connection:
            connection.execute(
                text("ALTER TABLE job ADD COLUMN retry_count INTEGER NOT NULL DEFAULT 0")
            )
        logger.warning(
            "Applied SQLite compatibility schema patch table=job column=retry_count default=0"
        )


@contextlib.contextmanager
def get_session(*, engine: Engine | None = None) -> Generator[Session]:
    """Yield a database session and ensure cleanup."""
    active_engine = engine or get_database_runtime().engine
    with Session(active_engine) as session:
        yield session
