import ast
import subprocess
import sys
from pathlib import Path


ROOT = Path(__file__).resolve().parents[1]
ARCH = ROOT / "docs" / "architecture"


def _route_count() -> int:
    count = 0
    for route_file in (ROOT / "app" / "api").glob("*/*.py"):
        tree = ast.parse(route_file.read_text(encoding="utf-8"))
        for node in ast.walk(tree):
            if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
                continue
            for decorator in node.decorator_list:
                if not isinstance(decorator, ast.Call):
                    continue
                function = decorator.func
                if (
                    isinstance(function, ast.Attribute)
                    and function.attr == "route"
                    and isinstance(function.value, ast.Name)
                    and function.value.id == "bp"
                ):
                    count += 1
    return count


def test_required_architecture_artifacts_exist():
    required = {
        "ARCHITECTURE_OVERVIEW.md",
        "OPENAPI.yaml",
        "FRONTEND_REMOVAL_INVENTORY.md",
        "DATA_MODEL.md",
        "DEPLOYMENT_SOURCE_OF_TRUTH.md",
        "ADR-001-authentication.md",
        "ADR-002-workflow-engine.md",
        "ADR-003-cross-store-consistency.md",
        "NEXT_ITERATION_ROADMAP.md",
    }
    assert required <= {path.name for path in ARCH.glob("*")}


def test_openapi_route_inventory_matches_flask_source():
    source_count = _route_count()
    contract = (ARCH / "OPENAPI.yaml").read_text(encoding="utf-8")

    assert source_count > 100
    assert "openapi: 3.1.0" in contract
    assert f"x-route-count: {source_count}" in contract
    assert contract.count("operationId:") == source_count


def test_openapi_generator_is_reproducible(tmp_path):
    generated = tmp_path / "OPENAPI.yaml"
    subprocess.run(
        [
            sys.executable,
            str(ROOT / "scripts" / "generate_openapi.py"),
            "--output",
            str(generated),
        ],
        cwd=ROOT,
        check=True,
    )

    assert generated.read_bytes() == (ARCH / "OPENAPI.yaml").read_bytes()


def test_contract_ci_regenerates_and_checks_the_committed_inventory():
    workflow = (ROOT / ".github" / "workflows" / "contracts.yml").read_text(
        encoding="utf-8"
    )
    assert "scripts/generate_openapi.py" in workflow
    assert "git diff --exit-code" in workflow
    assert "tests/test_architecture_artifacts.py" in workflow


def test_architecture_decisions_capture_product_boundaries():
    combined = "\n".join(
        (ARCH / name).read_text(encoding="utf-8")
        for name in (
            "DATA_MODEL.md",
            "DEPLOYMENT_SOURCE_OF_TRUTH.md",
            "ADR-001-authentication.md",
            "ADR-002-workflow-engine.md",
            "ADR-003-cross-store-consistency.md",
            "NEXT_ITERATION_ROADMAP.md",
        )
    )

    for required_term in (
        "管理员",
        "编辑者",
        "查看者",
        "DataFlow",
        "n8n Workflow",
        "同一环境只允许一个当前生效版本",
        "Qwen",
        "DeepSeek",
        "每日全量一致性巡检",
        "app/",
        "deployment/app/",
    ):
        assert required_term in combined


def test_removal_inventory_is_bound_to_the_recovery_backup():
    inventory = (ARCH / "FRONTEND_REMOVAL_INVENTORY.md").read_text(
        encoding="utf-8"
    )
    assert "DataOps-platform-backup-before-cleanup-20260716-170321.tar.gz" in inventory
    assert "866c6c4aff127f88497a5ddf39ac8d8da93008cc784ae6f276f80a7cd17d4ef0" in inventory
    assert "348" in inventory


def test_release_sync_and_package_include_versioned_migrations():
    sync = (ROOT / "deployment" / "sync_release.sh").read_text(encoding="utf-8")
    package = (ROOT / "deployment" / "package_release.sh").read_text(
        encoding="utf-8"
    )
    run_script = (ROOT / "scripts" / "run_dataops.sh").read_text(encoding="utf-8")

    assert 'copy_tree "${ROOT_DIR}/migrations"' in sync
    assert 'copy_file "${ROOT_DIR}/alembic.ini"' in sync
    assert 'copy_into_release "${SCRIPT_DIR}/migrations"' in package
    assert 'copy_into_release "${SCRIPT_DIR}/alembic.ini"' in package
    assert 'alembic" -c "${APP_DIR}/alembic.ini" upgrade head' in run_script
