"""SQLAlchemy engine + session factory.

The DB password lives in ``/run/secrets/pg_password``; we read it
from there (or ``$PG_PASSWORD`` for local dev) and splice it into
``DATABASE_URL`` so the password never has to be in plaintext in
``compose.yml`` or process environment listings.
"""

from __future__ import annotations

import os
from pathlib import Path
from typing import Generator
from urllib.parse import quote_plus, urlparse, urlunparse

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

from app.config import get_settings


def _resolve_password() -> str | None:
    inline = os.environ.get("PG_PASSWORD")
    if inline:
        return inline.strip()
    path = os.environ.get("PG_PASSWORD_FILE")
    if path and Path(path).exists():
        return Path(path).read_text(encoding="utf-8").strip()
    return None


def _build_url(base_url: str) -> str:
    """Inject the resolved password into ``base_url`` if absent."""
    parsed = urlparse(base_url)
    if parsed.password:
        return base_url
    pw = _resolve_password()
    if pw is None:
        return base_url
    netloc = f"{parsed.username or ''}:{quote_plus(pw)}@{parsed.hostname}"
    if parsed.port:
        netloc += f":{parsed.port}"
    return urlunparse(parsed._replace(netloc=netloc))


_settings = get_settings()
engine = create_engine(_build_url(_settings.database_url), pool_pre_ping=True, future=True)
SessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False, expire_on_commit=False)


class Base(DeclarativeBase):
    """Declarative base for ORM models."""


def get_session() -> Generator[Session, None, None]:
    """FastAPI dependency. Commits on success, rolls back on exception."""
    session = SessionLocal()
    try:
        yield session
        session.commit()
    except Exception:
        session.rollback()
        raise
    finally:
        session.close()
