# -*- coding: utf-8 -*-
"""
@Project : VientianeArena
@File    : decorator_checker.py
@Author  : YL_top01
@Date    : 2025/8/25 14:44
"""

# Built-in modules
import inspect
import functools
import warnings
from typing import Optional, Callable, Any
from datetime import datetime

# Third-party modules
# (无第三方依赖)

# Local modules
from yltop.recording.checks.checkexecutor_base import CheckExecutor, CheckLevel
from yltop.recording.core.logger import record_error, LoggerManager
from yltop.recording.errors.base import BaseCustomError
from yltop.recording.errors.errors import eTypeError
from yltop.recording.errors.functions import check_type, check_logger_initialized, check_function
from yltop.recording.errors.parameter import ParameterMissingError, TooManyParametersError, ParameterTypeError
from yltop.recording.errors.validation import ValidationError
from yltop.recording.utils.object import locate


class DecoratorCheckExecutor(CheckExecutor):
    """装饰器检查执行器"""

    def check_function(self, func: callable, context: dict = None) -> bool:
        """检查对象是否为函数"""
        condition = inspect.isfunction(func)
        error = eTypeError(func, "function")
        return self.execute(condition, error, context)

    def check_decorator(self, decorator: callable, context: dict = None) -> bool:
        """检查对象是否为有效的装饰器"""
        condition = inspect.isfunction(decorator) and len(inspect.signature(decorator).parameters) == 1
        error = ValidationError("无效的装饰器格式")
        return self.execute(condition, error, context)



# region 验证装饰器 - 改进错误处理
def validate_parameters(enable_type_check=True):
    """参数校验装饰器，支持类型检查"""

    def decorator(func):
        sig = inspect.signature(func)
        param_types = {
            name: param.annotation
            for name, param in sig.parameters.items()
            if param.annotation != inspect.Parameter.empty
        }

        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            try:
                # 参数绑定校验
                try:
                    bound = sig.bind(*args, **kwargs)
                except TypeError as e:
                    err_msg = str(e)
                    if "missing" in err_msg:
                        raise ParameterMissingError(func) from e
                    elif "too many" in err_msg:
                        raise TooManyParametersError(func) from e

                # 类型校验
                if enable_type_check:
                    for name, value in bound.arguments.items():
                        if name in param_types:
                            if not isinstance(value, param_types[name]):
                                raise ParameterTypeError(
                                    func,
                                    expect=param_types[name].__name__,
                                    actual=type(value).__name__,
                                    switch=True
                                )
                return func(*args, **kwargs)
            except Exception as e:
                record_error(e)
                raise

        return wrapper

    return decorator


# endregion



def log_function_errors(
        logger_name: Optional[str] = None,
        log_level: str = "ERROR",
        continue_on_error: bool = True,
        capture_args: bool = True
) -> Callable:
    """函数错误日志装饰器，适配游戏开发场景（复用现有检查执行器）"""

    def decorator(func: Callable) -> Callable:
        @functools.wraps(func)
        def wrapper(*args, **kwargs) -> Any:
            # 1. 复用 LoggerCheckExecutor 检查日志系统是否初始化（符合现有架构）
            logger_initialized = check_logger_initialized(level=CheckLevel.BOOL)
            if not logger_initialized:
                warnings.warn("日志系统未初始化，无法记录函数错误", RuntimeWarning)
                if not continue_on_error:
                    raise RuntimeError("日志系统未初始化，终止执行")
                return None  # 日志未初始化时的安全返回

            # 2. 复用现有检查执行器验证函数类型（确保装饰的是有效函数）
            if not check_function(func, level=CheckLevel.BOOL):
                warnings.warn(f"@{log_function_errors.__name__} 只能装饰函数对象", RuntimeWarning)
                return func(*args, **kwargs)  # 非函数对象直接执行，不处理

            # 3. 获取日志器（复用现有 LoggerManager）
            logger = LoggerManager.get_logger(logger_name) if logger_name else LoggerManager.get_logger()

            try:
                return func(*args, **kwargs)
            except Exception as e:
                # 4. 构建错误上下文（复用 utils.locate 工具）
                context = {
                    "function": f"{func.__module__}.{func.__name__}",
                    "location": locate(func),
                    "timestamp": datetime.now().isoformat()
                }

                # 5. 捕获函数参数（控制长度避免日志膨胀）
                if capture_args:
                    context["args"] = str(args)[:500]
                    context["kwargs"] = str(kwargs)[:500]

                # 6. 记录异常（区分自定义与系统异常）
                if isinstance(e, BaseCustomError):
                    # 自定义异常直接记录（复用 record_error 逻辑）
                    logger.record_error(e, extra_context=context)
                else:
                    # 系统异常转换为自定义异常（复用 eTypeError）
                    custom_err = eTypeError(
                        obj=func,
                        expected_type="valid execution",
                        message=f"Function error: {str(e)}", **context
                    )
                    logger.record(log_level, str(custom_err), context)

                # 7. 控制是否中断执行（游戏场景默认不中断）
                if not continue_on_error:
                    raise

                # 8. 返回默认值（基于函数返回注解，兼容现有类型检查逻辑）
                return_type = func.__annotations__.get("return")
                if return_type is None:
                    return None
                # 对基础类型返回默认值（复用 TypeCheckExecutor 的基础类型判断）
                if check_type(return_type, "basic", level=CheckLevel.BOOL):
                    return return_type() if return_type != type(None) else None
                return None

        return wrapper

    return decorator


def require_logger(func):
    """确保日志系统已初始化的装饰器"""

    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        if not check_logger_initialized(level=CheckLevel.BOOL):
            warnings.warn("请先初始化日志系统", RuntimeWarning)
            return None
        return func(*args, **kwargs)

    return wrapper