"""Docker entrypoint: wait for DB, reconcile Alembic state, migrate, start API."""

from __future__ import annotations

import sys
import time
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
if str(ROOT) not in sys.path:
    sys.path.insert(0, str(ROOT))

from alembic import command
from alembic.config import Config
from sqlalchemy import create_engine, inspect, text

from app.core.install_secrets import ensure_install_secrets
from app.core.config import settings
from app.core.server_logging import append_server_log_line, build_uvicorn_log_config, ensure_server_log_file

INITIAL_REVISION = "20260711_0001"
HEAD_REVISION = "20260716_0009"
SCHEMA_TABLES = (
    "content_pages",
    "email_verification_tokens",
    "password_reset_tokens",
    "refresh_tokens",
    "user_profiles",
    "users",
    "enterprises",
    "enterprise_members",
    "farm_hubs",
    "user_farm_access",
    "hub_credentials",
    "hub_pairing_sessions",
    "sync_outbox",
    "sync_event_log",
    "sync_applied_events",
    "sync_cursors",
    "sync_record_state",
    "sync_conflicts",
    "sync_reconcile_runs",
    "report_sync_state",
    "zootech_component",
    "zootech_recipe",
    "zootech_ingredient",
)


def wait_for_database(max_attempts: int = 30, delay_seconds: float = 1.0):
    if settings.database_url.startswith("sqlite"):
        raise RuntimeError(
            "DATABASE_URL points to SQLite — Docker API requires PostgreSQL.\n"
            "Your apps/api/data/secrets/install.env was likely overwritten by a local test.\n"
            "Fix:\n"
            "  rm apps/api/data/secrets/install.env\n"
            "  python apps/api/scripts/bootstrap_install.py\n"
            "  docker compose --profile docker-web down -v\n"
            "  docker compose --profile docker-web up -d --build"
        )
    engine = create_engine(settings.database_url)
    last_error: Exception | None = None
    for _ in range(max_attempts):
        try:
            with engine.connect() as connection:
                connection.execute(text("SELECT 1"))
            return engine
        except Exception as exc:
            last_error = exc
            time.sleep(delay_seconds)
    detail = str(last_error or "unknown error")
    hint = ""
    if "password authentication failed" in detail or "does not exist" in detail:
        hint = (
            "\n\nPostgres credentials in install.env do not match the existing database volume "
            "(common if docker compose ran before bootstrap_install.py).\n"
            "Local dev fix — deletes Docker DB data:\n"
            "  docker compose --profile docker-web down -v\n"
            "  docker compose --profile docker-web up -d --build\n"
            "See docs/deploy.md#восстановление-секретов"
        )
    raise RuntimeError(f"Database is unavailable: {detail}{hint}") from last_error


def current_revision(engine) -> str | None:
    inspector = inspect(engine)
    if "alembic_version" not in inspector.get_table_names():
        return None
    with engine.connect() as connection:
        return connection.execute(text("SELECT version_num FROM alembic_version")).scalar()


def _reset_schema(engine) -> None:
    cascade = " CASCADE" if engine.dialect.name == "postgresql" else ""
    with engine.begin() as connection:
        for table in SCHEMA_TABLES:
            connection.execute(text(f'DROP TABLE IF EXISTS "{table}"{cascade}'))
        connection.execute(text(f'DROP TABLE IF EXISTS "alembic_version"{cascade}'))


def run_migrations(engine) -> None:
    config = Config("alembic.ini")
    tables = set(inspect(engine).get_table_names())
    revision = current_revision(engine)
    schema_tables = set(SCHEMA_TABLES)
    existing_schema = tables & schema_tables

    if existing_schema:
        if schema_tables.issubset(tables):
            if revision is None:
                command.stamp(config, INITIAL_REVISION)
        else:
            _reset_schema(engine)

    command.upgrade(config, "head")


def main() -> None:
    ensure_install_secrets()
    engine = wait_for_database()
    run_migrations(engine)
    ensure_server_log_file()
    append_server_log_line("API starting (docker entrypoint)")
    import uvicorn

    uvicorn.run(
        "app.main:app",
        host="0.0.0.0",
        port=8000,
        log_config=build_uvicorn_log_config(),
    )


if __name__ == "__main__":
    main()
