#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
选题理解Agent（视频分析版本）

功能: 分析和理解视频的选题策略和定位
核心任务: 基于视频内容和三点分析结果，理解选题逻辑
特征: 整合视频逐帧分析结果和三点数据，揭示选题策略
"""

from typing import Any, Dict
from src.components.agents.base import BaseLLMAgent
from src.utils.logger import get_logger

logger = get_logger(__name__)


class TopicSelectionUnderstandingAgent(BaseLLMAgent):
    """选题理解Agent - 分析和理解视频的选题策略和定位（视频分析版本）"""

    def __init__(
        self,
        name: str = "topic_selection_understanding_agent",
        description: str = "选题理解Agent",
        model_provider: str = "google_genai",
        temperature: float = 0.7,
        max_tokens: int = 20480
    ):
        """
        初始化选题理解Agent

        Args:
            name: Agent名称
            description: Agent描述
            model_provider: 模型提供商 ("openai" 或 "google_genai")
            temperature: 生成温度,控制创造性
            max_tokens: 最大token数
        """
        system_prompt = self._build_system_prompt()
        super().__init__(
            name=name,
            description=description,
            model_provider=model_provider,
            system_prompt=system_prompt,
            temperature=temperature,
            max_tokens=max_tokens
        )

    def _build_system_prompt(self) -> str:
        """构建系统提示词 - 定义角色、能力、输出规范"""
        return """你是选题策略分析专家，擅长整合多维度信息揭示视频内容的选题逻辑。

# 视频分析要求
- 需要对视频进行逐帧或关键帧分析，理解视频的整体内容和结构
- 关注视频的画面、动作、场景、对话等各个维度
- 基于视频的实际内容进行分析，不能凭空想象

# 核心能力
1. **三点关系理解**：
   - 灵感点 = 创作前的触发源（什么刺激触发了创作）
   - 目的点 = 创作时的动机（为什么要创作）
   - 关键点 = 消费时的吸引力（什么吸引受众）
   - 选题 = 将灵感转化为目的，通过关键点实现

2. **有机整合**：分析三点之间的内在关联，而非简单罗列

3. **证据驱动**：所有结论基于具体数据推导

4. **覆盖验证**：确保选题理解覆盖所有灵感点、目的点、关键点，未覆盖的点需要说明原因

# 输出规范
严格按照以下JSON结构输出：
```json
{
    "主题": "string - 一句话概括选题主题",
    "描述": "string - 用具体细节描述核心逻辑（灵感来源→关键手法→呈现效果→创作目的），必须明确体现所有目的点",
    "覆盖情况": {
        "灵感点覆盖情况": [
            {
                "灵感点": "string - 灵感点名称",
                "是否覆盖": true/false,
                "覆盖说明": "string - 如何在选题中体现，如果未覆盖说明原因"
            }
        ],
        "目的点覆盖情况": [
            {
                "目的点": "string - 目的点名称",
                "是否覆盖": true/false,
                "覆盖说明": "string - 如何在选题中实现，如果未覆盖说明原因"
            }
        ],
        "关键点覆盖情况": [
            {
                "关键点": "string - 关键点名称",
                "是否覆盖": true/false,
                "覆盖说明": "string - 如何在选题中支撑，如果未覆盖说明原因"
            }
        ]
    }
}
```

**注意**：不需要输出 `explicit_elements` 字段，该字段由系统自动提取。

# topic_description 核心原则
**描述本质，精炼核心**：
1. **拒绝笼统概念** - 要具体到本质特征，不用抽象方法论
2. **删除非核心修饰** - 只保留最本质的词
3. **具体但不冗余** - 描述具体现象，不堆砌修饰词

