""" 查询 Performance Monitoring Utilities

Provides tools to monitor and analyze database query performance.
"""

from typing import Dict, List, Any
from dataclasses import dataclass, field
from datetime import datetime
import logging
import time
from contextlib import contextmanager

logger = logging.getLogger(__name__)


@dataclass
class QueryMetrics:
    """ 指标 for a single query execution."""
    query: str
    duration: float
    timestamp: float
    model: str = ""
    operation: str = ""
    params: Dict[str, Any] = field(default_factory=dict)


class QueryMonitor:
    """ 监视器 and track database query performance.
    
    Features:
    - Track query execution time
    - Detect slow queries
    - Detect N+1 query patterns
    - Provide performance statistics
    """
    
    def __init__(self, slow_query_threshold: float = 1.0):
        """ 初始化 query monitor.
        
        Args:
            slow_query_threshold: Threshold in seconds for slow query detection
        """
        self.slow_query_threshold = slow_query_threshold
        self.queries: List[QueryMetrics] = []
        self.slow_queries: List[QueryMetrics] = []
        self._enabled = True
    
    def enable(self):
        """Enable query monitoring."""
        self._enabled = True
    
    def disable(self):
        """Disable query monitoring."""
        self._enabled = False
    
    def clear(self):
        """ 清除 all recorded queries."""
        self.queries.clear()
        self.slow_queries.clear()
    
    @contextmanager
    def track_query(self, query: str, model: str = "", operation: str = "", params: Dict[str, Any] = None):
        """
        Context manager to track query execution.
        
        Usage:
            with monitor.track_query("SELECT * FROM projects", model="Project", operation="list"):
                # Execute query
                pass
        
        Args:
            query: SQL query string
            model: Model name
            operation: Operation name (e.g., "list", "get", "create")
            params: Query parameters
        """
        if not self._enabled:
            yield
            return
        
        start_time = time.time()
        try:
            yield
        finally:
            duration = time.time() - start_time
            timestamp = datetime.now().timestamp()
            
            metrics = QueryMetrics(
                query=query[:500],  # Truncate long queries
                duration=duration,
                timestamp=timestamp,
                model=model,
                operation=operation,
                params=params or {}
            )
            
            self.queries.append(metrics)
            
            if duration > self.slow_query_threshold:
                self.slow_queries.append(metrics)
                logger.warning(
                    f"Slow query detected: {duration:.2f}s - {model}.{operation}",
                    extra={
                        "query_time": duration,
                        "model": model,
                        "operation": operation,
                        "query": query[:200]
                    }
                )
    
    def detect_n_plus_one(self, threshold: int = 10) -> List[Dict[str, Any]]:
        """
        Detect potential N+1 query patterns.
        
        N+1 pattern: One query to get a list, then N queries to get related data.
        
        Args:
            threshold: Minimum number of similar queries to consider as N+1
            
        Returns:
            List of detected N+1 patterns
        """
        if len(self.queries) < threshold:
            return []
        
        patterns = []
        query_groups: Dict[str, List[QueryMetrics]] = {}
        
        # Group similar queries
        for query_metric in self.queries:
            # 正常ize query by removing specific IDs
            normalized = self._normalize_query(query_metric.query)
            if normalized not in query_groups:
                query_groups[normalized] = []
            query_groups[normalized].append(query_metric)
        
        # 查找 groups with many similar queries
        for normalized_query, group in query_groups.items():
            if len(group) >= threshold:
                # Check if queries are sequential (potential N+1)
                timestamps = [q.timestamp for q in group]
                if self._is_sequential(timestamps):
                    patterns.append({
                        "query": normalized_query,
                        "count": len(group),
                        "total_time": sum(q.duration for q in group),
                        "avg_time": sum(q.duration for q in group) / len(group),
                        "model": group[0].model,
                        "operation": group[0].operation
                    })
        
        return patterns
    
    def get_statistics(self) -> Dict[str, Any]:
        """ 获取 query performance statistics.
        
        Returns:
            Dictionary with statistics
        """
        if not self.queries:
            return {
                "total_queries": 0,
                "total_time": 0,
                "avg_time": 0,
                "slow_queries": 0,
                "n_plus_one_patterns": []
            }
        
        total_time = sum(q.duration for q in self.queries)
        
        return {
            "total_queries": len(self.queries),
            "total_time": total_time,
            "avg_time": total_time / len(self.queries),
            "min_time": min(q.duration for q in self.queries),
            "max_time": max(q.duration for q in self.queries),
            "slow_queries": len(self.slow_queries),
            "slow_query_threshold": self.slow_query_threshold,
            "n_plus_one_patterns": self.detect_n_plus_one()
        }
    
    def get_slow_queries(self, limit: int = 10) -> List[Dict[str, Any]]:
        """ 获取 slowest queries.
        
        Args:
            limit: Maximum number of queries to return
            
        Returns:
            List of slow queries sorted by duration
        """
        sorted_queries = sorted(self.slow_queries, key=lambda q: q.duration, reverse=True)
        
        return [
            {
                "query": q.query,
                "duration": q.duration,
                "model": q.model,
                "operation": q.operation,
                "timestamp": q.timestamp
            }
            for q in sorted_queries[:limit]
        ]
    
    def _normalize_query(self, query: str) -> str:
        """ 正常ize query by removing specific values.
        
        Args:
            query: SQL query string
            
        Returns:
            Normalized query string
        """
        import re
        # Remove UUIDs
        normalized = re.sub(r'[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}', 'UUID', query, flags=re.IGNORECASE)
        # Remove numbers
        normalized = re.sub(r'\b\d+\b', 'N', normalized)
        # Remove quoted strings
        normalized = re.sub(r"'[^']*'", "'STRING'", normalized)
        return normalized
    
    def _is_sequential(self, timestamps: List[float], max_gap: float = 1.0) -> bool:
        """
        Check if timestamps are sequential (within max_gap seconds).
        
        Args:
            timestamps: List of timestamps
            max_gap: Maximum gap between timestamps in seconds
            
        Returns:
            True if timestamps are sequential
        """
        if len(timestamps) < 2:
            return False
        
        sorted_timestamps = sorted(timestamps)
        for i in range(1, len(sorted_timestamps)):
            gap = sorted_timestamps[i] - sorted_timestamps[i-1]
            if gap > max_gap:
                return False
        
        return True


# 全局的 query monitor instance
query_monitor = QueryMonitor()
