import re
import stat
from pathlib import Path

from database.manager import Base


ROOT = Path(__file__).resolve().parents[1]


def _migration_created_tables():
    tables = set()
    for path in (ROOT / "migrations").glob("*.sql"):
        text = path.read_text(encoding="utf-8", errors="ignore")
        for match in re.finditer(
            r"CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?(?:public\.)?([\w_]+)",
            text,
            re.IGNORECASE,
        ):
            tables.add(match.group(1))
    return tables


def test_all_orm_metadata_tables_have_create_table_migration():
    metadata_tables = set(Base.metadata.tables)
    migration_tables = _migration_created_tables()

    assert metadata_tables - migration_tables == set()


def test_v2_blocker_migrations_exist_and_are_runner_readable():
    required = [
        "031_fix_incidents_autoheal_schema.sql",
        "032_market_intel_core_schema.sql",
        "033_fix_host_health_probe_labels.sql",
        "034_add_embedding_signature_to_rag_tables.sql",
        "035_core_business_tables_baseline.sql",
        "036_normalize_incidents_dual_columns.sql",
        "037_add_action_plans_guardrails.sql",
    ]

    for filename in required:
        path = ROOT / "migrations" / filename
        assert path.exists(), f"missing migration: {filename}"
        mode = stat.S_IMODE(path.stat().st_mode)
        assert mode & stat.S_IRGRP, f"{filename} is not group-readable"
        assert mode & stat.S_IROTH, f"{filename} is not world-readable"


def test_host_health_probe_label_check_accepts_runtime_labels():
    migration = (ROOT / "migrations" / "033_fix_host_health_probe_labels.sql").read_text(encoding="utf-8")

    assert "DROP CONSTRAINT IF EXISTS chk_host_label_029" in migration
    assert "ADD CONSTRAINT chk_host_label_029" in migration
    assert "NOT VALID" in migration

    expected_runtime_labels = [
        "GCP-SSD",
        "GCP-SSD-2",
        "111 備援",
        "GCP-SSD（via Nginx 110）",
        "GCP-SSD-2（via Nginx 110）",
    ]
    for label in expected_runtime_labels:
        assert f"'{label}'" in migration


def test_rag_embedding_signature_migration_covers_query_and_learning_tables():
    migration = (ROOT / "migrations" / "034_add_embedding_signature_to_rag_tables.sql").read_text(encoding="utf-8")

    expected_snippets = [
        "ALTER TABLE IF EXISTS rag_query_log",
        "ADD COLUMN IF NOT EXISTS embedding_signature VARCHAR(64)",
        "idx_rag_query_log_embedding_signature",
        "WHERE query_embedding IS NOT NULL AND embedding_signature IS NOT NULL",
        "ALTER TABLE IF EXISTS learning_episodes",
        "idx_le_embedding_signature",
        "WHERE embedding IS NOT NULL AND embedding_signature IS NOT NULL",
    ]
    for snippet in expected_snippets:
        assert snippet in migration


def test_incidents_dual_column_migration_backfills_legacy_and_current_columns():
    migration = (ROOT / "migrations" / "036_normalize_incidents_dual_columns.sql").read_text(encoding="utf-8")

    expected_snippets = [
        "ADD COLUMN IF NOT EXISTS error_traceback TEXT",
        "ADD COLUMN IF NOT EXISTS traceback_str TEXT",
        "ADD COLUMN IF NOT EXISTS playbook_id INTEGER",
        "ADD COLUMN IF NOT EXISTS matched_playbook_id INTEGER",
        "SET traceback_str = error_traceback",
        "SET error_traceback = traceback_str",
        "SET matched_playbook_id = playbook_id",
        "SET playbook_id = matched_playbook_id",
        "incidents_playbook_id_fkey",
        "incidents_matched_playbook_id_fkey",
    ]
    for snippet in expected_snippets:
        assert snippet in migration


def test_action_plans_guardrail_migration_keeps_source_and_status_checks():
    migration = (ROOT / "migrations" / "037_add_action_plans_guardrails.sql").read_text(encoding="utf-8")

    expected_constraints = [
        "chk_action_plans_source_marker",
        "chk_action_plans_action_type",
        "chk_action_plans_created_by",
        "chk_action_plans_status",
    ]
    for constraint in expected_constraints:
        assert constraint in migration

    assert "CHECK (action_type IS NOT NULL OR created_by IS NOT NULL)" in migration
    assert "'code_review_fix'" in migration
    assert "'openclaw_recommendation'" in migration
    assert "'pending_review'" in migration
    assert migration.count("NOT VALID") >= 4


def test_dashboard_hot_path_index_migration_keeps_reboot_fix_durable():
    migration = (ROOT / "migrations" / "040_dashboard_hot_path_indexes.sql").read_text(encoding="utf-8")

    expected_snippets = [
        "idx_comp_match_attempts_source_sku_attempted_at",
        "ON competitor_match_attempts (source, sku, attempted_at DESC)",
        "INCLUDE (attempt_status)",
        "idx_price_records_product_timestamp_id_desc",
        "ON price_records (product_id, timestamp DESC, id DESC)",
        "INCLUDE (price)",
        "idx_products_status_id_icode",
        "ON products (status, id, i_code)",
        "ANALYZE products",
        "ANALYZE price_records",
        "ANALYZE competitor_match_attempts",
    ]
    for snippet in expected_snippets:
        assert snippet in migration

    assert "DROP " not in migration.upper()
    assert "DELETE " not in migration.upper()
    assert "TRUNCATE " not in migration.upper()


def test_legacy_zero_byte_database_decoys_do_not_return():
    for filename in ["momo.db", "momo_data.db", "momo_database.db"]:
        assert not (ROOT / "database" / filename).exists()