# coverage_analysis 核心原则
**确保完整覆盖**：
1. **逐一检查** - 检查每个灵感点、目的点、关键点是否在选题中有体现
2. **明确说明** - 如果覆盖，说明如何体现；如果未覆盖，说明为什么未体现
3. **追求完整** - 努力让选题理解覆盖所有三点，未覆盖需要充分理由"""

    def process(self, state: Dict[str, Any], config=None) -> Dict[str, Any]:
        """处理状态：读取视频和prompt → 调用视频分析 → 写回选题理解结果
        
        步骤：
        1. 从 state 中获取视频文件对象
        2. 构建完整的 prompt（包含系统提示词和用户提示词）
        3. 使用 safe_invoke_video_analysis 进行分析
        4. 将选题理解结果写回 state
        
        Returns:
            dict: {"topic_selection_understanding": <解析后的JSON>}
        """
        if not self.is_initialized:
            self.initialize()
        
        logger.info("TopicSelectionUnderstandingAgent 开始执行")
        
        try:
            # Step 1: 从 state 中获取视频文件对象
            from src.utils.llm_invoker import get_video_file_from_state, LLMInvoker
            video_file = get_video_file_from_state(state)
            
            if not video_file:
                logger.warning("无法获取视频文件对象，跳过选题理解分析")
                return {
                    "topic_selection_understanding": {
                        "主题": "",
                        "描述": "",
                        "覆盖情况": {
                            "灵感点覆盖情况": [],
                            "目的点覆盖情况": [],
                            "关键点覆盖情况": []
                        },
                        "explicit_elements": self._extract_explicit_elements(state),
                        "错误": "无法获取视频文件对象"
                    }
                }
            
            # Step 2: 构建完整的 prompt（包含系统提示词和用户提示词）
            prompt = self._build_all_prompt(state)
            
            # Step 3: 使用 safe_invoke_video_analysis 进行分析
            logger.info("基于视频内容进行选题理解分析...")
            result = LLMInvoker.safe_invoke_video_analysis(
                "选题理解分析",
                video_file,
                prompt,
                agent=self,
                fallback={
                    "主题": "",
                    "描述": "",
                    "覆盖情况": {
                        "灵感点覆盖情况": [],
                        "目的点覆盖情况": [],
                        "关键点覆盖情况": []
                    }
                }
            )
            
            logger.info(f"TopicSelectionUnderstandingAgent 执行完成: {result}")
            
            return self._update_state(state, result)
            
        except Exception as e:
            logger.error(f"TopicSelectionUnderstandingAgent 执行失败: {e}", exc_info=True)
            return self._update_state(state, {
                "主题": "",
                "描述": "",
                "覆盖情况": {
                    "灵感点覆盖情况": [],
                    "目的点覆盖情况": [],
                    "关键点覆盖情况": []
                },
                "错误": str(e)
            })
        
    def _build_all_prompt(self, state: Dict[str, Any]) -> list:  # BaseLLMAgent要求实现
        """
        构建LLM消息

        Args:
            state: 包含视频和其他agent结果的状态字典,需要包含:
                - video_uploaded_uri: 上传的视频URI
                - inspiration_points: 灵感点提取结果
                - purpose_point: 目的点提取结果
                - key_points: 关键点提取结果

        Returns:
            消息列表
        """
        # 提取输入数据（视频分析版本）
        video_url = state.get("video", "")
        video_uri = state.get("video_uploaded_uri", "")
        video_url_for_prompt = video_url if video_url else video_uri
        inspiration_points = state.get("inspiration_points", [])
        purpose_point = state.get("purpose_point", {})
        key_points = state.get("key_points", {})

        if not video_url_for_prompt:
            logger.warning("未找到视频URL或URI，跳过选题理解")
            return [
                {"role": "system", "content": self.system_prompt},
                {"role": "user", "content": "未找到视频URL或URI"}
            ]


        # 构建提示词（只使用视频和其他agent的结果）
        prompt = self._build_user_prompt(
            video_url_for_prompt,
            inspiration_points,
            purpose_point,
            key_points
        )

        # 构建消息（将视频URL直接添加到prompt中）
        return self.system_prompt + "\n\n" + prompt

    def _update_state(self, state: Dict[str, Any], response: Any) -> Dict[str, Any]:
        """
        更新状态

        Args:
            state: 原始状态
            response: LLM响应或已解析的字典

        Returns:
            更新后的状态
        """
        # 检查 response 是否已经是解析后的字典（来自 safe_invoke_video_analysis）
        if isinstance(response, dict):
            # 已经是解析后的结果，直接使用
            result = response
        else:
            # 是 LLM 响应对象，需要提取 content 并解析
            if hasattr(response, 'content'):
                content = response.content
            elif hasattr(response, 'text'):
                content = response.text
            else:
                content = str(response)
            
            result = self._parse_response(content)

        # 确保 result 是字典
        if not isinstance(result, dict):
            logger.warning(f"解析结果不是字典类型: {type(result)}")
            result = {
                "主题": "",
                "描述": "",
                "覆盖情况": {
                    "灵感点覆盖情况": [],
                    "目的点覆盖情况": [],
                    "关键点覆盖情况": []
                }
            }

        # 通过代码直接映射 explicit_elements（不依赖LLM输出）
        result["explicit_elements"] = self._extract_explicit_elements(state)

        return {
            "topic_selection_understanding": result
        }

    def _build_user_prompt(
        self,
        video_url: str,
        inspiration_points: Any,
        purpose_point: Dict[str, Any],
        key_points: Dict[str, Any]
    ) -> str:
        """构建用户提示 - 提供数据和执行指令（视频分析版本）"""
        # 格式化三点数据
        inspiration_section = self._format_inspiration_points(inspiration_points)
        purpose_section = self._format_purpose_point(purpose_point)
        key_points_section = self._format_key_points(key_points)

        return f"""# 输入数据

