"""
防抖服务
实现基于 commit hash + 分支的去重策略
"""

import asyncio
import hashlib
import json
from typing import Optional, Dict, Any
from datetime import datetime, timedelta
import structlog
from redis import asyncio as aioredis

from app.config import get_settings

logger = structlog.get_logger()


class DeduplicationService:
    """防抖服务"""
    
    def __init__(self, redis_client: aioredis.Redis):
        self.redis = redis_client
        self.settings = get_settings()
        self.cache_prefix = "webhook:dedup:"
    
    async def is_duplicate(self, dedup_key: str) -> bool:
        """
        检查是否为重复事件
        
        Args:
            dedup_key: 防抖键值 (commit_hash:branch)
            
        Returns:
            bool: True 表示重复，False 表示新事件
        """
        if not self.settings.deduplication.enabled:
            return False
        
        try:
            cache_key = f"{self.cache_prefix}{dedup_key}"
            
            # 检查是否在缓存中
            exists = await self.redis.exists(cache_key)
            if exists:
                logger.info("Duplicate event detected", dedup_key=dedup_key)
                return True
            
            # 记录新事件
            await self._record_event(cache_key, dedup_key)
            logger.info("New event recorded", dedup_key=dedup_key)
            return False
            
        except Exception as e:
            logger.error("Error checking duplication", 
                        dedup_key=dedup_key, error=str(e))
            # 出错时允许通过，避免阻塞
            return False
    
    async def _record_event(self, cache_key: str, dedup_key: str):
        """记录事件到缓存"""
        try:
            # 设置缓存，TTL 为防抖窗口时间
            ttl = self.settings.deduplication.cache_ttl
            await self.redis.setex(cache_key, ttl, json.dumps({
                "dedup_key": dedup_key,
                "timestamp": datetime.utcnow().isoformat(),
                "ttl": ttl
            }))
            
            # 同时记录到时间窗口缓存
            window_key = f"{self.cache_prefix}window:{dedup_key}"
            window_ttl = self.settings.deduplication.window_seconds
            await self.redis.setex(window_key, window_ttl, "1")
            
        except Exception as e:
            logger.error("Error recording event", 
                        cache_key=cache_key, error=str(e))
    
    async def get_event_info(self, dedup_key: str) -> Optional[Dict[str, Any]]:
        """获取事件信息"""
        try:
            cache_key = f"{self.cache_prefix}{dedup_key}"
            data = await self.redis.get(cache_key)
            if data:
                return json.loads(data)
            return None
        except Exception as e:
            logger.error("Error getting event info", 
                        dedup_key=dedup_key, error=str(e))
            return None
    
    async def clear_event(self, dedup_key: str) -> bool:
        """清除事件记录"""
        try:
            cache_key = f"{self.cache_prefix}{dedup_key}"
            window_key = f"{self.cache_prefix}window:{dedup_key}"
            
            # 删除两个缓存键
            await self.redis.delete(cache_key, window_key)
            logger.info("Event cleared", dedup_key=dedup_key)
            return True
            
        except Exception as e:
            logger.error("Error clearing event", 
                        dedup_key=dedup_key, error=str(e))
            return False
    
    async def get_stats(self) -> Dict[str, Any]:
        """获取防抖统计信息"""
        try:
            # 获取所有防抖键
            pattern = f"{self.cache_prefix}*"
            keys = await self.redis.keys(pattern)
            
            # 统计不同类型的键
            total_keys = len(keys)
            window_keys = len([k for k in keys if b"window:" in k])
            event_keys = total_keys - window_keys
            
            # 获取配置信息
            config = {
                "enabled": self.settings.deduplication.enabled,
                "window_seconds": self.settings.deduplication.window_seconds,
                "cache_ttl": self.settings.deduplication.cache_ttl,
                "strategy": self.settings.deduplication.strategy
            }
            
            return {
                "total_keys": total_keys,
                "window_keys": window_keys,
                "event_keys": event_keys,
                "config": config,
                "timestamp": datetime.utcnow().isoformat()
            }
            
        except Exception as e:
            logger.error("Error getting deduplication stats", error=str(e))
            return {"error": str(e)}
    
    async def cleanup_expired_events(self) -> int:
        """清理过期事件"""
        try:
            pattern = f"{self.cache_prefix}*"
            keys = await self.redis.keys(pattern)
            
            cleaned_count = 0
            for key in keys:
                # 检查 TTL
                ttl = await self.redis.ttl(key)
                if ttl <= 0:
                    await self.redis.delete(key)
                    cleaned_count += 1
            
            if cleaned_count > 0:
                logger.info("Cleaned up expired events", count=cleaned_count)
            
            return cleaned_count
            
        except Exception as e:
            logger.error("Error cleaning up expired events", error=str(e))
            return 0
    
    def generate_dedup_key(self, commit_hash: str, branch: str) -> str:
        """
        生成防抖键值
        
        Args:
            commit_hash: 提交哈希
            branch: 分支名
            
        Returns:
            str: 防抖键值
        """
        if self.settings.deduplication.strategy == "commit_branch":
            return f"{commit_hash}:{branch}"
        elif self.settings.deduplication.strategy == "commit_only":
            return commit_hash
        elif self.settings.deduplication.strategy == "branch_only":
            return branch
        else:
            # 默认使用 commit_hash:branch
            return f"{commit_hash}:{branch}"
    
    async def is_in_window(self, dedup_key: str) -> bool:
        """
        检查是否在防抖时间窗口内
        
        Args:
            dedup_key: 防抖键值
            
        Returns:
            bool: True 表示在窗口内
        """
        try:
            window_key = f"{self.cache_prefix}window:{dedup_key}"
            exists = await self.redis.exists(window_key)
            return bool(exists)
            
        except Exception as e:
            logger.error("Error checking window", 
                        dedup_key=dedup_key, error=str(e))
            return False


# 全局防抖服务实例
_dedup_service: Optional[DeduplicationService] = None


def get_deduplication_service() -> DeduplicationService:
    """获取防抖服务实例"""
    global _dedup_service
    if _dedup_service is None:
        # 这里需要从依赖注入获取 Redis 客户端
        # 在实际使用时，应该通过依赖注入传入
        raise RuntimeError("DeduplicationService not initialized")
    return _dedup_service


def set_deduplication_service(service: DeduplicationService):
    """设置防抖服务实例"""
    global _dedup_service
    _dedup_service = service 