import pytest
import subprocess
import os
from pathlib import Path

# 项目根目录
PROJECT_ROOT = "/Users/Chiguyong/Code/Fischer/geo/backend"


class TestDatabaseMigration:
    """数据库迁移验证测试"""

    def test_alembic_current_shows_no_errors(self):
        """alembic current 应无错误输出"""
        result = subprocess.run(
            ["alembic", "current"],
            cwd=PROJECT_ROOT,
            capture_output=True,
            text=True
        )
        assert result.returncode == 0, f"alembic current failed: {result.stderr}"

    def test_all_required_tables_exist(self):
        """所有必需表应存在"""
        import asyncio
        import sys
        sys.path.insert(0, os.path.join(PROJECT_ROOT, "app"))

        async def check_tables():
            from app.database import engine
            from sqlalchemy import inspect

            async with engine.connect() as conn:
                inspector = inspect(conn)
                tables = await conn.run_sync(lambda sync_conn: inspect(sync_conn).get_table_names())
                return set(tables)

        try:
            existing_tables = asyncio.run(check_tables())
        except Exception as e:
            pytest.skip(f"无法连接数据库: {e}")

        required_tables = [
            "users", "brands", "competitors", "queries",
            "citations", "alerts", "alert_settings", "suggestions",
            "organizations", "subscriptions", "subscription_plans",
            "agent_configs", "agent_executions", "knowledge_bases",
            "content_versions", "platform_rules", "analytics_events",
            "distribution_schedules", "client_brands"
        ]

        missing_tables = [t for t in required_tables if t not in existing_tables]
        assert not missing_tables, f"缺失表: {missing_tables}"

    def test_migration_head_matches_models(self):
        """迁移头应与模型定义一致"""
        result = subprocess.run(
            ["alembic", "check"],
            cwd=PROJECT_ROOT,
            capture_output=True,
            text=True
        )
        assert result.returncode == 0, f"alembic check failed: {result.stderr}"

    def test_no_duplicate_migration_versions(self):
        """迁移版本号应唯一"""
        versions_dir = Path(PROJECT_ROOT) / "alembic" / "versions"
        if not versions_dir.exists():
            pytest.skip("alembic/versions/ 目录不存在")

        version_files = list(versions_dir.glob("*.py"))
        version_numbers = []

        for f in version_files:
            # 版本号通常是目录名中下划线前的部分
            # 例如: 059724556401_add_missing_sentiment_fields.py
            name = f.stem
            # 取第一个下划线前的部分作为版本号
            if "_" in name:
                version_num = name.split("_")[0]
                version_numbers.append((version_num, f.name))

        # 检查重复
        seen = {}
        duplicates = []
        for version_num, filename in version_numbers:
            if version_num in seen:
                duplicates.append((version_num, filename, seen[version_num]))
            else:
                seen[version_num] = filename

        assert not duplicates, f"发现重复版本号: {duplicates}"

    def test_foreign_keys_integrity(self):
        """关键外键约束应存在"""
        import asyncio
        import sys
        sys.path.insert(0, os.path.join(PROJECT_ROOT, "app"))

        async def check_fks():
            from app.database import engine
            from sqlalchemy import inspect

            async with engine.connect() as conn:
                fks = await conn.run_sync(
                    lambda sync_conn: inspect(sync_conn).get_foreign_keys("brands")
                )
                return fks

        try:
            foreign_keys = asyncio.run(check_fks())
        except Exception as e:
            pytest.skip(f"无法连接数据库: {e}")

        fk_columns = [fk['constrained_columns'] for fk in foreign_keys]

        # 检查 brands.user_id 外键存在
        has_user_fk = any('user_id' in cols for cols in fk_columns)
        assert has_user_fk, "brands 表缺少 user_id 外键"

    def test_alembic_history_shows_migrations(self):
        """alembic history 应显示迁移历史"""
        result = subprocess.run(
            ["alembic", "history"],
            cwd=PROJECT_ROOT,
            capture_output=True,
            text=True
        )
        assert result.returncode == 0, f"alembic history failed: {result.stderr}"
        assert result.stdout.strip(), "alembic history 为空"
