"""
Scene Slot Bundle Metrics Service.
[AC-SCENE-SLOT-04] 场景槽位包监控指标

职责：
1. 收集场景槽位包相关的监控指标
2. 提供告警检测接口
3. 支持指标导出
"""

import logging
from dataclasses import dataclass, field
from datetime import datetime
from typing import Any
from collections import defaultdict
import threading

logger = logging.getLogger(__name__)


@dataclass
class SceneSlotMetricPoint:
    """单个指标数据点"""
    timestamp: datetime
    tenant_id: str
    scene_key: str
    metric_name: str
    value: float
    tags: dict[str, str] = field(default_factory=dict)


@dataclass
class SceneSlotMetricsSummary:
    """指标汇总"""
    total_requests: int = 0
    cache_hits: int = 0
    cache_misses: int = 0
    missing_slots_triggered: int = 0
    ask_back_triggered: int = 0
    scene_not_configured: int = 0
    slot_not_found: int = 0
    avg_completion_ratio: float = 0.0
    
    @property
    def cache_hit_rate(self) -> float:
        if self.total_requests == 0:
            return 0.0
        return self.cache_hits / self.total_requests


class SceneSlotMetricsCollector:
    """
    [AC-SCENE-SLOT-04] 场景槽位包指标收集器
    
    收集以下指标：
    - scene_slot_requests_total: 场景槽位请求总数
    - scene_slot_cache_hits: 缓存命中次数
    - scene_slot_cache_misses: 缓存未命中次数
    - scene_slot_missing_triggered: 缺失槽位触发次数
    - scene_slot_ask_back_triggered: 追问触发次数
    - scene_slot_not_configured: 场景未配置次数
    - scene_slot_not_found: 槽位未找到次数
    - scene_slot_completion_ratio: 槽位完成比例
    """
    
    def __init__(self, max_points: int = 10000):
        self._max_points = max_points
        self._points: list[SceneSlotMetricPoint] = []
        self._counters: dict[str, int] = defaultdict(int)
        self._lock = threading.Lock()
        self._start_time = datetime.utcnow()
    
    def record(
        self,
        tenant_id: str,
        scene_key: str,
        metric_name: str,
        value: float = 1.0,
        tags: dict[str, str] | None = None,
    ) -> None:
        """
        记录指标
        
        Args:
            tenant_id: 租户 ID
            scene_key: 场景标识
            metric_name: 指标名称
            value: 指标值
            tags: 额外标签
        """
        point = SceneSlotMetricPoint(
            timestamp=datetime.utcnow(),
            tenant_id=tenant_id,
            scene_key=scene_key,
            metric_name=metric_name,
            value=value,
            tags=tags or {},
        )
        
        with self._lock:
            self._points.append(point)
            if len(self._points) > self._max_points:
                self._points = self._points[-self._max_points:]
            
            counter_key = f"{tenant_id}:{scene_key}:{metric_name}"
            self._counters[counter_key] += 1
    
    def record_cache_hit(self, tenant_id: str, scene_key: str) -> None:
        """记录缓存命中"""
        self.record(tenant_id, scene_key, "cache_hit")
    
    def record_cache_miss(self, tenant_id: str, scene_key: str) -> None:
        """记录缓存未命中"""
        self.record(tenant_id, scene_key, "cache_miss")
    
    def record_missing_slots(self, tenant_id: str, scene_key: str, count: int = 1) -> None:
        """记录缺失槽位触发"""
        self.record(tenant_id, scene_key, "missing_slots_triggered", float(count))
    
    def record_ask_back(self, tenant_id: str, scene_key: str) -> None:
        """记录追问触发"""
        self.record(tenant_id, scene_key, "ask_back_triggered")
    
    def record_scene_not_configured(self, tenant_id: str, scene_key: str) -> None:
        """记录场景未配置"""
        self.record(tenant_id, scene_key, "scene_not_configured")
    
    def record_slot_not_found(self, tenant_id: str, scene_key: str, slot_key: str) -> None:
        """记录槽位未找到"""
        self.record(tenant_id, scene_key, "slot_not_found", tags={"slot_key": slot_key})
    
    def record_completion_ratio(self, tenant_id: str, scene_key: str, ratio: float) -> None:
        """记录槽位完成比例"""
        self.record(tenant_id, scene_key, "completion_ratio", ratio)
    
    def get_summary(self, tenant_id: str | None = None) -> SceneSlotMetricsSummary:
        """
        获取指标汇总
        
        Args:
            tenant_id: 租户 ID（可选，为 None 时返回所有租户的汇总）
            
        Returns:
            指标汇总
        """
        summary = SceneSlotMetricsSummary()
        
        with self._lock:
            points = self._points.copy()
        
        for point in points:
            if tenant_id and point.tenant_id != tenant_id:
                continue
            
            summary.total_requests += 1
            
            if point.metric_name == "cache_hit":
                summary.cache_hits += 1
            elif point.metric_name == "cache_miss":
                summary.cache_misses += 1
            elif point.metric_name == "missing_slots_triggered":
                summary.missing_slots_triggered += int(point.value)
            elif point.metric_name == "ask_back_triggered":
                summary.ask_back_triggered += 1
            elif point.metric_name == "scene_not_configured":
                summary.scene_not_configured += 1
            elif point.metric_name == "slot_not_found":
                summary.slot_not_found += 1
            elif point.metric_name == "completion_ratio":
                summary.avg_completion_ratio = (
                    summary.avg_completion_ratio * summary.total_requests + point.value
                ) / (summary.total_requests + 1) if summary.total_requests > 0 else point.value
        
        return summary
    
    def get_metrics_by_scene(
        self,
        tenant_id: str,
        scene_key: str,
    ) -> dict[str, Any]:
        """
        获取特定场景的指标
        
        Args:
            tenant_id: 租户 ID
            scene_key: 场景标识
            
        Returns:
            指标字典
        """
        metrics = {
            "requests": 0,
            "cache_hits": 0,
            "cache_misses": 0,
            "missing_slots_triggered": 0,
            "ask_back_triggered": 0,
            "slot_not_found": 0,
            "avg_completion_ratio": 0.0,
        }
        
        with self._lock:
            points = [p for p in self._points if p.tenant_id == tenant_id and p.scene_key == scene_key]
        
        if not points:
            return metrics
        
        completion_ratios = []
        
        for point in points:
            metrics["requests"] += 1
            if point.metric_name in metrics:
                if point.metric_name == "completion_ratio":
                    completion_ratios.append(point.value)
                else:
                    metrics[point.metric_name] += int(point.value)
        
        if completion_ratios:
            metrics["avg_completion_ratio"] = sum(completion_ratios) / len(completion_ratios)
        
        return metrics
    
    def check_alerts(
        self,
        tenant_id: str,
        scene_key: str,
        thresholds: dict[str, float] | None = None,
    ) -> list[dict[str, Any]]:
        """
        检查告警条件
        
        Args:
            tenant_id: 租户 ID
            scene_key: 场景标识
            thresholds: 告警阈值配置
            
        Returns:
            告警列表
        """
        default_thresholds = {
            "cache_hit_rate_low": 0.5,
            "missing_slots_rate_high": 0.3,
            "scene_not_configured_rate_high": 0.1,
        }
        
        effective_thresholds = {**default_thresholds, **(thresholds or {})}
        
        metrics = self.get_metrics_by_scene(tenant_id, scene_key)
        alerts = []
        
        if metrics["requests"] > 0:
            cache_hit_rate = metrics["cache_hits"] / metrics["requests"]
            if cache_hit_rate < effective_thresholds["cache_hit_rate_low"]:
                alerts.append({
                    "alert_type": "cache_hit_rate_low",
                    "severity": "warning",
                    "message": f"场景 {scene_key} 的缓存命中率 ({cache_hit_rate:.2%}) 低于阈值 ({effective_thresholds['cache_hit_rate_low']:.0%})",
                    "current_value": cache_hit_rate,
                    "threshold": effective_thresholds["cache_hit_rate_low"],
                    "suggestion": "检查场景槽位包配置是否频繁变更，或增加缓存 TTL",
                })
            
            missing_slots_rate = metrics["missing_slots_triggered"] / metrics["requests"]
            if missing_slots_rate > effective_thresholds["missing_slots_rate_high"]:
                alerts.append({
                    "alert_type": "missing_slots_rate_high",
                    "severity": "warning",
                    "message": f"场景 {scene_key} 的缺失槽位触发率 ({missing_slots_rate:.2%}) 高于阈值 ({effective_thresholds['missing_slots_rate_high']:.0%})",
                    "current_value": missing_slots_rate,
                    "threshold": effective_thresholds["missing_slots_rate_high"],
                    "suggestion": "检查槽位配置是否合理，或优化槽位提取策略",
                })
            
            scene_not_configured_rate = metrics.get("scene_not_configured", 0) / metrics["requests"]
            if scene_not_configured_rate > effective_thresholds["scene_not_configured_rate_high"]:
                alerts.append({
                    "alert_type": "scene_not_configured_rate_high",
                    "severity": "error",
                    "message": f"场景 {scene_key} 未配置率 ({scene_not_configured_rate:.2%}) 高于阈值 ({effective_thresholds['scene_not_configured_rate_high']:.0%})",
                    "current_value": scene_not_configured_rate,
                    "threshold": effective_thresholds["scene_not_configured_rate_high"],
                    "suggestion": "请为该场景创建场景槽位包配置",
                })
        
        return alerts
    
    def export_metrics(self, tenant_id: str | None = None) -> dict[str, Any]:
        """
        导出指标数据
        
        Args:
            tenant_id: 租户 ID（可选）
            
        Returns:
            指标数据字典
        """
        with self._lock:
            points = [
                {
                    "timestamp": p.timestamp.isoformat(),
                    "tenant_id": p.tenant_id,
                    "scene_key": p.scene_key,
                    "metric_name": p.metric_name,
                    "value": p.value,
                    "tags": p.tags,
                }
                for p in self._points
                if tenant_id is None or p.tenant_id == tenant_id
            ]
        
        return {
            "start_time": self._start_time.isoformat(),
            "end_time": datetime.utcnow().isoformat(),
            "total_points": len(points),
            "points": points,
            "summary": self.get_summary(tenant_id).__dict__,
        }
    
    def reset(self) -> None:
        """重置指标收集器"""
        with self._lock:
            self._points = []
            self._counters = defaultdict(int)
            self._start_time = datetime.utcnow()


_metrics_collector: SceneSlotMetricsCollector | None = None


def get_scene_slot_metrics_collector() -> SceneSlotMetricsCollector:
    """获取场景槽位指标收集器实例"""
    global _metrics_collector
    if _metrics_collector is None:
        _metrics_collector = SceneSlotMetricsCollector()
    return _metrics_collector
