"""
Python 版本兼容性测试
验证 aacode 能够在 requires-python (>=3.8) 范围内正确运行
"""
import ast
import re
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent.parent))

import pytest


class TestPythonVersionCompat:
    """Python 版本兼容性"""

    def test_all_py_files_parse(self):
        """所有 .py 文件语法正确（模拟 3.8+ 解析器）"""
        root = Path(__file__).parent.parent / "aacode"
        errors = []
        for f in sorted(root.rglob("*.py")):
            if ".venv" in str(f):
                continue
            source = f.read_text(encoding="utf-8")
            try:
                ast.parse(source, filename=str(f))
            except SyntaxError as e:
                errors.append(f"{f.relative_to(root.parent)}: {e}")
        assert not errors, "\n".join(errors[:10])

    def test_no_backslash_in_fstring_expr(self):
        """f-string 表达式内无反斜杠（兼容 Python 3.8~3.11）"""
        root = Path(__file__).parent.parent / "aacode"
        issues = []
        for f in sorted(root.rglob("*.py")):
            if ".venv" in str(f):
                continue
            for i, line in enumerate(f.read_text().split("\n"), 1):
                stripped = line.strip()
                if not stripped.startswith(("f'", 'f"', "f'")):
                    continue
                brace_depth = 0
                in_fstring_expr = False
                for j, c in enumerate(stripped):
                    if c == "{" and j > 0 and stripped[j - 1] != "{":
                        brace_depth += 1
                        in_fstring_expr = True
                    elif c == "}" and j > 0 and stripped[j - 1] != "}":
                        brace_depth -= 1
                        if brace_depth == 0:
                            in_fstring_expr = False
                    elif c == "\\" and in_fstring_expr:
                        issues.append(f"{f.name}:{i}  {stripped[:80]}")
                        break
        assert not issues, "\n".join(issues[:10])

    def test_pep604_union_has_future_import(self):
        """使用 X | Y 联合类型语法的文件必须有 from __future__ import annotations"""
        root = Path(__file__).parent.parent / "aacode"
        for f in sorted(root.rglob("*.py")):
            if ".venv" in str(f):
                continue
            source = f.read_text()
            has_union_syntax = False
            for line in source.split("\n"):
                stripped = line.strip()
                # 只匹配类型注解中的 PEP 604 语法（排除字符串字面量中的 |）
                # 模式: def func(x: str | None) 或  x: str | None = ...
                # 要求 : 后面紧跟一个单词 或 ) 后跟 -> 返回类型
                if re.search(r":\s*(?:\w+\s*\|\s*(?:None|\w+))", stripped) and not stripped.startswith(("#", "//", "f.write", "print")):
                    # 确认这不是字符串字面量: 不在引号内
                    # 简化检查: 如果行中有 f" 或 " 且 | 在引号内, 跳过
                    if "\"" in stripped:
                        # 检查 | 是否在引号外 (粗略检查)
                        in_quote = False
                        for c in stripped:
                            if c == "\"":
                                in_quote = not in_quote
                        if in_quote:
                            continue  # | 在未闭合的引号内
                    has_union_syntax = True
                    break
            if has_union_syntax:
                assert "from __future__ import annotations" in source, \
                    f"{f.name} uses PEP 604 union syntax but missing future import"

    def test_from_future_annotations_importable(self):
        """所有使用 from __future__ import annotations 的文件可正常导入"""
        import importlib
        root = Path(__file__).parent.parent / "aacode"
        modules = set()
        for f in sorted(root.rglob("*.py")):
            if ".venv" in str(f):
                continue
            source = f.read_text()
            if "from __future__ import annotations" in source:
                rel = f.relative_to(root.parent)
                mod = str(rel.with_suffix("")).replace("/", ".")
                modules.add(mod)

        errors = []
        for mod_name in sorted(modules):
            try:
                importlib.import_module(mod_name)
            except Exception as e:
                errors.append(f"{mod_name}: {e}")
        assert not errors, "\n".join(errors[:10])

    def test_aacode_top_level_import(self):
        """aacode 包顶层的 __init__ 可导入"""
        import aacode
        assert hasattr(aacode, "__version__")
        assert isinstance(aacode.__version__, str)

    def test_core_modules_importable(self):
        """所有核心模块可导入"""
        modules = [
            "aacode.core.react_loop",
            "aacode.core.main_agent",
            "aacode.core.agent",
            "aacode.core.sub_agent",
            "aacode.config",
            "aacode.cli",
            "aacode.i18n",
        ]
        errors = []
        for mod in modules:
            try:
                __import__(mod)
            except Exception as e:
                errors.append(f"{mod}: {e}")
        assert not errors, "\n".join(errors[:5])

    def test_sandbox_modules_syntax(self):
        """sandbox 模块语法正确"""
        import aacode.sandbox.mcp_client
        import aacode.sandbox.vm_manager
        assert True

    def test_tool_modules_importable(self):
        """工具模块可导入"""
        import aacode.tools.atomic_tools
        import aacode.tools.code_tools
        import aacode.tools.web_tools
        import aacode.tools.sandbox_tools
        assert True

    def test_utils_modules_importable(self):
        """工具函数模块可导入"""
        import aacode.utils.session_manager
        import aacode.utils.context_manager
        import aacode.utils.safety
        import aacode.utils.todo_manager
        import aacode.utils.async_helpers
        assert True

    def test_version_consistent(self):
        """pyproject.toml 中的版本与 aacode.__version__ 一致"""
        try:
            import tomllib
        except ImportError:
            tomllib = None  # Python < 3.11
        if tomllib:
            pyproject = tomllib.load(open(Path(__file__).parent.parent / "pyproject.toml", "rb"))
            import aacode
            assert aacode.__version__ == pyproject["project"]["version"], \
                f"aacode __version__ ({aacode.__version__}) != pyproject.toml ({pyproject['project']['version']})"

    def test_requires_python_classifiers_consistent(self):
        """requires-python 和 classifiers 一致（仅提示，不阻塞）"""
        try:
            import tomllib
        except ImportError:
            tomllib = None
        if tomllib:
            pyproject = tomllib.load(open(Path(__file__).parent.parent / "pyproject.toml", "rb"))
            req = pyproject["project"]["requires-python"]
            classifiers = pyproject["project"]["classifiers"]
            py_versions = []
            for c in classifiers:
                if c.startswith("Programming Language :: Python :: 3."):
                    ver = int(c.split("::")[-1].strip().split(".")[-1])
                    py_versions.append(ver)
            if py_versions:
                pass  # 仅用于信息，不做硬断言


