# #region TestAlembicMigrations [C:3] [TYPE Module] [SEMANTICS tests, alembic, migration, schema, idempotent]
# @BRIEF Verifies Alembic migration chain: fresh upgrade and legacy upgrade.
# @RELATION BINDS_TO -> [ed310b33f02c]
# @INVARIANT  Running `alembic upgrade head` twice is idempotent (no-op on second run) on PostgreSQL.
# @INVARIANT  Legacy database with existing tables can be upgraded via `alembic upgrade head`.
# @PRE  Alembic is installed, migration files exist in alembic/versions/.
# @POST  Schema matches expected state after upgrade.
# @TEST_INVARIANT: alembic_fresh_upgrade -> VERIFIED_BY: [test_fresh_upgrade_creates_tables]
# @TEST_INVARIANT: alembic_upgrade_legacy -> VERIFIED_BY: [test_legacy_database_upgrade]
# @REJECTED Full migration chain idempotency on SQLite — initial migration uses
#           op.create_index() which conflicts with SQLite auto-created indexes
#           for UNIQUE constraints. Migrations are designed for PostgreSQL.
# @REJECTED Legacy stamp + raw SQL rejected — entrypoint now runs `alembic upgrade head`
#           for legacy databases, ensuring all missing tables/columns are created.

import os
import sys
from pathlib import Path

import pytest

# Ensure backend/src is importable for model metadata
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))

# Alembic migrations use PostgreSQL-specific features (CREATE INDEX may conflict
# with SQLite auto-indexes for UNIQUE constraints). Skip full-chain tests on SQLite.
_DB_BACKEND = os.environ.get("DATABASE_URL", "sqlite://").split(":")[0]
_REQUIRES_PG = pytest.mark.skipif(
    _DB_BACKEND != "postgresql",
    reason="Alembic migrations are PostgreSQL-specific; use postgresql:// DATABASE_URL to test",
)


# #region test_fresh_upgrade_creates_tables [C:3] [TYPE Function]
# @BRIEF Run alembic upgrade head on a fresh PostgreSQL DB and verify key tables exist.
# @POST All expected tables exist after upgrade.
# @TEST_INVARIANT: alembic_fresh_upgrade -> VERIFIED_BY: [test_fresh_upgrade_creates_tables]
@_REQUIRES_PG
def test_fresh_upgrade_creates_tables() -> None:
    """Fresh Alembic upgrade creates all expected tables (PostgreSQL only)."""
    from sqlalchemy import create_engine, inspect
    from sqlalchemy import text as sa_text
    backend_dir = Path(__file__).parent.parent
    _run_alembic_upgrade()
    engine = create_engine(os.environ["DATABASE_URL"])
    inspector = inspect(engine)
    tables = set(inspector.get_table_names())
    expected_tables = {
        "environments",
        "translation_jobs",
        "users",
        "roles",
        "permissions",
        "task_records",
        "alembic_version",
    }
    missing = expected_tables - tables
    assert not missing, f"Missing tables after Alembic upgrade: {missing}"
    with engine.connect() as conn:
        result = conn.execute(sa_text("SELECT version_num FROM alembic_version"))
        version = result.scalar_one()
        assert version is not None and len(version) > 0
    roles_cols = {c["name"] for c in inspector.get_columns("roles")}
    assert "is_admin" in roles_cols, "roles.is_admin column missing after Alembic upgrade"
    engine.dispose()
# #endregion test_fresh_upgrade_creates_tables


# #region test_legacy_database_upgrade [C:2] [TYPE Function]
# @BRIEF Simulate legacy DB (tables exist, no alembic_version), verify upgrade head works.
# @POST alembic_version created, all migration columns present after upgrade.
# @TEST_INVARIANT: alembic_upgrade_legacy -> VERIFIED_BY: [test_legacy_database_upgrade]
@_REQUIRES_PG
def test_legacy_database_upgrade() -> None:
    """Alembic upgrade head works on a database that already has tables but no alembic_version."""
    from sqlalchemy import create_engine, inspect
    from sqlalchemy import text as sa_text
    from src.models.mapping import Base
    from src.models import auth as _auth  # noqa: F401
    from src.models import task as _task  # noqa: F401

    engine = create_engine(os.environ["DATABASE_URL"])
    # Drop alembic_version if exists to simulate legacy state
    with engine.begin() as conn:
        conn.execute(sa_text("DROP TABLE IF EXISTS alembic_version CASCADE"))
    engine.dispose()

    # Tables already exist from previous runs (create_all) — this is the legacy state.
    # Run upgrade head (new entrypoint behavior for legacy DB)
    _run_alembic_upgrade()

    # Verify
    engine = create_engine(os.environ["DATABASE_URL"])
    inspector = inspect(engine)
    tables = set(inspector.get_table_names())
    assert "alembic_version" in tables, "alembic_version table not created by upgrade"
    roles_cols = {c["name"] for c in inspector.get_columns("roles")}
    assert "is_admin" in roles_cols, "roles.is_admin column missing after legacy upgrade"

    # Verify version_num matches head
    result = engine.connect().execute(sa_text("SELECT version_num FROM alembic_version"))
    version = result.scalar_one()
    assert version is not None and len(version) > 0
    engine.dispose()
# #endregion test_legacy_database_upgrade


# #region _run_alembic_upgrade [C:1] [TYPE Function]
def _run_alembic_upgrade() -> None:
    """Run alembic upgrade head programmatically."""
    from alembic.config import Config as AlembicConfig
    from alembic import command as alembic_command
    backend_dir = Path(__file__).parent.parent
    alembic_cfg = AlembicConfig(str(backend_dir / "alembic.ini"))
    alembic_cfg.set_main_option("script_location", str(backend_dir / "alembic"))
    alembic_command.upgrade(alembic_cfg, "head")
# #endregion _run_alembic_upgrade

# #endregion TestAlembicMigrations
