import asyncio
import functools
import inspect
from abc import ABC, abstractmethod
from threading import Timer, Lock
from typing import Callable, Any, Optional

from app.log import logger


class BaseDebouncer(ABC):
    """
    防抖器的抽象基类。定义了防抖器的基本接口和日志功能。
    所有防抖器实现类必须继承此类并实现其抽象方法。
    """
    def __init__(self, func: Callable, interval: float, *,
                 leading: bool = False, enable_logging: bool = False, source: str = ""):
        """
        初始化防抖器实例。
        :param func: 要防抖的函数或协程
        :param interval: 防抖间隔，单位秒
        :param leading: 是否启用前沿模式
        :param enable_logging: 是否启用日志记录
        :param source: 日志来源标识
        """
        self.func = func
        self.interval = interval
        self.leading = leading
        self.enable_logging = enable_logging
        self.source = source

    @abstractmethod
    def __call__(self, *args, **kwargs) -> None:
        """
        定义防抖调用的契约，子类必须实现。
        """
        pass

    @abstractmethod
    def cancel(self) -> None:
        """
        定义取消挂起调用的契约，子类必须实现。
        """
        pass

    def format_log(self, message: str) -> str:
        """
        格式化日志消息，加入 source 前缀。
        """
        return f"[{self.source}] {message}" if self.source else message

    def log(self, level: str, message: str):
        """
        根据日志级别记录日志。
        """
        if self.enable_logging:
            log_method = getattr(logger, level, logger.debug)
            log_method(self.format_log(message))

    def log_debug(self, message: str):
        """
        记录调试日志。
        """
        self.log("debug", message)

    def log_info(self, message: str):
        """
        记录信息日志。
        """
        self.log("info", message)

    def log_warning(self, message: str):
        """
        记录警告日志。
        """
        self.log("warning", message)

    def error(self, message: str):
        """
        记录错误日志。
        """
        self.log("error", message)

    def critical(self, message: str):
        """
        记录严重错误日志。
        """
        self.log("critical", message)


class Debouncer(BaseDebouncer):
    """
    同步防抖实现类
    """

    def __init__(self, *args, **kwargs):
        """
        初始化防抖器实例。
        """
        super().__init__(*args, **kwargs)
        self.timer: Optional[Timer] = None
        self.lock = Lock()
        # 用于前沿模式，标记是否处于“冷却”或“不应期”
        self.is_cooling_down = False

    def __call__(self, *args, **kwargs) -> None:
        """
        调用防抖函数。
        :param args:
        :param kwargs:
        :return:
        """
        with self.lock:
            if self.leading:
                self._call_leading(*args, **kwargs)
            else:
                self._call_trailing(*args, **kwargs)

    def _call_leading(self, *args, **kwargs):
        """
        前沿模式的逻辑。
        """
        # 如果不在冷却期，则立即执行
        if not self.is_cooling_down:
            self.log_info("前沿模式: 立即执行函数。")
            self.func(*args, **kwargs)

        # 无论是否执行，都重置冷却计时器
        if self.timer and self.timer.is_alive():
            self.timer.cancel()

        # 设置自己进入冷却期
        self.is_cooling_down = True

        # 在间隔结束后，将冷却状态解除
        self.timer = Timer(self.interval, self._end_cool_down)
        self.timer.start()
        self.log_debug(f"前沿模式: 进入 {self.interval} 秒的冷却期。")

    def _end_cool_down(self):
        """
        计时器到期后，解除冷却状态
        """
        with self.lock:
            self.is_cooling_down = False
            self.log_debug("前沿模式: 冷却时间结束，可以再次立即执行。")

    def _call_trailing(self, *args, **kwargs):
        """
        后沿模式的逻辑。
        """
        # 【日志点】记录计时器被重置
        if self.timer and self.timer.is_alive():
            self.timer.cancel()
            self.log_debug("后沿模式: 检测到新的调用，已重置计时器。")

        def execute():
            self.log_info("后沿模式: 计时结束，开始执行函数。")
            self.func(*args, **kwargs)

        self.timer = Timer(self.interval, execute)
        self.timer.start()
        self.log_debug(f"后沿模式: 计时器已启动，将在 {self.interval} 秒后执行。")

    def cancel(self) -> None:
        """
        取消任何挂起的调用，并重置状态。
        """
        with self.lock:
            if self.timer and self.timer.is_alive():
                self.timer.cancel()
                self.timer = None
                self.log_info("防抖器被手动取消。")
            self.is_cooling_down = False


