#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
壓縮性能監控工具
================

監控 Gzip 壓縮的性能效果，包括壓縮比率、響應時間和文件大小統計。
提供實時性能數據和優化建議。
"""

import time
import threading
from typing import Dict, List, Optional, Tuple
from dataclasses import dataclass, field
from datetime import datetime, timedelta
import json


@dataclass
class CompressionMetrics:
    """壓縮指標數據類"""
    timestamp: datetime
    path: str
    original_size: int
    compressed_size: int
    compression_ratio: float
    response_time: float
    content_type: str
    was_compressed: bool


@dataclass
class CompressionSummary:
    """壓縮摘要統計"""
    total_requests: int = 0
    compressed_requests: int = 0
    total_original_bytes: int = 0
    total_compressed_bytes: int = 0
    average_compression_ratio: float = 0.0
    average_response_time: float = 0.0
    compression_percentage: float = 0.0
    bandwidth_saved: int = 0
    top_compressed_paths: List[Tuple[str, float]] = field(default_factory=list)


class CompressionMonitor:
    """壓縮性能監控器"""
    
    def __init__(self, max_metrics: int = 1000):
        self.max_metrics = max_metrics
        self.metrics: List[CompressionMetrics] = []
        self.lock = threading.Lock()
        self._start_time = datetime.now()
        
        # 路徑統計
        self.path_stats: Dict[str, Dict] = {}
        
        # 內容類型統計
        self.content_type_stats: Dict[str, Dict] = {}
    
    def record_request(self, 
                      path: str,
                      original_size: int,
                      compressed_size: int,
                      response_time: float,
                      content_type: str = "",
                      was_compressed: bool = False):
        """記錄請求的壓縮數據"""
        
        compression_ratio = 0.0
        if original_size > 0 and was_compressed:
            compression_ratio = (1 - compressed_size / original_size) * 100
        
        metric = CompressionMetrics(
            timestamp=datetime.now(),
            path=path,
            original_size=original_size,
            compressed_size=compressed_size,
            compression_ratio=compression_ratio,
            response_time=response_time,
            content_type=content_type,
            was_compressed=was_compressed
        )
        
        with self.lock:
            self.metrics.append(metric)
            
            # 限制記錄數量
            if len(self.metrics) > self.max_metrics:
                self.metrics = self.metrics[-self.max_metrics:]
            
            # 更新路徑統計
            self._update_path_stats(metric)
            
            # 更新內容類型統計
            self._update_content_type_stats(metric)
    
    def _update_path_stats(self, metric: CompressionMetrics):
        """更新路徑統計"""
        path = metric.path
        if path not in self.path_stats:
            self.path_stats[path] = {
                'requests': 0,
                'compressed_requests': 0,
                'total_original_bytes': 0,
                'total_compressed_bytes': 0,
                'total_response_time': 0.0,
                'best_compression_ratio': 0.0
            }
        
        stats = self.path_stats[path]
        stats['requests'] += 1
        stats['total_original_bytes'] += metric.original_size
        stats['total_compressed_bytes'] += metric.compressed_size
        stats['total_response_time'] += metric.response_time
        
        if metric.was_compressed:
            stats['compressed_requests'] += 1
            stats['best_compression_ratio'] = max(
                stats['best_compression_ratio'], 
                metric.compression_ratio
            )
    
    def _update_content_type_stats(self, metric: CompressionMetrics):
        """更新內容類型統計"""
        content_type = metric.content_type or 'unknown'
        if content_type not in self.content_type_stats:
            self.content_type_stats[content_type] = {
                'requests': 0,
                'compressed_requests': 0,
                'total_original_bytes': 0,
                'total_compressed_bytes': 0,
                'average_compression_ratio': 0.0
            }
        
        stats = self.content_type_stats[content_type]
        stats['requests'] += 1
        stats['total_original_bytes'] += metric.original_size
        stats['total_compressed_bytes'] += metric.compressed_size
        
        if metric.was_compressed:
            stats['compressed_requests'] += 1
            
            # 重新計算平均壓縮比
            if stats['total_original_bytes'] > 0:
                stats['average_compression_ratio'] = (
                    1 - stats['total_compressed_bytes'] / stats['total_original_bytes']
                ) * 100
    
    def get_summary(self, time_window: Optional[timedelta] = None) -> CompressionSummary:
        """獲取壓縮摘要統計"""
        with self.lock:
            metrics = self.metrics
            
            # 如果指定時間窗口，過濾數據
            if time_window:
                cutoff_time = datetime.now() - time_window
                metrics = [m for m in metrics if m.timestamp >= cutoff_time]
            
            if not metrics:
                return CompressionSummary()
            
            total_requests = len(metrics)
            compressed_requests = sum(1 for m in metrics if m.was_compressed)
            total_original_bytes = sum(m.original_size for m in metrics)
            total_compressed_bytes = sum(m.compressed_size for m in metrics)
            total_response_time = sum(m.response_time for m in metrics)
            
            # 計算統計數據
            compression_percentage = (compressed_requests / total_requests * 100) if total_requests > 0 else 0
            average_compression_ratio = 0.0
            bandwidth_saved = 0
            
            if total_original_bytes > 0:
                average_compression_ratio = (1 - total_compressed_bytes / total_original_bytes) * 100
                bandwidth_saved = total_original_bytes - total_compressed_bytes
            
            average_response_time = total_response_time / total_requests if total_requests > 0 else 0
            
            # 獲取壓縮效果最好的路徑
            top_compressed_paths = self._get_top_compressed_paths()
            
            return CompressionSummary(
                total_requests=total_requests,
                compressed_requests=compressed_requests,
                total_original_bytes=total_original_bytes,
                total_compressed_bytes=total_compressed_bytes,
                average_compression_ratio=average_compression_ratio,
                average_response_time=average_response_time,
                compression_percentage=compression_percentage,
                bandwidth_saved=bandwidth_saved,
                top_compressed_paths=top_compressed_paths
            )
    
    def _get_top_compressed_paths(self, limit: int = 5) -> List[Tuple[str, float]]:
        """獲取壓縮效果最好的路徑"""
        path_ratios = []
        
        for path, stats in self.path_stats.items():
            if stats['compressed_requests'] > 0 and stats['total_original_bytes'] > 0:
                compression_ratio = (
                    1 - stats['total_compressed_bytes'] / stats['total_original_bytes']
                ) * 100
                path_ratios.append((path, compression_ratio))
        
        # 按壓縮比排序
        path_ratios.sort(key=lambda x: x[1], reverse=True)
        return path_ratios[:limit]
    
    def get_path_stats(self) -> Dict[str, Dict]:
        """獲取路徑統計"""
        with self.lock:
            return self.path_stats.copy()
    
    def get_content_type_stats(self) -> Dict[str, Dict]:
        """獲取內容類型統計"""
        with self.lock:
            return self.content_type_stats.copy()
    
    def get_recent_metrics(self, limit: int = 100) -> List[CompressionMetrics]:
        """獲取最近的指標數據"""
        with self.lock:
            return self.metrics[-limit:] if self.metrics else []
    
    def reset_stats(self):
        """重置統計數據"""
        with self.lock:
            self.metrics.clear()
            self.path_stats.clear()
            self.content_type_stats.clear()
            self._start_time = datetime.now()
    
    def export_stats(self) -> Dict:
        """導出統計數據為字典格式"""
        summary = self.get_summary()
        
        return {
            'summary': {
                'total_requests': summary.total_requests,
                'compressed_requests': summary.compressed_requests,
                'compression_percentage': round(summary.compression_percentage, 2),
                'average_compression_ratio': round(summary.average_compression_ratio, 2),
                'bandwidth_saved_mb': round(summary.bandwidth_saved / (1024 * 1024), 2),
                'average_response_time_ms': round(summary.average_response_time * 1000, 2),
                'monitoring_duration_hours': round(
                    (datetime.now() - self._start_time).total_seconds() / 3600, 2
                )
            },
            'top_compressed_paths': [
                {'path': path, 'compression_ratio': round(ratio, 2)}
                for path, ratio in summary.top_compressed_paths
            ],
            'path_stats': {
                path: {
                    'requests': stats['requests'],
                    'compression_percentage': round(
                        stats['compressed_requests'] / stats['requests'] * 100, 2
                    ) if stats['requests'] > 0 else 0,
                    'average_response_time_ms': round(
                        stats['total_response_time'] / stats['requests'] * 1000, 2
                    ) if stats['requests'] > 0 else 0,
                    'bandwidth_saved_kb': round(
                        (stats['total_original_bytes'] - stats['total_compressed_bytes']) / 1024, 2
                    )
                }
                for path, stats in self.path_stats.items()
            },
            'content_type_stats': {
                content_type: {
                    'requests': stats['requests'],
                    'compression_percentage': round(
                        stats['compressed_requests'] / stats['requests'] * 100, 2
                    ) if stats['requests'] > 0 else 0,
                    'average_compression_ratio': round(stats['average_compression_ratio'], 2)
                }
                for content_type, stats in self.content_type_stats.items()
            }
        }


# 全域監控器實例
_compression_monitor: Optional[CompressionMonitor] = None


def get_compression_monitor() -> CompressionMonitor:
    """獲取全域壓縮監控器實例"""
    global _compression_monitor
    if _compression_monitor is None:
        _compression_monitor = CompressionMonitor()
    return _compression_monitor
