"""
记忆系统管理器

Memory Manager - 负责管理和维护项目的记忆系统，确保多剧集一致性
"""
import json
import uuid
from typing import List, Dict, Any, Optional
from datetime import datetime

from app.models.memory import (
    TimelineEvent,
    PendingThread,
    CharacterStateChange,
    ForeshadowingEvent,
    ConsistencyIssue,
    ResolutionSuggestion,
    EnhancedMemory,
    ImportanceLevel,
    ThreadStatus,
    MemoryExtractionResult
)
from app.models.project import SeriesProject, Episode, Memory
from app.core.llm.glm_client import get_glm_client
from app.utils.logger import get_logger

logger = get_logger(__name__)


class MemoryManager:
    """
    记忆系统管理器

    核心功能：
    1. 从剧集中提取关键事件
    2. 检测伏笔和待收线
    3. 追踪角色状态变化
    4. 检查一致性
    5. 提供收线建议
    """

    def __init__(self):
        """初始化记忆管理器"""
        self.glm_client = get_glm_client()
        logger.info("记忆管理器初始化完成")

    async def extract_events_from_episode(
        self,
        episode_content: str,
        episode_number: int,
        characters: List[str]
    ) -> List[TimelineEvent]:
        """
        从完成的剧集中提取关键事件

        使用 LLM 分析剧情，识别并提取关键事件

        Args:
            episode_content: 剧集内容
            episode_number: 集数
            characters: 角色列表

        Returns:
            提取的事件列表
        """
        try:
            logger.info(f"开始从 EP{episode_number} 提取事件")

            # 构建提取提示词
            prompt = self._build_event_extraction_prompt(
                episode_content,
                episode_number,
                characters
            )

            # 调用 LLM
            response = await self.glm_client.chat(
                messages=[
                    {
                        "role": "system",
                        "content": "你是专业的剧情分析专家，擅长从剧集中提取关键事件。"
                    },
                    {"role": "user", "content": prompt}
                ],
                temperature=0.3
            )

            content = response["choices"][0]["message"]["content"]

            # 解析响应
            events = self._parse_events_response(content, episode_number)

            logger.info(f"成功从 EP{episode_number} 提取 {len(events)} 个事件")
            return events

        except Exception as e:
            logger.error(f"提取事件失败: {str(e)}")
            return []

    async def detect_foreshadowing(
        self,
        episode_content: str,
        episode_number: int,
        existing_threads: List[PendingThread]
    ) -> tuple[List[ForeshadowingEvent], List[PendingThread]]:
        """
        检测剧集中的伏笔和新待收线

        Args:
            episode_content: 剧集内容
            episode_number: 集数
            existing_threads: 现有待收线列表

        Returns:
            (新检测到的伏笔, 新识别的待收线)
        """
        try:
            logger.info(f"开始检测 EP{episode_number} 的伏笔")

            # 构建检测提示词
            prompt = self._build_foreshadowing_detection_prompt(
                episode_content,
                episode_number,
                existing_threads
            )

            # 调用 LLM
            response = await self.glm_client.chat(
                messages=[
                    {
                        "role": "system",
                        "content": "你是专业的剧情分析专家，擅长识别伏笔和待收线。"
                    },
                    {"role": "user", "content": prompt}
                ],
                temperature=0.4
            )

            content = response["choices"][0]["message"]["content"]

            # 解析响应
            foreshadowing, new_threads = self._parse_foreshadowing_response(
                content,
                episode_number
            )

            logger.info(
                f"检测到 {len(foreshadowing)} 个伏笔，"
                f"{len(new_threads)} 个新待收线"
            )
            return foreshadowing, new_threads

        except Exception as e:
            logger.error(f"检测伏笔失败: {str(e)}")
            return [], []

    async def update_character_states(
        self,
        episode_content: str,
        episode_number: int,
        characters: List[str],
        current_states: Dict[str, List[CharacterStateChange]]
    ) -> Dict[str, CharacterStateChange]:
        """
        追踪角色状态变化

        Args:
            episode_content: 剧集内容
            episode_number: 集数
            characters: 角色列表
            current_states: 当前角色状态

        Returns:
            角色状态变化字典
        """
        try:
            logger.info(f"开始更新 EP{episode_number} 的角色状态")

            # 构建状态分析提示词
            prompt = self._build_character_state_prompt(
                episode_content,
                episode_number,
                characters,
                current_states
            )

            # 调用 LLM
            response = await self.glm_client.chat(
                messages=[
                    {
                        "role": "system",
                        "content": "你是专业的角色分析专家，擅长追踪角色发展和状态变化。"
                    },
                    {"role": "user", "content": prompt}
                ],
                temperature=0.3
            )

            content = response["choices"][0]["message"]["content"]

            # 解析响应
            state_changes = self._parse_character_states_response(
                content,
                episode_number
            )

            logger.info(f"更新了 {len(state_changes)} 个角色的状态")
            return state_changes

        except Exception as e:
            logger.error(f"更新角色状态失败: {str(e)}")
            return {}

    async def check_consistency(
        self,
        episode_content: str,
        episode_number: int,
        memory: EnhancedMemory
    ) -> List[ConsistencyIssue]:
        """
        检查剧集与记忆的一致性

        Args:
            episode_content: 剧集内容
            episode_number: 集数
            memory: 项目记忆

        Returns:
            一致性问题列表
        """
        try:
            logger.info(f"开始检查 EP{episode_number} 的一致性")

            # 构建一致性检查提示词
            prompt = self._build_consistency_check_prompt(
                episode_content,
                episode_number,
                memory
            )

            # 调用 LLM
            response = await self.glm_client.chat(
                messages=[
                    {
                        "role": "system",
                        "content": "你是专业的剧情一致性检查专家，擅长发现剧情矛盾。"
                    },
                    {"role": "user", "content": prompt}
                ],
                temperature=0.2
            )

            content = response["choices"][0]["message"]["content"]

            # 解析响应
            issues = self._parse_consistency_response(content)

            logger.info(f"发现 {len(issues)} 个一致性问题")
            return issues

        except Exception as e:
            logger.error(f"一致性检查失败: {str(e)}")
            return []

    async def suggest_thread_resolution(
        self,
        thread: PendingThread,
        current_episode: int,
        memory: EnhancedMemory
    ) -> ResolutionSuggestion:
        """
        为待收线问题提供收线建议

        Args:
            thread: 待收线问题
            current_episode: 当前集数
            memory: 项目记忆

        Returns:
            收线建议
        """
        try:
            logger.info(f"为待收线 {thread.id} 生成收线建议")

            # 构建收线建议提示词
            prompt = self._build_resolution_prompt(
                thread,
                current_episode,
                memory
            )

            # 调用 LLM
            response = await self.glm_client.chat(
                messages=[
                    {
                        "role": "system",
                        "content": "你是专业的剧情创作专家，擅长设计精彩的收线。"
                    },
                    {"role": "user", "content": prompt}
                ],
                temperature=0.7
            )

            content = response["choices"][0]["message"]["content"]

            # 解析响应
            suggestion = self._parse_resolution_response(content, thread.id)

            logger.info(f"成功生成待收线 {thread.id} 的建议")
            return suggestion

        except Exception as e:
            logger.error(f"生成收线建议失败: {str(e)}")
            return ResolutionSuggestion(
                thread_id=thread.id,
                suggested_episode=current_episode + 1,
                approach="无法生成建议",
                key_elements=[],
                character_involvement={},
                potential_twists=[],
                estimated_impact=""
            )

    async def update_memory_from_episode(
        self,
        project: SeriesProject,
        episode: Episode
    ) -> MemoryExtractionResult:
        """
        从剧集更新项目记忆（综合操作）

        Args:
            project: 项目
            episode: 剧集

        Returns:
            更新结果
        """
        try:
            logger.info(f"开始从 EP{episode.number} 更新项目 {project.id} 的记忆")

            if not episode.content:
                return MemoryExtractionResult(
                    success=False,
                    message="剧集内容为空"
                )

            # 获取角色列表
            characters = list(project.globalContext.characterProfiles.keys())

            # 转换现有记忆为 EnhancedMemory
            enhanced_memory = self._convert_to_enhanced_memory(project.memory)

            # 1. 提取事件
            events = await self.extract_events_from_episode(
                episode.content,
                episode.number,
                characters
            )

            # 2. 检测伏笔和待收线
            existing_threads = enhanced_memory.pendingThreads
            foreshadowing, new_threads = await self.detect_foreshadowing(
                episode.content,
                episode.number,
                existing_threads
            )

            # 3. 更新角色状态
            state_changes = await self.update_character_states(
                episode.content,
                episode.number,
                characters,
                enhanced_memory.characterStates
            )

            # 4. 检查一致性
            issues = await self.check_consistency(
                episode.content,
                episode.number,
                enhanced_memory
            )

            # 5. 更新记忆
            enhanced_memory.eventTimeline.extend(events)
            enhanced_memory.foreshadowing.extend(foreshadowing)
            enhanced_memory.pendingThreads.extend(new_threads)

            # 更新角色状态
            for char_name, change in state_changes.items():
                if char_name not in enhanced_memory.characterStates:
                    enhanced_memory.characterStates[char_name] = []
                enhanced_memory.characterStates[char_name].append(change)

            # 添加一致性问题
            enhanced_memory.consistencyIssues.extend(issues)

            # 更新元数据
            enhanced_memory.last_updated = datetime.now()
            enhanced_memory.last_episode_processed = episode.number

            # 转换回 Memory 并保存
            project.memory = self._convert_to_memory(enhanced_memory)

            logger.info(
                f"记忆更新完成: {len(events)} 事件, "
                f"{len(foreshadowing)} 伏笔, {len(new_threads)} 待收线, "
                f"{len(state_changes)} 角色状态, {len(issues)} 问题"
            )

            return MemoryExtractionResult(
                success=True,
                events_extracted=len(events),
                foreshadowing_detected=len(foreshadowing),
                character_states_updated=len(state_changes),
                consistency_issues_found=len(issues),
                threads_suggested=len(new_threads),
                new_threads=new_threads,
                message="记忆更新成功"
            )

        except Exception as e:
            logger.error(f"更新记忆失败: {str(e)}")
            return MemoryExtractionResult(
                success=False,
                message=f"更新失败: {str(e)}"
            )

    # ============================================
    # 提示词构建方法
    # ============================================

    def _build_event_extraction_prompt(
        self,
        content: str,
        episode_number: int,
        characters: List[str]
    ) -> str:
        """构建事件提取提示词"""
        return f"""请分析以下第 {episode_number} 集的剧情内容，提取关键事件。

【角色列表】
{', '.join(characters) if characters else '无'}

【剧集内容】
{content[:5000]}  # 限制长度以避免超出 token 限制

【任务要求】
1. 识别剧情中的关键事件（推动剧情发展、改变角色关系、揭示重要信息等）
2. 对每个事件，提供：
   - 事件描述（简明扼要）
   - 涉及的角色
   - 重要程度（low/medium/high）
   - 标签（如：冲突/转折/揭示/情感等）

请以 JSON 格式返回，格式如下：
{{
    "events": [
        {{
            "event": "事件描述",
            "characters_involved": ["角色1", "角色2"],
            "importance": "high",
            "tags": ["转折", "冲突"]
        }}
    ]
}}

只返回 JSON，不要其他内容。"""

    def _build_foreshadowing_detection_prompt(
        self,
        content: str,
        episode_number: int,
        existing_threads: List[PendingThread]
    ) -> str:
        """构建伏笔检测提示词"""
        threads_info = "\n".join([
            f"- {t.id}: {t.description} (重要性: {t.importance})"
            for t in existing_threads[:10]  # 限制数量
        ])

        return f"""请分析以下剧情，识别伏笔和新的待收线问题。

【当前集数】
第 {episode_number} 集

【现有待收线】
{threads_info if threads_info else '无'}

【剧集内容】
{content[:5000]}

【任务要求】
1. 识别新的伏笔（后续会呼应的线索）
2. 识别新的待收线问题（需要后续解决）
3. 检查现有待收线是否有进展

请以 JSON 格式返回：
{{
    "foreshadowing": [
        {{
            "description": "伏笔描述",
            "type": "plot/character/dialogue/object",
            "importance": "high/medium/low"
        }}
    ],
    "new_threads": [
        {{
            "description": "待收线描述",
            "importance": "high/medium/low",
            "characters_involved": ["角色名"],
            "reminder_episode": 建议收线集数
        }}
    ]
}}

只返回 JSON，不要其他内容。"""

    def _build_character_state_prompt(
        self,
        content: str,
        episode_number: int,
        characters: List[str],
        current_states: Dict[str, List[CharacterStateChange]]
    ) -> str:
        """构建角色状态分析提示词"""
        states_info = ""
        for char, states in list(current_states.items())[:5]:
            latest = states[-1] if states else None
            states_info += f"\n{char}: {latest.state if latest else '未知状态'}\n"

        return f"""请分析以下剧情，追踪角色的状态变化。

【当前集数】
第 {episode_number} 集

【角色当前状态】
{states_info if states_info else '无记录'}

【剧集内容】
{content[:5000]}

【任务要求】
1. 识别哪些角色发生了状态变化（心理、处境、关系等）
2. 描述变化的具体内容
3. 评估情感影响

请以 JSON 格式返回：
{{
    "state_changes": [
        {{
            "character": "角色名",
            "state": "新的状态描述",
            "change": "变化过程描述",
            "emotional_impact": "情感影响（如：痛苦/喜悦/恐惧等）",
            "trigger_event": "触发变化的事件"
        }}
    ]
}}

只返回 JSON，不要其他内容。"""

    def _build_consistency_check_prompt(
        self,
        content: str,
        episode_number: int,
        memory: EnhancedMemory
    ) -> str:
        """构建一致性检查提示词"""
        # 构建关键信息摘要
        timeline_summary = "\n".join([
            f"EP{e.episode}: {e.event}"
            for e in memory.eventTimeline[-20:]  # 最近20个事件
        ])

        threads_summary = "\n".join([
            f"- {t.description} (EP{t.introduced_at})"
            for t in memory.pendingThreads if not t.resolved
        ])

        return f"""请检查以下剧集与之前剧情的一致性。

【当前集数】
第 {episode_number} 集

【之前的关键事件】
{timeline_summary if timeline_summary else '无'}

【未收线的待收线】
{threads_summary if threads_summary else '无'}

【当前剧集内容】
{content[:5000]}

【任务要求】
检查以下类型的一致性问题：
1. 角色行为不一致（与之前性格或状态不符）
2. 剧情矛盾（与已建立的事实冲突）
3. 设定错误（场景、时间、物品等）
4. 对话不合理（角色语气、知识范围等）

请以 JSON 格式返回发现的问题：
{{
    "issues": [
        {{
            "type": "character/plot/setting/dialogue",
            "severity": "high/medium/low",
            "description": "问题描述",
            "location": "问题位置（如：第X幕）",
            "conflicting_info": {{"之前": "...", "现在": "..."}},
            "suggestion": "修复建议"
        }}
    ]
}}

如果没有发现问题，返回 {{"issues": []}}。
只返回 JSON，不要其他内容。"""

    def _build_resolution_prompt(
        self,
        thread: PendingThread,
        current_episode: int,
        memory: EnhancedMemory
    ) -> str:
        """构建收线建议提示词"""
        # 获取相关上下文
        related_events = [
            e for e in memory.eventTimeline
            if thread.id in e.related_threads
        ]

        context = f"""
【待收线信息】
描述: {thread.description}
引入集数: EP{thread.introduced_at}
重要程度: {thread.importance}
涉及角色: {', '.join(thread.characters_involved)}

【相关事件】
{chr(10).join([f"EP{e.episode}: {e.event}" for e in related_events[-5:]])}

【当前进度】
第 {current_episode} 集
"""

        return f"""基于以上信息，请为这个待收线问题设计一个精彩的收线方案。

{context}

【任务要求】
1. 建议在哪个集数收线（考虑剧情节奏和重要性）
2. 设计收线方式（直接/反转/渐进等）
3. 列出关键要素
4. 说明各角色的参与方式
5. 提供可能的反转点子
6. 评估对整体剧情的影响

请以 JSON 格式返回：
{{
    "suggested_episode": 收线集数,
    "approach": "收线方式描述",
    "key_elements": ["要素1", "要素2"],
    "character_involvement": {{"角色名": "参与方式"}},
    "potential_twists": ["反转点子1", "反转点子2"],
    "estimated_impact": "对整体剧情的影响"
}}

只返回 JSON，不要其他内容。"""

    # ============================================
    # 响应解析方法
    # ============================================

    def _parse_events_response(
        self,
        content: str,
        episode_number: int
    ) -> List[TimelineEvent]:
        """解析事件提取响应"""
        try:
            # 尝试提取 JSON
            json_start = content.find('{')
            json_end = content.rfind('}') + 1

            if json_start == -1 or json_end == 0:
                logger.warning("无法在响应中找到 JSON")
                return []

            json_str = content[json_start:json_end]
            data = json.loads(json_str)

            events = []
            for item in data.get("events", []):
                event = TimelineEvent(
                    episode=episode_number,
                    event=item.get("event", ""),
                    characters_involved=item.get("characters_involved", []),
                    importance=ImportanceLevel(item.get("importance", "medium")),
                    tags=item.get("tags", [])
                )
                events.append(event)

            return events

        except json.JSONDecodeError as e:
            logger.error(f"JSON 解析失败: {str(e)}")
            return []
        except Exception as e:
            logger.error(f"解析事件响应失败: {str(e)}")
            return []

    def _parse_foreshadowing_response(
        self,
        content: str,
        episode_number: int
    ) -> tuple[List[ForeshadowingEvent], List[PendingThread]]:
        """解析伏笔检测响应"""
        try:
            json_start = content.find('{')
            json_end = content.rfind('}') + 1

            if json_start == -1 or json_end == 0:
                return [], []

            json_str = content[json_start:json_end]
            data = json.loads(json_str)

            # 解析伏笔
            foreshadowing = []
            for item in data.get("foreshadowing", []):
                fs = ForeshadowingEvent(
                    id=str(uuid.uuid4()),
                    description=item.get("description", ""),
                    introduced_at=episode_number,
                    type=item.get("type", "plot"),
                    importance=ImportanceLevel(item.get("importance", "medium"))
                )
                foreshadowing.append(fs)

            # 解析待收线
            threads = []
            for item in data.get("new_threads", []):
                thread = PendingThread(
                    id=str(uuid.uuid4()),
                    description=item.get("description", ""),
                    introduced_at=episode_number,
                    importance=ImportanceLevel(item.get("importance", "medium")),
                    characters_involved=item.get("characters_involved", []),
                    reminder_episode=item.get("reminder_episode")
                )
                threads.append(thread)

            return foreshadowing, threads

        except Exception as e:
            logger.error(f"解析伏笔响应失败: {str(e)}")
            return [], []

    def _parse_character_states_response(
        self,
        content: str,
        episode_number: int
    ) -> Dict[str, CharacterStateChange]:
        """解析角色状态响应"""
        try:
            json_start = content.find('{')
            json_end = content.rfind('}') + 1

            if json_start == -1 or json_end == 0:
                return {}

            json_str = content[json_start:json_end]
            data = json.loads(json_str)

            states = {}
            for item in data.get("state_changes", []):
                char_name = item.get("character", "")
                if char_name:
                    change = CharacterStateChange(
                        episode=episode_number,
                        state=item.get("state", ""),
                        change=item.get("change", ""),
                        emotional_impact=item.get("emotional_impact"),
                        trigger_event=item.get("trigger_event")
                    )
                    states[char_name] = change

            return states

        except Exception as e:
            logger.error(f"解析角色状态响应失败: {str(e)}")
            return {}

    def _parse_consistency_response(
        self,
        content: str
    ) -> List[ConsistencyIssue]:
        """解析一致性检查响应"""
        try:
            json_start = content.find('{')
            json_end = content.rfind('}') + 1

            if json_start == -1 or json_end == 0:
                return []

            json_str = content[json_start:json_end]
            data = json.loads(json_str)

            issues = []
            for item in data.get("issues", []):
                issue = ConsistencyIssue(
                    id=str(uuid.uuid4()),
                    type=item.get("type", "plot"),
                    severity=ImportanceLevel(item.get("severity", "medium")),
                    description=item.get("description", ""),
                    location=item.get("location"),
                    conflicting_info=item.get("conflicting_info", {}),
                    suggestion=item.get("suggestion", "")
                )
                issues.append(issue)

            return issues

        except Exception as e:
            logger.error(f"解析一致性响应失败: {str(e)}")
            return []

    def _parse_resolution_response(
        self,
        content: str,
        thread_id: str
    ) -> ResolutionSuggestion:
        """解析收线建议响应"""
        try:
            json_start = content.find('{')
            json_end = content.rfind('}') + 1

            if json_start == -1 or json_end == 0:
                # 返回默认建议
                return ResolutionSuggestion(
                    thread_id=thread_id,
                    suggested_episode=0,
                    approach="无法解析建议",
                    key_elements=[],
                    character_involvement={},
                    potential_twists=[],
                    estimated_impact=""
                )

            json_str = content[json_start:json_end]
            data = json.loads(json_str)

            return ResolutionSuggestion(
                thread_id=thread_id,
                suggested_episode=data.get("suggested_episode", 0),
                approach=data.get("approach", ""),
                key_elements=data.get("key_elements", []),
                character_involvement=data.get("character_involvement", {}),
                potential_twists=data.get("potential_twists", []),
                estimated_impact=data.get("estimated_impact", "")
            )

        except Exception as e:
            logger.error(f"解析收线建议响应失败: {str(e)}")
            return ResolutionSuggestion(
                thread_id=thread_id,
                suggested_episode=0,
                approach="解析失败",
                key_elements=[],
                character_involvement={},
                potential_twists=[],
                estimated_impact=""
            )

    # ============================================
    # 转换方法
    # ============================================

    def _convert_to_enhanced_memory(self, memory: Memory) -> EnhancedMemory:
        """将基础 Memory 转换为 EnhancedMemory"""
        # 防御性处理 characterStates
        char_states = {}
        raw_states = getattr(memory, 'characterStates', {})
        
        if isinstance(raw_states, dict):
            for char, states in raw_states.items():
                if isinstance(states, list):
                    char_states[char] = [
                        CharacterStateChange(**state) if isinstance(state, dict) else state
                        for state in states
                    ]
                else:
                    char_states[char] = []
        elif isinstance(raw_states, str) and raw_states:
            import ast
            try:
                parsed = ast.literal_eval(raw_states)
                if isinstance(parsed, dict):
                    for char, states in parsed.items():
                        if isinstance(states, list):
                            char_states[char] = [
                                CharacterStateChange(**state) if isinstance(state, dict) else state
                                for state in states
                            ]
            except:
                pass

        return EnhancedMemory(
            eventTimeline=[
                TimelineEvent(**event) if isinstance(event, dict) else event
                for event in (memory.eventTimeline if isinstance(memory.eventTimeline, list) else [])
            ],
            pendingThreads=[
                PendingThread(**thread) if isinstance(thread, dict) else thread
                for thread in (memory.pendingThreads if isinstance(memory.pendingThreads, list) else [])
            ],
            characterStates=char_states,
            foreshadowing=[
                ForeshadowingEvent(**fs) if isinstance(fs, dict) else fs
                for fs in (memory.foreshadowing if isinstance(memory.foreshadowing, list) else [])
            ],
            relationships=getattr(memory, 'relationships', {}),
            consistencyIssues=[],
            last_updated=datetime.now(),
            last_episode_processed=0
        )

    def _convert_to_memory(self, enhanced: EnhancedMemory) -> Memory:
        """将 EnhancedMemory 转换回基础 Memory"""
        return Memory(
            eventTimeline=[event.dict() for event in enhanced.eventTimeline],
            pendingThreads=[thread.dict() for thread in enhanced.pendingThreads],
            foreshadowing=[fs.dict() for fs in enhanced.foreshadowing],
            characterStates={
                char: [state.dict() for state in states]
                for char, states in enhanced.characterStates.items()
            }
        )


# ============================================
# 全局单例
# ============================================

_memory_manager: Optional[MemoryManager] = None


def get_memory_manager() -> MemoryManager:
    """获取记忆管理器单例"""
    global _memory_manager
    if _memory_manager is None:
        _memory_manager = MemoryManager()
    return _memory_manager