class AsyncDebouncer(BaseDebouncer):
    """
    异步防抖实现类。
    """
    def __init__(self, *args, **kwargs):
        """
        初始化异步防抖器实例。
        """
        super().__init__(*args, **kwargs)
        self.task: Optional[asyncio.Task] = None
        self.lock = asyncio.Lock()
        self.is_cooling_down = False

    async def __call__(self, *args, **kwargs) -> None:
        """
        异步调用防抖函数。
        """
        async with self.lock:
            if self.leading:
                await self._call_leading(*args, **kwargs)
            else:
                await self._call_trailing(*args, **kwargs)

    async def _call_leading(self, *args, **kwargs):
        """
        前沿模式的逻辑。
        """
        if not self.is_cooling_down:
            self.log_info("前沿模式 (async): 立即执行协程。")
            await self.func(*args, **kwargs)

        if self.task and not self.task.done():
            self.task.cancel()

        self.is_cooling_down = True
        self.task = asyncio.create_task(self._end_cool_down())
        self.log_debug(f"前沿模式 (async): 进入 {self.interval} 秒的冷却期。")

    async def _end_cool_down(self):
        """
        计时器到期后，解除冷却状态
        """
        await asyncio.sleep(self.interval)
        async with self.lock:
            self.is_cooling_down = False
            self.log_debug("前沿模式 (async): 冷却时间结束。")

    async def _call_trailing(self, *args, **kwargs):
        """
        后沿模式的逻辑。
        """
        if self.task and not self.task.done():
            self.task.cancel()
            self.log_debug("后沿模式 (async): 检测到新的调用，已取消旧任务。")

        self.task = asyncio.create_task(self._delayed_execute(*args, **kwargs))
        self.log_debug(f"后沿模式 (async): 任务已创建，将在 {self.interval} 秒后执行。")

    async def _delayed_execute(self, *args, **kwargs):
        """
        延迟执行实际的协程函数。
        """
        try:
            await asyncio.sleep(self.interval)
            self.log_info("后沿模式 (async): 延迟结束，开始执行协程。")
            await self.func(*args, **kwargs)
        except asyncio.CancelledError:
            # 任务被取消是正常行为，无需处理
            pass

    async def cancel(self) -> None:
        """
        取消任何挂起的调用，并重置状态。
        """
        async with self.lock:
            if self.task and not self.task.done():
                self.task.cancel()
                self.task = None
                self.log_info("异步防抖器被手动取消。")
            self.is_cooling_down = False


def debounce(interval: float, *, leading: bool = False,
             enable_logging: bool = False, source: str = "") -> Callable:
    """
    支持同步和异步的防抖装饰器工厂。
    """

    def decorator(func: Callable) -> Callable:
        # 检查函数类型，并选择合适的引擎
        if inspect.iscoroutinefunction(func):
            # 异步函数，使用 AsyncDebouncer
            instance = AsyncDebouncer(func, interval,
                                      leading=leading,
                                      enable_logging=enable_logging,
                                      source=source)

            @functools.wraps(func)
            async def async_wrapper(*args, **kwargs) -> Any:
                await instance(*args, **kwargs)

            async_wrapper.cancel = instance.cancel
            return async_wrapper

        else:
            # 同步函数，使用 Debouncer
            instance = Debouncer(func, interval,
                                 leading=leading,
                                 enable_logging=enable_logging,
                                 source=source)

            @functools.wraps(func)
            def wrapper(*args, **kwargs) -> Any:
                instance(*args, **kwargs)

            wrapper.cancel = instance.cancel
            return wrapper

    return decorator
