"""维度2：话题异常检测"""

from __future__ import annotations

import json
from collections import Counter
from datetime import datetime, timedelta

from cogprofile.detectors.base import BaseDetector, register_detector
from cogprofile.models import InsightType, Memory, TopicAnomaly

TOPIC_EXTRACT_PROMPT = """你是topic analysis专家。从以下对话记忆中提取核心topic tags。

规则：
- 每条记忆提取1-3个topic tags
- 标签用简短的中文词语，如"工作压力""考研""健身"
- 返回JSON数组，格式：[{"index": 0, "topics": ["工作压力", "加班"]}]
- 只返回JSON，不要其他内容。"""


@register_detector(InsightType.TOPIC_ANOMALY)
class TopicAnomalyDetector(BaseDetector):
    """
    话题异常检测器。

    工作流程：
    1. 提取每条记忆的topic tags
    2. 分别统计近7天和近30天的话题频率
    3. 计算异常比率（近7天频率 / 30天均值）
    4. 超过阈值的话题生成洞察
    """

    async def detect(
        self,
        memories: list[Memory],
        user_id: str,
        recent_days: int = 7,
        baseline_days: int = 30,
        spike_threshold: float = 2.0,
        min_recent_count: int = 3,
        **kwargs,
    ) -> list[TopicAnomaly]:

        if len(memories) < 5:
            return []

        # 第1步：确保每条记忆都有topic tags
        await self._fill_topics(memories)

        now = datetime.now()
        recent_cutoff = now - timedelta(days=recent_days)
        baseline_cutoff = now - timedelta(days=baseline_days)

        recent = [m for m in memories if m.created_at >= recent_cutoff]
        baseline = [m for m in memories if m.created_at >= baseline_cutoff]

        if not recent or not baseline:
            return []

        # 第2步：统计频率
        recent_topics = Counter()
        baseline_topics = Counter()

        for m in recent:
            for t in m.topics:
                recent_topics[t] += 1

        for m in baseline:
            for t in m.topics:
                baseline_topics[t] += 1

        # 第3步：找异常话题
        insights = []
        baseline_weeks = max(baseline_days / 7, 1)

        for topic, recent_count in recent_topics.items():
            if recent_count < min_recent_count:
                continue

            baseline_count = baseline_topics.get(topic, 0)
            # 基线：30天内的周均值
            baseline_weekly_avg = baseline_count / baseline_weeks

            if baseline_weekly_avg == 0:
                spike_ratio = float(recent_count)
            else:
                spike_ratio = recent_count / baseline_weekly_avg

            if spike_ratio >= spike_threshold:
                summary = (
                    f'"{topic}"这个话题最近{recent_days}天出现了{recent_count}次，'
                    f"而过去{baseline_days}天总共才{baseline_count}次。"
                    f"这个话题正在急剧升温。"
                )

                insights.append(TopicAnomaly(
                    confidence=min(0.95, 0.5 + spike_ratio * 0.1),
                    summary=summary,
                    evidence=[
                        f"{m.created_at.strftime('%m/%d')}: {m.content[:50]}"
                        for m in recent if topic in m.topics
                    ][:5],
                    topic=topic,
                    frequency_recent=recent_count,
                    frequency_baseline=baseline_count,
                    spike_ratio=round(spike_ratio, 2),
                ))

        # 按异常程度排序，最多返回3条
        insights.sort(key=lambda i: i.spike_ratio, reverse=True)
        return insights[:3]

    async def _fill_topics(self, memories: list[Memory]):
        """为没有topic tags的记忆批量提取话题"""
        untagged = [m for m in memories if not m.topics]
        if not untagged:
            return

        for i in range(0, len(untagged), 20):
            batch = untagged[i:i+20]
            prompt = "\n".join(
                f"[{j}] {m.content[:200]}" for j, m in enumerate(batch)
            )
            try:
                result = await self.llm.chat_json(
                    system_prompt=TOPIC_EXTRACT_PROMPT,
                    user_prompt=prompt,
                )
                items = json.loads(result)
                for item in items:
                    idx = item.get("index", -1)
                    if 0 <= idx < len(batch):
                        batch[idx].topics = item.get("topics", [])
            except (json.JSONDecodeError, KeyError, ValueError):
                continue
