"""
性能优化模块

提供缓存、批量操作优化等功能。
"""
import asyncio
import time
from functools import wraps, lru_cache
from typing import Optional, Dict, Any, Callable, TypeVar, ParamSpec
from dataclasses import dataclass, field
from loguru import logger


P = ParamSpec('P')
T = TypeVar('T')


@dataclass
class CacheEntry:
    """缓存条目"""
    value: Any
    timestamp: float
    ttl: float
    hits: int = 0


class AsyncTTLCache:
    """
    异步 TTL 缓存

    支持过期时间和 LRU 淘汰策略。
    """

    def __init__(self, maxsize: int = 128, ttl: float = 60.0):
        """
        初始化缓存

        Args:
            maxsize: 最大缓存数量
            ttl: 缓存有效期（秒）
        """
        self.maxsize = maxsize
        self.ttl = ttl
        self._cache: Dict[str, CacheEntry] = {}
        self._access_order: list = []
        self._hits = 0
        self._misses = 0

    def _make_key(self, *args, **kwargs) -> str:
        """生成缓存键"""
        key_parts = [str(arg) for arg in args]
        key_parts.extend(f"{k}={v}" for k, v in sorted(kwargs.items()))
        return ":".join(key_parts)

    def get(self, key: str) -> Optional[Any]:
        """
        获取缓存值

        Args:
            key: 缓存键

        Returns:
            缓存值或 None
        """
        if key not in self._cache:
            self._misses += 1
            return None

        entry = self._cache[key]

        # 检查是否过期
        if time.time() - entry.timestamp > entry.ttl:
            self.delete(key)
            self._misses += 1
            return None

        # 更新访问顺序
        if key in self._access_order:
            self._access_order.remove(key)
        self._access_order.append(key)

        entry.hits += 1
        self._hits += 1
        return entry.value

    def set(self, key: str, value: Any) -> None:
        """
        设置缓存值

        Args:
            key: 缓存键
            value: 缓存值
        """
        # LRU 淘汰
        while len(self._cache) >= self.maxsize:
            if self._access_order:
                oldest_key = self._access_order.pop(0)
                del self._cache[oldest_key]
            else:
                break

        self._cache[key] = CacheEntry(
            value=value,
            timestamp=time.time(),
            ttl=self.ttl,
        )
        self._access_order.append(key)

    def delete(self, key: str) -> None:
        """删除缓存"""
        if key in self._cache:
            del self._cache[key]
        if key in self._access_order:
            self._access_order.remove(key)

    def clear(self) -> None:
        """清空缓存"""
        self._cache.clear()
        self._access_order.clear()

    def get_stats(self) -> Dict[str, Any]:
        """获取缓存统计"""
        total = self._hits + self._misses
        hit_rate = self._hits / total if total > 0 else 0

        return {
            "size": len(self._cache),
            "maxsize": self.maxsize,
            "ttl": self.ttl,
            "hits": self._hits,
            "misses": self._misses,
            "hit_rate": hit_rate,
        }


def async_cached(
    maxsize: int = 128,
    ttl: float = 60.0,
):
    """
    异步函数缓存装饰器

    Args:
        maxsize: 最大缓存数量
        ttl: 缓存有效期（秒）

    Example:
        @async_cached(maxsize=100, ttl=30.0)
        async def get_element_text(selector: str) -> str:
            ...
    """
    cache = AsyncTTLCache(maxsize=maxsize, ttl=ttl)

    def decorator(func: Callable[P, T]) -> Callable[P, T]:
        @wraps(func)
        async def wrapper(*args, **kwargs):
            key = cache._make_key(*args, **kwargs)

            # 检查缓存
            cached = cache.get(key)
            if cached is not None:
                return cached

            # 执行函数
            result = await func(*args, **kwargs)

            # 缓存结果
            cache.set(key, result)
            return result

        # 添加缓存管理方法
        wrapper.cache_clear = cache.clear
        wrapper.cache_stats = cache.get_stats

        return wrapper

    return decorator


class PropertyCache:
    """
    属性缓存 Mixin

    用于缓存类属性的计算结果。
    """

    def __init__(self):
        self._property_cache: Dict[str, Any] = {}
        self._cache_timestamps: Dict[str, float] = {}
        self._cache_ttl: float = 5.0

    def _get_cached_property(self, name: str) -> Optional[Any]:
        """获取缓存的属性值"""
        if name in self._property_cache:
            timestamp = self._cache_timestamps.get(name, 0)
            if time.time() - timestamp < self._cache_ttl:
                return self._property_cache[name]
        return None

    def _set_cached_property(self, name: str, value: Any) -> None:
        """设置缓存的属性值"""
        self._property_cache[name] = value
        self._cache_timestamps[name] = time.time()

    def _clear_property_cache(self, name: Optional[str] = None) -> None:
        """清除属性缓存"""
        if name:
            self._property_cache.pop(name, None)
            self._cache_timestamps.pop(name, None)
        else:
            self._property_cache.clear()
            self._cache_timestamps.clear()


