from __future__ import annotations

import importlib.util
from pathlib import Path

from alembic.config import Config
from alembic.script import ScriptDirectory


BACKEND_ROOT = Path(__file__).resolve().parents[2]
MIGRATIONS_ROOT = BACKEND_ROOT / "migrations"
REVISION_PATH = MIGRATIONS_ROOT / "versions" / "0001_initial_schema.py"
RESTORE_REVISION_PATH = (
    MIGRATIONS_ROOT / "versions" / "0002_add_restore_audit_action.py"
)
Q3_REVISION_PATH = (
    MIGRATIONS_ROOT
    / "versions"
    / "0003_q3_business_alignment_and_content_search.py"
)


def _load_revision_module():
    spec = importlib.util.spec_from_file_location("dms_initial_migration", REVISION_PATH)
    assert spec is not None and spec.loader is not None
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module


def test_alembic_config_and_head_are_valid():
    config = Config(str(MIGRATIONS_ROOT / "alembic.ini"))
    script = ScriptDirectory.from_config(config)
    assert script.get_current_head() == (
        "0003_q3_business_alignment_and_content_search"
    )


def test_initial_migration_exports_upgrade_and_downgrade():
    module = _load_revision_module()
    assert callable(module.upgrade)
    assert callable(module.downgrade)


def test_initial_migration_contains_seven_tables_and_generated_columns():
    source = REVISION_PATH.read_text(encoding="utf-8")
    for table_name in (
        "sys_organization",
        "sys_user",
        "doc_category",
        "doc_document",
        "doc_attachment_binding",
        "doc_permission",
        "sys_audit_log",
    ):
        assert f'"{table_name}"' in source
    assert source.count('sa.Computed("IF(is_deleted = 0, 1, NULL)"') == 2
    assert "uq_doc_attachment_binding_active" in source
    assert "uq_doc_permission_active" in source


def test_initial_migration_has_no_cascade():
    source = REVISION_PATH.read_text(encoding="utf-8")
    assert 'ondelete="CASCADE"' not in source
    assert source.count('ondelete="RESTRICT"') >= 8


def test_downgrade_uses_dependency_safe_order():
    source = REVISION_PATH.read_text(encoding="utf-8")
    positions = [
        source.index(f'op.drop_table("{name}")')
        for name in (
            "sys_audit_log",
            "doc_permission",
            "doc_attachment_binding",
            "doc_document",
            "doc_category",
            "sys_user",
            "sys_organization",
        )
    ]
    assert positions == sorted(positions)


def test_restore_migration_is_scoped_and_reversible():
    source = RESTORE_REVISION_PATH.read_text(encoding="utf-8")
    assert 'down_revision: str | None = "0001_initial_schema"' in source
    assert "RESTORE_DOCUMENT" in source
    assert "ix_doc_document_recycle_deleted" in source
    assert '["is_deleted", "deleted_at", "id"]' in source
    assert "drop_index" in source
    assert source.count("create_check_constraint") == 2
    assert "create_table" not in source
    assert "add_column" not in source


def test_q3_migration_is_single_scoped_revision_with_safe_downgrade():
    source = Q3_REVISION_PATH.read_text(encoding="utf-8")
    assert (
        'down_revision: str | None = "0002_add_restore_audit_action"'
        in source
    )
    assert "content_text" in source
    assert "content_extract_status" in source
    assert "content_extracted_at" in source
    assert "LONGTEXT" in source
    assert "auth_version = auth_version + 1" in source
    assert "role_code = 'USER'" in source
    assert "role_code IN ('USER', 'ADMIN')" in source
    assert "RuntimeError" in source
    assert "create_table" not in source