## 视频内容 
请对视频进行逐帧或关键帧分析。

**重要**：你需要对视频进行逐帧或关键帧分析，从以下维度理解视频内容：
- **画面内容**：场景、物体、人物、环境等视觉元素
- **动作变化**：人物的动作、物体的运动、场景的转换
- **时间线索**：视频的时间顺序、节奏变化、关键时间点
- **视觉细节**：颜色、光影、构图、视觉焦点等
- **音频信息**（如有）：对话、音效、背景音乐等

基于视频的实际内容，结合以下三点分析结果，理解选题策略。

## 灵感点分析
{inspiration_section}

## 目的点分析
{purpose_section}

## 关键点分析
{key_points_section}

---

# 执行指令

基于以上数据，输出选题策略分析。

## 质量要求
- **描述**: 必须是完整流畅的1-2句话，清晰展现完整逻辑链"灵感来源→关键手法→呈现效果→创作目的"
  - **必须包含目的点**: 明确说明创作者的目的，不能只描述内容本身
  - **必须体现三点内在关联**: 分析三点的有机融合逻辑，而非简单罗列
  - **基于视频内容**: 所有分析必须基于视频的实际内容，不能凭空想象
  - 拒绝笼统概念，要具体到本质特征
  - 删除非核心修饰词，只保留最本质的词
  - 禁用空洞词汇: "精心"、"独特"、"全面"、"旨在"、"充分"、"深度"、"夸张的"、"强烈的"、"明显的"