class BatchOperations:
    """
    批量操作优化器

    提供批量元素操作的优化实现。
    """

    def __init__(self, page):
        self._page = page

    async def get_texts(self, selector: str) -> list:
        """
        批量获取文本（一次 evaluate 调用）

        Args:
            selector: 元素选择器

        Returns:
            list: 文本列表
        """
        return await self._page.evaluate(f"""
            () => {{
                const elements = document.querySelectorAll('{selector}');
                return Array.from(elements).map(el => el.textContent || '');
            }}
        """)

    async def get_attributes(self, selector: str, attribute: str) -> list:
        """
        批量获取属性值

        Args:
            selector: 元素选择器
            attribute: 属性名

        Returns:
            list: 属性值列表
        """
        return await self._page.evaluate(f"""
            () => {{
                const elements = document.querySelectorAll('{selector}');
                return Array.from(elements).map(el => el.getAttribute('{attribute}') || '');
            }}
        """)

    async def set_values(self, selector: str, values: list) -> int:
        """
        批量设置输入值

        Args:
            selector: 元素选择器
            values: 值列表

        Returns:
            int: 成功设置的元素数量
        """
        values_js = str(values)
        return await self._page.evaluate(f"""
            () => {{
                const elements = document.querySelectorAll('{selector}');
                const values = {values_js};
                let count = 0;
                elements.forEach((el, i) => {{
                    if (i < values.length && el.tagName === 'INPUT') {{
                        el.value = values[i];
                        count++;
                    }}
                }});
                return count;
            }}
        """)

    async def click_all(self, selector: str, delay: float = 0.1) -> int:
        """
        批量点击元素

        Args:
            selector: 元素选择器
            delay: 点击间隔

        Returns:
            int: 成功点击的元素数量
        """
        count = 0
        elements = self._page.locator(selector)
        total = await elements.count()

        for i in range(total):
            try:
                await elements.nth(i).click()
                count += 1
                if i < total - 1:
                    await asyncio.sleep(delay)
            except Exception as e:
                logger.warning(f"点击元素 {i} 失败: {e}")

        return count


class PerformanceMonitor:
    """
    性能监控器

    监控操作耗时。
    """

    def __init__(self):
        self._operations: Dict[str, list] = {}
        self._start_times: Dict[str, float] = {}

    def start(self, operation: str) -> None:
        """开始计时"""
        self._start_times[operation] = time.time()

    def end(self, operation: str) -> float:
        """结束计时"""
        start_time = self._start_times.pop(operation, None)
        if start_time is None:
            return 0.0

        elapsed = time.time() - start_time

        if operation not in self._operations:
            self._operations[operation] = []
        self._operations[operation].append(elapsed)

        return elapsed

    def get_stats(self, operation: str) -> Dict[str, float]:
        """获取操作统计"""
        times = self._operations.get(operation, [])

        if not times:
            return {"count": 0, "total": 0, "avg": 0, "min": 0, "max": 0}

        return {
            "count": len(times),
            "total": sum(times),
            "avg": sum(times) / len(times),
            "min": min(times),
            "max": max(times),
        }

    def get_all_stats(self) -> Dict[str, Dict[str, float]]:
        """获取所有操作统计"""
        return {op: self.get_stats(op) for op in self._operations}


def timed(operation_name: Optional[str] = None):
    """
    计时装饰器

    Example:
        @timed("click_element")
        async def click(self, selector: str):
            ...
    """
    def decorator(func: Callable[P, T]) -> Callable[P, T]:
        name = operation_name or func.__name__

        @wraps(func)
        async def wrapper(*args, **kwargs):
            start_time = time.time()
            try:
                result = await func(*args, **kwargs)
                elapsed = time.time() - start_time
                logger.debug(f"[{name}] 耗时: {elapsed:.3f}s")
                return result
            except Exception as e:
                elapsed = time.time() - start_time
                logger.warning(f"[{name}] 失败 (耗时: {elapsed:.3f}s): {e}")
                raise

        return wrapper

    return decorator


