"""工具代码验证器

验证生成的工具代码：语法检查、导入检查、结构验证
"""

import ast
import logging
import sys
from dataclasses import dataclass
from typing import List, Optional, Tuple

from .base import BaseTool

logger = logging.getLogger("agent")


@dataclass
class ToolValidationResult:
    """工具验证结果"""
    valid: bool
    errors: List[str]
    warnings: List[str]
    tool_class: Optional[type] = None

    @classmethod
    def success(cls, tool_class: Optional[type] = None) -> "ToolValidationResult":
        """创建成功结果"""
        return cls(valid=True, errors=[], warnings=[], tool_class=tool_class)

    @classmethod
    def failure(cls, errors: List[str], warnings: Optional[List[str]] = None) -> "ToolValidationResult":
        """创建失败结果"""
        return cls(valid=False, errors=errors, warnings=warnings or [])


class ToolCodeValidator:
    """验证生成的工具代码"""

    # 必须的导入
    REQUIRED_IMPORTS = ["BaseTool", "ToolOutput"]

    def __init__(self):
        pass

    def validate_syntax(self, code: str) -> Tuple[bool, Optional[str]]:
        """验证 Python 语法

        Args:
            code: Python 代码

        Returns:
            Tuple[bool, Optional[str]]: (是否有效, 错误信息)
        """
        try:
            ast.parse(code)
            return True, None
        except SyntaxError as e:
            return False, f"语法错误: {e.msg} (行 {e.lineno})"
        except Exception as e:
            return False, f"解析错误: {str(e)}"

    def validate_imports(self, code: str) -> List[str]:
        """检查依赖是否可用

        Args:
            code: Python 代码

        Returns:
            List[str]: 缺失的导入列表
        """
        missing = []

        # 解析 AST
        try:
            tree = ast.parse(code)
        except Exception:
            return missing

        # 收集导入
        imports = set()
        for node in ast.walk(tree):
            if isinstance(node, ast.Import):
                for alias in node.names:
                    imports.add(alias.name.split('.')[0])
            elif isinstance(node, ast.ImportFrom):
                if node.module:
                    imports.add(node.module.split('.')[0])

        # 检查必需的标准库
        required = {"typing", "pathlib"}
        for req in required:
            if req not in imports:
                # 检查是否实际使用了这些模块
                if req in code:
                    missing.append(f"缺少导入: {req}")

        return missing

    def validate_structure(self, code: str) -> List[str]:
        """验证代码结构：继承 BaseTool、实现 execute 方法

        Args:
            code: Python 代码

        Returns:
            List[str]: 结构问题列表
        """
        issues = []

        try:
            tree = ast.parse(code)
        except Exception as e:
            issues.append(f"无法解析代码: {e}")
            return issues

        # 查找类定义
        class_found = False
        has_execute = False
        has_base_tool = False
        has_name = False
        has_description = False

        for node in ast.walk(tree):
            if isinstance(node, ast.ClassDef):
                class_found = True

                # 检查是否继承 BaseTool
                for base in node.bases:
                    if isinstance(base, ast.Name):
                        if base.id == "BaseTool":
                            has_base_tool = True

                # 检查类属性 - 支持 AnnAssign (name: str = "value") 和 Assign (name = "value")
                for item in node.body:
                    # 检查带类型注解的赋值
                    if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name):
                        if item.target.id == "name":
                            has_name = True
                        elif item.target.id == "description":
                            has_description = True
                    # 检查简单赋值
                    elif isinstance(item, ast.Assign):
                        for target in item.targets:
                            if isinstance(target, ast.Name):
                                if target.id == "name":
                                    has_name = True
                                elif target.id == "description":
                                    has_description = True

                # 检查 execute 方法
                for item in node.body:
                    if isinstance(item, ast.FunctionDef):
                        if item.name == "execute":
                            has_execute = True
                            # 检查方法签名
                            args = item.args
                            if len(args.args) < 2:  # self, parameters
                                issues.append("execute 方法参数不足，需要: (self, parameters, context=None)")

        if not class_found:
            issues.append("未找到类定义")

        if not has_base_tool:
            issues.append("类未继承 BaseTool")

        if not has_execute:
            issues.append("未实现 execute 方法")

        if not has_name:
            issues.append("未定义 name 属性")

        if not has_description:
            issues.append("未定义 description 属性")

        return issues

    def validate_full(self, code: str, tool_name: str) -> ToolValidationResult:
        """完整验证：语法 + 导入 + 结构

        Args:
            code: Python 代码
            tool_name: 工具名称（用于日志）

        Returns:
            ToolValidationResult: 验证结果
        """
        errors = []
        warnings = []

        # 1. 语法验证
        valid_syntax, syntax_error = self.validate_syntax(code)
        if not valid_syntax:
            errors.append(f"语法错误: {syntax_error}")
            return ToolValidationResult.failure(errors, warnings)

        # 2. 结构验证
        structure_issues = self.validate_structure(code)
        if structure_issues:
            errors.extend(structure_issues)
            return ToolValidationResult.failure(errors, warnings)

        # 3. 导入验证
        missing_imports = self.validate_imports(code)
        if missing_imports:
            warnings.extend([f"可能缺少导入: {imp}" for imp in missing_imports])

        # 4. 尝试动态加载
        tool_class = self._try_load_class(code)
        if not tool_class:
            errors.append("无法加载工具类")
            return ToolValidationResult.failure(errors, warnings)

        # 5. 验证是 BaseTool 子类
        if not issubclass(tool_class, BaseTool):
            errors.append("工具类未继承 BaseTool")
            return ToolValidationResult.failure(errors, warnings)

        # 6. 验证必需属性
        if not hasattr(tool_class, 'name') or not tool_class.name:
            errors.append("工具缺少 name 属性")
        if not hasattr(tool_class, 'description') or not tool_class.description:
            errors.append("工具缺少 description 属性")

        if errors:
            return ToolValidationResult.failure(errors, warnings)

        logger.info(f"[ToolCodeValidator] 工具验证通过: {tool_name}")
        return ToolValidationResult.success(tool_class)

    def _try_load_class(self, code: str) -> Optional[type]:
        """尝试动态加载类

        Args:
            code: Python 代码

        Returns:
            Optional[type]: 工具类
        """
        try:
            # 创建临时模块
            import types
            module = types.ModuleType("temp_tool_module")

            # 添加必要的导入到模块
            sys.modules["temp_tool_module"] = module

            # 执行代码
            exec(code, module.__dict__)

            # 查找 BaseTool 子类
            for attr_name in dir(module):
                attr = getattr(module, attr_name)
                if (isinstance(attr, type) and
                    issubclass(attr, BaseTool) and
                    attr is not BaseTool):
                    return attr

            return None

        except Exception as e:
            logger.warning(f"[ToolCodeValidator] 加载类失败: {e}")
            return None
        finally:
            # 清理
            if "temp_tool_module" in sys.modules:
                del sys.modules["temp_tool_module"]

    def validate_and_instantiate(self, code: str, tool_name: str) -> Tuple[bool, Optional[BaseTool], List[str]]:
        """验证并实例化工具

        Args:
            code: Python 代码
            tool_name: 工具名称

        Returns:
            Tuple[bool, Optional[BaseTool], List[str]]: (是否成功, 工具实例, 错误列表)
        """
        result = self.validate_full(code, tool_name)

        if result.valid and result.tool_class:
            try:
                tool = result.tool_class()
                return True, tool, []
            except Exception as e:
                return False, None, [f"实例化失败: {e}"]

        return False, None, result.errors


# ============================================================================
# 便捷函数
# ============================================================================


def validate_tool_code(code: str, tool_name: str) -> ToolValidationResult:
    """便捷函数：验证工具代码

    Args:
        code: Python 代码
        tool_name: 工具名称

    Returns:
        ToolValidationResult: 验证结果
    """
    validator = ToolCodeValidator()
    return validator.validate_full(code, tool_name)
