"""维度3：目标漂移检测"""

from __future__ import annotations

import json
from datetime import datetime, timedelta

from cogprofile.detectors.base import BaseDetector, register_detector
from cogprofile.models import DetectedGoalDrift as GoalDrift, InsightType, Memory, MemoryCategory

GOAL_EXTRACT_PROMPT = """你是goal analysis专家。从以下对话记忆中识别用户提到的目标、计划或愿望。

规则：
- 只提取明确的目标/计划（如"想学日语""打算减肥""准备考研"）
- 不要提取一般性陈述
- 返回JSON数组：[{"index": 0, "goals": ["学日语", "考研"]}]
- 如果没有目标相关内容，返回空goals: []
- 只返回JSON。"""


@register_detector(InsightType.GOAL_DRIFT)
class GoalDriftDetector(BaseDetector):
    """
    目标漂移检测器。

    工作流程：
    1. 从记忆中提取所有提及的目标
    2. 按目标聚合，统计首次/末次提及时间、提及次数
    3. 计算沉默天数
    4. 判断目标状态：活跃 / 正在淡化 / 可能已放弃
    """

    async def detect(
        self,
        memories: list[Memory],
        user_id: str,
        silence_warning_days: int = 14,
        silence_abandon_days: int = 30,
        min_mentions: int = 2,
        **kwargs,
    ) -> list[GoalDrift]:

        if len(memories) < 3:
            return []

        # 第1步：提取目标
        goal_mentions = await self._extract_goals(memories)

        if not goal_mentions:
            return []

        # 第2步：按目标聚合
        now = datetime.now()
        goal_stats: dict[str, dict] = {}

        for goal, memory in goal_mentions:
            goal_lower = goal.strip().lower()
            if goal_lower not in goal_stats:
                goal_stats[goal_lower] = {
                    "goal": goal.strip(),
                    "first": memory.created_at,
                    "last": memory.created_at,
                    "count": 0,
                    "memories": [],
                }
            stats = goal_stats[goal_lower]
            stats["count"] += 1
            stats["memories"].append(memory)
            if memory.created_at < stats["first"]:
                stats["first"] = memory.created_at
            if memory.created_at > stats["last"]:
                stats["last"] = memory.created_at

        # 第3步：生成洞察
        insights = []
        for goal_key, stats in goal_stats.items():
            if stats["count"] < min_mentions:
                continue

            days_silent = (now - stats["last"]).days

            if days_silent < silence_warning_days:
                status = "active"
                continue  # 活跃的目标不需要提醒
            elif days_silent < silence_abandon_days:
                status = "fading"
            else:
                status = "likely_abandoned"

            summary = (
                f"你从{stats['first'].strftime('%m月%d日')}开始提到"
                f'"{stats["goal"]}"，'
                f"一共提了{stats['count']}次，"
                f"但已经{days_silent}天没再提过了。"
            )
            if status == "fading":
                summary += "这个目标似乎正在淡化。"
            else:
                summary += "这个目标可能已经被搁置了。"

            insights.append(GoalDrift(
                confidence=min(0.9, 0.4 + days_silent * 0.01),
                summary=summary,
                evidence=[
                    f"{m.created_at.strftime('%m/%d')}: {m.content[:50]}"
                    for m in stats["memories"][-3:]
                ],
                goal=stats["goal"],
                first_mentioned=stats["first"],
                last_mentioned=stats["last"],
                mention_count=stats["count"],
                days_silent=days_silent,
                status=status,
            ))

        insights.sort(key=lambda i: i.days_silent, reverse=True)
        return insights[:5]

    async def _extract_goals(
        self, memories: list[Memory]
    ) -> list[tuple[str, Memory]]:
        """从记忆中提取目标，返回 (目标名, 来源记忆) 对"""
        results = []

        # 优先使用已分类为"目标"的记忆
        goal_memories = [m for m in memories if m.category == MemoryCategory.GOAL]
        for m in goal_memories:
            results.append((m.content[:30], m))

        # 对未分类的记忆用LLM提取
        other_memories = [m for m in memories if m.category != MemoryCategory.GOAL]
        for i in range(0, len(other_memories), 20):
            batch = other_memories[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=GOAL_EXTRACT_PROMPT,
                    user_prompt=prompt,
                )
                items = json.loads(result)
                for item in items:
                    idx = item.get("index", -1)
                    if 0 <= idx < len(batch):
                        for goal in item.get("goals", []):
                            results.append((goal, batch[idx]))
            except (json.JSONDecodeError, KeyError, ValueError):
                continue

        return results
