"""
CogProfile Engine —— 认知身份证引擎

核心入口。输入对话记忆，输出持续更新的认知身份证。

用法:
    from cogprofile import CogProfileEngine

    engine = CogProfileEngine(llm_api_key="sk-xxx")

    # 输入记忆，更新身份证
    profile = await engine.update(user_id="user_001", memories=[...])

    # 读取最新身份证
    profile = await engine.get_profile(user_id="user_001")
"""

from __future__ import annotations

from datetime import datetime, timedelta
from typing import Optional

from cogprofile.detectors import get_all_detectors
from cogprofile.llm import LLMClient, LLMConfig
from cogprofile.models import (
    CogProfile,
    EmotionSection,
    EmotionState,
    EmotionTrend,
    AttentionSection,
    HotTopic,
    FadingTopic,
    GoalsSection,
    Goal,
    ProfileMeta,
    Memory,
)
from cogprofile.storage import InMemoryStore, MemoryStore


class CogProfileEngine:
    """
    认知身份证引擎。

    职责：
    1. 接收对话记忆
    2. 运行内部洞察检测器（用户不可见）
    3. 将检测结果合并到用户的 CogProfile 中
    4. 返回更新后的 CogProfile
    """

    def __init__(
        self,
        llm_api_key: str = "",
        llm_provider: str = "deepseek",
        llm_model: Optional[str] = None,
        store: Optional[MemoryStore] = None,
    ):
        self.llm_config = LLMConfig(
            provider=llm_provider,
            api_key=llm_api_key,
            model=llm_model,
        )
        self.llm = LLMClient(self.llm_config)
        self.store = store or InMemoryStore()

        # 初始化检测器（内部使用，不对外暴露）
        self._detectors = {
            t: cls(llm=self.llm) for t, cls in get_all_detectors().items()
        }

        # 用户认知身份证缓存
        self._profiles: dict[str, CogProfile] = {}

    async def update(
        self,
        user_id: str,
        memories: Optional[list[Memory]] = None,
        days: int = 30,
    ) -> CogProfile:
        """
        核心方法：输入新记忆，更新认知身份证。

        参数:
            user_id: 用户ID
            memories: 新的记忆列表（如果不传，从 store 里读取）
            days: 分析的时间窗口

        返回:
            更新后的 CogProfile
        """
        # 如果传入了新记忆，先存到 store 里
        if memories and isinstance(self.store, InMemoryStore):
            for m in memories:
                self.store.add(m)

        # 从 store 读取全部记忆
        since = datetime.now() - timedelta(days=days)
        all_memories = await self.store.get_memories(user_id=user_id, since=since)

        if not all_memories:
            return self._get_or_create_profile(user_id)

        # 获取或创建 profile
        profile = self._get_or_create_profile(user_id)

        # 运行所有检测器
        for insight_type, detector in self._detectors.items():
            try:
                insights = await detector.detect(
                    memories=all_memories,
                    user_id=user_id,
                )
                # 将洞察结果合并到 profile 对应的字段里
                self._merge_insights(profile, insight_type, insights)
            except Exception as e:
                print(f"[cogprofile] {insight_type.value} 检测失败: {e}")
                continue

        # 更新元数据
        profile.update_count += 1
        profile.updated_at = datetime.now().isoformat()
        profile.meta.total_memories_processed = len(all_memories)
        profile.meta.llm_provider = self.llm_config.provider
        profile.meta.llm_model = self.llm_config.resolved_model

        if all_memories:
            profile.meta.earliest_memory = all_memories[0].created_at.strftime("%Y-%m-%d")
            profile.meta.latest_memory = all_memories[-1].created_at.strftime("%Y-%m-%d")

        # 缓存
        self._profiles[user_id] = profile
        return profile

    async def get_profile(self, user_id: str) -> Optional[CogProfile]:
        """读取用户最新的认知身份证"""
        return self._profiles.get(user_id)

    async def list_users(self) -> list[str]:
        """列出所有有 profile 的用户"""
        return list(self._profiles.keys())

    async def delete_profile(self, user_id: str) -> bool:
        """删除用户的认知身份证"""
        if user_id in self._profiles:
            del self._profiles[user_id]
            return True
        return False

    async def export_profile(self, user_id: str) -> Optional[dict]:
        """导出用户身份证为 JSON（用户可带走自己的数据）"""
        profile = self._profiles.get(user_id)
        if profile:
            return profile.model_dump(mode="json")
        return None

    # ========== 内部方法 ==========

    def _get_or_create_profile(self, user_id: str) -> CogProfile:
        if user_id not in self._profiles:
            self._profiles[user_id] = CogProfile(user_id=user_id)
        return self._profiles[user_id]

    def _merge_insights(self, profile: CogProfile, insight_type, insights: list):
        """将检测器输出的洞察合并到 profile 对应的字段"""
        from cogprofile.models import InsightType

        for insight in insights:
            if insight_type == InsightType.EMOTION_TREND:
                profile.emotion.current = EmotionState(
                    valence=insight.end_score,
                    label=self._valence_to_label(insight.end_score),
                    confidence=insight.confidence,
                    assessed_at=datetime.now().strftime("%Y-%m-%d"),
                )
                profile.emotion.trend = EmotionTrend(
                    direction=insight.direction,
                    slope=round(insight.delta / max(insight.period_days, 1), 4),
                    period_days=insight.period_days,
                    start_valence=insight.start_score,
                    end_valence=insight.end_score,
                    confidence=insight.confidence,
                )

            elif insight_type == InsightType.TOPIC_ANOMALY:
                profile.attention.hot_topics = [
                    HotTopic(
                        topic=ins.topic,
                        frequency_7d=ins.frequency_recent,
                        frequency_30d=ins.frequency_baseline,
                        trend="intensifying" if ins.spike_ratio > 2 else "stable",
                        spike_ratio=ins.spike_ratio,
                    )
                    for ins in insights
                ]

            elif insight_type == InsightType.GOAL_DRIFT:
                active = []
                fading = []
                abandoned = []
                for ins in insights:
                    goal = Goal(
                        description=ins.goal,
                        first_mentioned=ins.first_mentioned.strftime("%Y-%m-%d"),
                        last_mentioned=ins.last_mentioned.strftime("%Y-%m-%d"),
                        mention_count=ins.mention_count,
                        days_silent=ins.days_silent,
                        commitment_strength=max(0.0, 1.0 - ins.days_silent / 60),
                        status=ins.status,
                    )
                    if ins.status == "active":
                        active.append(goal)
                    elif ins.status == "fading":
                        fading.append(goal)
                    else:
                        abandoned.append(goal)
                if active:
                    profile.goals.active = active
                if fading:
                    profile.goals.fading = fading
                if abandoned:
                    profile.goals.abandoned = abandoned

    @staticmethod
    def _valence_to_label(valence: float) -> str:
        if valence >= 0.6:
            return "positive"
        elif valence >= 0.2:
            return "slightly positive"
        elif valence >= -0.2:
            return "neutral"
        elif valence >= -0.6:
            return "stressed"
        else:
            return "very negative"