class LFUCache:
    """
    LFU (Least Frequently Used) 缓存

    根据访问频率淘汰缓存项，适合热点数据场景。
    """

    def __init__(self, maxsize: int = 128):
        """
        初始化 LFU 缓存

        Args:
            maxsize: 最大缓存数量
        """
        self.maxsize = maxsize
        self._cache: Dict[str, CacheEntry] = {}
        self._freq: Dict[str, int] = {}  # 访问频率
        self._hits = 0
        self._misses = 0

    def get(self, key: str) -> Optional[Any]:
        """获取缓存值，同时增加访问频率"""
        if key not in self._cache:
            self._misses += 1
            return None

        # 增加访问频率
        self._freq[key] = self._freq.get(key, 0) + 1

        entry = self._cache[key]

        # 检查是否过期
        if time.time() - entry.timestamp > entry.ttl:
            self.delete(key)
            self._misses += 1
            return None

        self._hits += 1
        return entry.value

    def set(self, key: str, value: Any, ttl: float = 60.0) -> None:
        """设置缓存值"""
        # LFU 淘汰：移除访问频率最低的
        while len(self._cache) >= self.maxsize:
            if self._freq:
                # 找到频率最低的键
                min_key = min(self._freq, key=self._freq.get)
                self.delete(min_key)
            else:
                break

        self._cache[key] = CacheEntry(
            value=value,
            timestamp=time.time(),
            ttl=ttl,
        )
        self._freq[key] = 1  # 初始频率为1

    def delete(self, key: str) -> None:
        """删除缓存"""
        self._cache.pop(key, None)
        self._freq.pop(key, None)

    def clear(self) -> None:
        """清空缓存"""
        self._cache.clear()
        self._freq.clear()

    def get_stats(self) -> Dict[str, Any]:
        """获取缓存统计"""
        total = self._hits + self._misses
        hit_rate = self._hits / total if total > 0 else 0

        return {
            "size": len(self._cache),
            "maxsize": self.maxsize,
            "hits": self._hits,
            "misses": self._misses,
            "hit_rate": hit_rate,
            "top_keys": sorted(self._freq.items(), key=lambda x: x[1], reverse=True)[:10],
        }


class AsyncMemoize:
    """
    异步记忆化装饰器

    缓存异步函数的结果，避免重复计算。
    支持过期时间和自动刷新。
    """

    def __init__(
        self,
        ttl: float = 60.0,
        maxsize: int = 128,
        auto_refresh: bool = False,
    ):
        """
        初始化记忆化器

        Args:
            ttl: 缓存有效期（秒）
            maxsize: 最大缓存数量
            auto_refresh: 是否在过期时后台刷新
        """
        self.ttl = ttl
        self.maxsize = maxsize
        self.auto_refresh = auto_refresh
        self._cache: Dict[str, tuple] = {}  # (value, timestamp, task)
        self._lock = asyncio.Lock()

    def _make_key(self, *args, **kwargs) -> str:
        """生成缓存键"""
        key_parts = [str(arg) for arg in args]
        key_parts.extend(f"{k}={v}" for k, v in sorted(kwargs.items()))
        return ":".join(key_parts)

    async def get_or_compute(self, key: str, compute_func: Callable) -> Any:
        """
        获取缓存或计算结果

        Args:
            key: 缓存键
            compute_func: 计算函数

        Returns:
            缓存值或计算结果
        """
        async with self._lock:
            # 检查缓存
            if key in self._cache:
                value, timestamp, _ = self._cache[key]
                age = time.time() - timestamp

                # 未过期，直接返回
                if age < self.ttl:
                    return value

                # 过期但支持自动刷新
                if self.auto_refresh:
                    # 返回旧值，后台刷新
                    task = asyncio.create_task(compute_func())
                    self._cache[key] = (value, timestamp, task)
                    return value

            # 需要计算
            result = await compute_func()

            # 缓存结果
            self._evict_if_needed()
            self._cache[key] = (result, time.time(), None)

            return result

    def _evict_if_needed(self) -> None:
        """如果需要，淘汰旧缓存"""
        while len(self._cache) >= self.maxsize:
            # 移除最旧的
            oldest_key = min(self._cache.keys(), key=lambda k: self._cache[k][1])
            del self._cache[oldest_key]

    def clear(self) -> None:
        """清空缓存"""
        self._cache.clear()

    def get_stats(self) -> Dict[str, Any]:
        """获取缓存统计"""
        return {
            "size": len(self._cache),
            "maxsize": self.maxsize,
            "ttl": self.ttl,
            "auto_refresh": self.auto_refresh,
        }


def memoize(
    ttl: float = 60.0,
    maxsize: int = 128,
    auto_refresh: bool = False,
):
    """
    记忆化装饰器

    Example:
        @memoize(ttl=30.0, maxsize=100)
        async def fetch_data(url: str) -> dict:
            async with aiohttp.ClientSession() as session:
                async with session.get(url) as response:
                    return await response.json()
    """
    memoizer = AsyncMemoize(ttl=ttl, maxsize=maxsize, auto_refresh=auto_refresh)

    def decorator(func: Callable[P, T]) -> Callable[P, T]:
        @wraps(func)
        async def wrapper(*args, **kwargs):
            key = memoizer._make_key(*args, **kwargs)
            return await memoizer.get_or_compute(key, lambda: func(*args, **kwargs))

        # 添加缓存管理方法
        wrapper.cache_clear = memoizer.clear
        wrapper.cache_stats = memoizer.get_stats

        return wrapper

    return decorator