**重要提醒**：
- 不需要输出 `explicit_elements` 字段，系统会自动从三点数据中提取
- 所有分析必须基于视频的实际内容和三点分析结果"""

    def _format_inspiration_points(self, inspiration_points: Any) -> str:
        """格式化灵感点信息 - 只保留核心信息

        支持两种数据结构：
        1. 新结构（列表）：直接是列表 [...]
        2. 旧结构：{"points": [...]}
        """
        # 新结构：inspiration_points 直接是列表
        if isinstance(inspiration_points, list):
            if not inspiration_points:
                return "**未提取到灵感点**"

            formatted = f"**灵感点总数**: {len(inspiration_points)}\n\n"

            for idx, point in enumerate(inspiration_points, 1):
                formatted += f"### 灵感点 {idx}: {point.get('灵感点', 'N/A')}\n"
                formatted += f"**分类**: {point.get('分类', '')}\n"
                formatted += f"**描述**: {point.get('描述', '')}\n"
                if point.get("推理"):
                    formatted += f"**推理**: {point.get('推理', '')}\n"
                formatted += "\n"

            return formatted

        # 旧结构：{"points": [...]} 或字典格式
        if isinstance(inspiration_points, dict):
            # 旧结构：points列表
            if "points" in inspiration_points:
                points = inspiration_points.get("points", [])
                if not points:
                    return "**未提取到灵感点**"

                formatted = f"**灵感点总数**: {len(points)}\n\n"

                for idx, point in enumerate(points, 1):
                    formatted += f"### 灵感点 {idx}: {point.get('灵感点表象', 'N/A')}\n"
                    formatted += f"**本质**: {point.get('灵感点本质', '')}\n"
                    formatted += f"{point.get('本质详细说明', '')}\n\n"

                return formatted

        return "**未提取到灵感点**"

    def _format_purpose_point(self, purpose_point: Dict[str, Any]) -> str:
        """格式化目的点信息 - 只支持新结构，确保包含所有目的点"""
        if not purpose_point:
            return "**未提取到目的点**"

        # 新结构：{"perspective": "创作者视角", "purposes": [...], "total_count": 2}
        purposes = purpose_point.get("purposes", [])
        if not purposes:
            return "**未提取到目的点**"

        formatted = f"**目的点总数**: {len(purposes)}（所有目的点平等不分主次）\n\n"

        for idx, purpose in enumerate(purposes, 1):
            维度 = purpose.get("维度", {})
            if isinstance(维度, dict):
                维度_str = f"{维度.get('一级分类', '')}/{维度.get('二级分类', '')}"
            else:
                维度_str = str(维度)

            目的点 = purpose.get("目的点", "N/A")
            描述 = purpose.get("描述", "")

            formatted += f"### 目的点 {idx}: {目的点}\n"
            formatted += f"**维度**: {维度_str}\n"
            if 描述:
                formatted += f"**描述**: {描述}\n"
            formatted += "\n"

        return formatted

    def _extract_explicit_elements(self, state: Dict[str, Any]) -> Dict[str, list]:
        """从状态中提取显式元素（通过代码映射，不依赖LLM）

        Args:
            state: 包含三点数据的状态字典

        Returns:
            显式元素字典，包含灵感点列表、目的点、关键点列表
        """
        # 提取灵感点列表
        inspiration_points_data = state.get("inspiration_points", [])
        灵感点列表 = []

        # 新结构：inspiration_points 直接是列表
        if isinstance(inspiration_points_data, list):
            for point in inspiration_points_data:
                灵感点 = point.get("灵感点", "")
                if 灵感点:
                    灵感点列表.append(灵感点)
        # 旧结构：字典格式
        elif isinstance(inspiration_points_data, dict):
            # 尝试旧的points结构
            if "points" in inspiration_points_data:
                points = inspiration_points_data.get("points", [])
                for point in points:
                    灵感点表象 = point.get("灵感点表象", "")
                    if 灵感点表象:
                        灵感点列表.append(灵感点表象)

        # 提取目的点（支持新旧两种结构）
        purpose_point_data = state.get("purpose_point", {})
        目的点列表 = []

        # 新结构：{"purposes": [...]}
        if "purposes" in purpose_point_data:
            purposes = purpose_point_data.get("purposes", [])
            # 提取所有目的点
            for purpose in purposes:
                purpose_text = purpose.get("目的点", "")
                if purpose_text:
                    目的点列表.append(purpose_text)
        # 旧结构（兼容）：{"main_purpose": {...}}
        elif "main_purpose" in purpose_point_data:
            main_purpose = purpose_point_data.get("main_purpose", {})
            purpose_text = main_purpose.get("目的点", "") if isinstance(main_purpose, dict) else ""
            if purpose_text:
                目的点列表.append(purpose_text)

        # 提取关键点列表（支持层级结构）
        key_points_data = state.get("key_points", {})
        关键点列表 = []

        # 递归提取所有关键点（包括子关键点）
        def extract_points_recursive(points: list):
            for point in points:
                关键点 = point.get("关键点", "")
                if 关键点:
                    关键点列表.append(关键点)

                # 递归处理子关键点
                children = point.get("children", [])
                if children:
                    extract_points_recursive(children)

        key_points_list = key_points_data.get("key_points", [])
        extract_points_recursive(key_points_list)

        return {
            "灵感点列表": 灵感点列表,
            "目的点列表": 目的点列表,
            "关键点列表": 关键点列表
        }

    def _format_key_points(self, key_points: Dict[str, Any]) -> str:
        """格式化关键点信息 - 支持层级结构"""
        if not key_points or "key_points" not in key_points:
            return "**未提取到关键点**"

        points = key_points.get("key_points", [])
        if not points:
            return "**未提取到关键点**"

        total_count = key_points.get("total_count", len(points))
        root_count = key_points.get("root_count", len(points))
        hierarchy_enabled = key_points.get("hierarchy_enabled", False)

        formatted = f"**关键点总数**: {total_count}\n"
        if hierarchy_enabled:
            formatted += f"**一级关键点数**: {root_count}\n"
            formatted += f"**层级结构**: 已启用\n\n"
        else:
            formatted += "\n"

        # 递归格式化关键点树
        def format_point_tree(point: Dict[str, Any], level: int = 1, index: int = 1) -> str:
            indent = "  " * (level - 1)
            result = f"{indent}### 关键点 {index} (层级 {level}): {point.get('关键点', 'N/A')}\n"
            result += f"{indent}**维度**: [{point.get('维度大类', '')}] {point.get('维度细分', '')}\n"
            result += f"{indent}**描述**: {point.get('描述', '')}\n"

            # 递归处理子关键点
            children = point.get("children", [])
            if children:
                result += f"{indent}**子关键点**: {len(children)}个\n\n"
                for child_idx, child in enumerate(children, 1):
                    result += format_point_tree(child, level + 1, child_idx)
            else:
                result += "\n"

            return result

        # 格式化所有一级关键点
        for idx, point in enumerate(points, 1):
            formatted += format_point_tree(point, level=1, index=idx)

        return formatted

    def _parse_response(self, response: str) -> Dict[str, Any]:
        """解析LLM响应"""
        import json
        import re

        try:
            # 尝试提取JSON代码块
            json_match = re.search(r'```(?:json)?\s*(.*?)\s*```', response, re.DOTALL)
            if json_match:
                json_str = json_match.group(1)
            else:
                json_str = response

            # 解析JSON
            result = json.loads(json_str)

            # 确保包含必需字段（explicit_elements 由代码添加，不在这里处理）
            required_fields = {
                "主题": "",
                "描述": "",
                "覆盖情况": {
                    "灵感点覆盖情况": [],
                    "目的点覆盖情况": [],
                    "关键点覆盖情况": []
                }
            }

            # 填充缺失字段
            for key, default_value in required_fields.items():
                if key not in result:
                    result[key] = default_value
                elif key == "覆盖情况" and isinstance(result[key], dict):
                    # 确保覆盖情况的子字段存在
                    if "灵感点覆盖情况" not in result[key]:
                        result[key]["灵感点覆盖情况"] = []
                    if "目的点覆盖情况" not in result[key]:
                        result[key]["目的点覆盖情况"] = []
                    if "关键点覆盖情况" not in result[key]:
                        result[key]["关键点覆盖情况"] = []

            return result

        except json.JSONDecodeError as e:
            logger.error(f"JSON解析失败: {e}")
            # JSON解析失败，返回默认结构（explicit_elements 由代码添加）
            return {
                "主题": response[:200] if len(response) > 200 else response,
                "描述": "解析失败",
                "覆盖情况": {
                    "灵感点覆盖情况": [],
                    "目的点覆盖情况": [],
                    "关键点覆盖情况": []
                }
            }
    def _build_messages(self, state: Dict[str, Any]) -> list[Dict[str, Any]]:
        """构建消息"""
        return []