class TestDesktopCompat:
    """Desktop 兼容性检查（只做语法级验证，不启动 Tauri）"""

    def test_desktop_commands_rs_version(self):
        """Cargo.toml 版本号格式正确"""
        cargo = Path(__file__).parent.parent / "desktop/src-tauri/Cargo.toml"
        content = cargo.read_text()
        import re
        match = re.search(r'^version\s*=\s*"(\d+\.\d+\.\d+)"', content, re.MULTILINE)
        assert match, "Cargo.toml 中找不到版本号"
        parts = match.group(1).split(".")
        assert len(parts) == 3
        assert all(p.isdigit() for p in parts)

    def test_tauri_conf_version_matches_cargo(self):
        """tauri.conf.json 与 Cargo.toml 版本一致"""
        import json
        import re
        cargo = Path(__file__).parent.parent / "desktop/src-tauri/Cargo.toml"
        tauri_conf = Path(__file__).parent.parent / "desktop/src-tauri/tauri.conf.json"

        cargo_ver = re.search(r'^version\s*=\s*"(\d+\.\d+\.\d+)"', cargo.read_text(), re.MULTILINE)
        tauri_data = json.loads(tauri_conf.read_text())

        assert cargo_ver, "Cargo.toml version not found"
        assert tauri_data["version"] == cargo_ver.group(1), \
            f"tauri.conf.json ({tauri_data['version']}) != Cargo.toml ({cargo_ver.group(1)})"
