"""
Comment Analysis Agent.

评论分析Agent：从评论中提取帖子解构维度。
"""

from typing import Dict, Any, List
import json

from src.components.agents.base import BaseLLMAgent
from src.states.what_deconstruction_state import WhatDeconstructionState
from src.utils.logger import get_logger

logger = get_logger(__name__)


class CommentAnalysisAgent(BaseLLMAgent):
    """评论分析Agent

    核心功能：
    1. 过滤评论：过滤掉与帖子内容无关的评论
    2. 判断评论与内容元素的相关性
    3. 从相关评论中提取帖子应该解构的维度
    4. 为后续解构提供结构化的维度数据

    工作流程（两步法）：
    - 第1步：评论过滤 → 保留相关评论
    - 第2步：相关评论 → 元素相关性判断 → 提取解构维度 → 输出结构化维度数据
    """

    def __init__(
        self,
        name: str = "comment_analysis_agent",
        description: str = "从评论中提取帖子解构维度",
        model_provider: str = "google_genai",
        temperature: float = 0.1,
        max_tokens: int = 40960
    ):
        # 维度提取的 system prompt
        system_prompt = """你是一个专业的内容分析专家。你的核心任务是：从用户评论中提取出**帖子元素物理拆解所需的维度**。

# 背景说明

你收到的评论已经过筛选，都是与帖子内容相关、描述物理特征的评论。你的任务是从这些评论中提取解构维度。

# 核心原则

**只提取与元素物理拆解密切相关的维度**，这些维度必须是：
- 可观察的物理特征和属性
- 可量化的客观指标
- 与元素分割、重组、复现直接相关
- 能够指导后续元素解构和分析

# 工作流程

## 步骤1：分析评论关注点

分析评论中提到的物理特征和客观属性：
- 评论关注了哪些**可观察的物理特征**？
- 评论讨论了哪些**客观属性或状态**？
- 评论询问了哪些**具体细节**？

## 步骤2：识别解构维度

**维度识别的4个标准**（必须同时满足）：
1. **可观察性**：该维度必须是通过视觉、触觉等感官可以直接观察到的物理特征
2. **可量化性**：该维度可以用具体参数、数值、状态来描述（如尺寸、颜色值、处理状态）
3. **拆解相关性**：该维度必须与元素的物理分割、重组、复现直接相关
4. **客观验证性**：该维度可以通过客观标准验证，不依赖个人主观判断

**典型的物理拆解维度**：
- ✅ 处理状态（如"切块状态"、"烹饪程度"）
- ✅ 色彩特征（如"颜色分布"、"色调风格"）
- ✅ 材质特征（如"表面质地"、"材料类型"）
- ✅ 尺寸规格（如"大小比例"、"形状轮廓"）
- ✅ 结构组成（如"构图方式"、"元素排列"）
- ✅ 工艺细节（如"制作工艺"、"呈现手法"）

**不应提取的维度**：
- ❌ 情感类维度（如"温馨感"、"幸福感"）
- ❌ 抽象概念（如"氛围"、"风格"，除非可以具体量化）
- ❌ 主观评价（如"好看程度"、"吸引力"）

## 步骤3：提取并描述维度

对于每个识别出的维度：
- **维度名称**：2-6个字，简洁明确
- **维度描述**：说明为什么要从这个维度解构，这个维度包含什么具体内容
- **提取理由**：基于评论内容，说明为什么提取这个维度

## 步骤4：输出结构化数据

必须严格按照以下JSON格式输出：

{
    "deconstruction_dimensions": [
        {
            "dimension_name": "维度名称（2-6个字）",
            "dimension_description": "该维度的详细说明（为什么要从这个维度解构，这个维度包含什么）",
            "extraction_reason": "为什么从评论中提取这个维度（基于评论内容的客观分析）"
        }
    ]
}

# 重要提醒

- 如果评论中没有涉及物理特征的内容，返回空的 deconstruction_dimensions 列表
- 每个维度都必须是**可操作的**、**可验证的**物理特征
- 维度要能够指导后续的图片/文本解构工作
- 严格排除主观感受类、情感类维度
"""

        super().__init__(
            name=name,
            description=description,
            model_provider=model_provider,
            system_prompt=system_prompt,
            temperature=temperature,
            max_tokens=max_tokens
        )

        # 评论过滤的 system prompt
        self.filter_system_prompt = """你是一个专业的评论过滤专家。你的任务是：判断每条评论是否在描述帖子中肉眼可见元素的具体特征。

# 核心标准（唯一判断标准）

**只保留描述帖子中肉眼可见元素具体特征的评论**

评论必须**同时满足**以下条件才能保留：
1. **可见性**：评论描述的内容必须在帖子中肉眼可见（文字、图片、视频等）
2. **客观性**：评论是在描述"看到的具体内容"，而不是"联想、想法、感受、分类"
3. **物理性**：评论涉及元素的**具体物理特征**（颜色、大小、形状、材质、状态、构成、细节等）
4. **描述性**：评论是在**描述特征**，而不是在**讨论分类、质疑标签、表达观点**

# 判断方法

**核心问题：评论是在描述"帖子具体内容的特征"，还是在"讨论其他问题"？**

**判断框架**：

1. **确认可见性**
   - 评论提到的内容在帖子中能看到吗？

2. **识别表达对象**
   - 评论是在描述"帖子中元素的具体特征"吗？

3. **判断表达性质**
   - 评论是在客观描述特征，还是在讨论分类/标签/观点？

4. **检查物理特征**
   - 评论是否包含可观察的物理特征描述？

# 重点区分：描述特征 vs 讨论分类

**❌ 过滤掉（只讨论分类/标签/观点）**：
- "咋不是表情包" → 只在质疑分类，未描述任何特征
- "这是搞笑类的" → 只在讨论类别，未描述内容
- "应该打上XX标签" → 只在讨论标签，未描述特征
- "这算XX风格吗？" → 只在讨论风格定义，未描述具体内容

**✅ 保留（描述具体特征）**：
- "表情包中的猫咪眼睛圆圆的，很可爱" → 描述了猫咪的具体特征
- "图片是上下对比的形式" → 描述了图片的构成结构
- "这个芒果是切成块的" → 描述了芒果的物理状态
- "色调偏暖，橘色为主" → 描述了色彩特征

# 输出格式

必须严格按照以下JSON格式输出：

{
    "filtered_comments": [
        {
            "content": "评论原文",
            "is_relevant": true/false,
            "reason": "理由（5-10字精准表达）"
        }
    ],
    "summary": {
        "total": 10,
        "relevant": 7,
        "filtered_out": 3
    }
}

# 重要提醒

- **描述特征优先**：只保留描述元素具体特征的评论
- **排除分类讨论**：过滤仅讨论分类、标签、类别的评论（如"咋不是表情包"）
- **排除情感表达**：过滤所有情感、共鸣、联想、个人经历等内容
- **排除主观判断**：过滤主观评价、观点表达、建议等内容
- **物理特征为准**：只保留包含物理特征（颜色、大小、形状、材质、状态、构成等）的评论
- **精简理由**：理由控制在5-10字，精准表达核心判断依据

**典型过滤场景**：
- "咋不是XX分类" → 过滤（质疑分类，非描述特征）
- "太XX了" → 过滤（情感表达，非物理描述）
- "我也有同感" → 过滤（主观共鸣，非内容描述）
- "应该XX" → 过滤（观点建议，非客观描述）
"""

    def process(self, state: WhatDeconstructionState) -> Dict[str, Any]:
        return {"deconstruction_dimensions": [], "filtered_comments": []}
        """处理流程（重写父类方法）

        两步处理：
        1. 过滤评论
        2. 提取维度
        """
        try:
            # 第1步：过滤评论
            logger.info(f"\n{'#'*60}")
            logger.info("第1步：开始过滤评论")
            logger.info(f"{'#'*60}")
            filtered_comments = self._filter_comments(state)

            if not filtered_comments:
                logger.warning("\n⚠️  没有相关评论，跳过维度提取\n")
                return {"deconstruction_dimensions": [], "filtered_comments": []}

            # 第2步：提取维度（使用过滤后的评论）
            logger.info(f"\n{'#'*60}")
            logger.info("第2步：开始提取解构维度")
            logger.info(f"{'#'*60}\n")

            # 确保已初始化
            if not self.is_initialized:
                self.initialize()

            # 临时修改 state 中的评论为过滤后的评论
            original_comments = state.get("comments", [])
            state["comments"] = filtered_comments

            # 构建消息
            messages = self._build_extraction_messages(state)

            # 调用 LLM
            response = self.model.invoke(messages)

            # 更新状态
            result = self._update_state(state, response)

            # 恢复原始评论
            state["comments"] = original_comments

            # 添加过滤后的评论到返回结果
            result["filtered_comments"] = filtered_comments

            return result

        except Exception as e:
            logger.error(f"评论分析处理失败: {e}", exc_info=True)
            return {"deconstruction_dimensions": [], "filtered_comments": []}

    def _filter_comments(self, state: WhatDeconstructionState) -> List[Dict[str, Any]]:
        """过滤评论：只保留与帖子内容相关的评论

        支持分批处理大量评论，避免LLM输出长度限制

        Returns:
            List[Dict]: 过滤后的相关评论列表
        """
        try:
            comments = state.get("comments", [])

            if not comments:
                logger.info("没有评论需要过滤")
                return []

            # 调试日志：打印传入的评论数量
            total_input = len(comments)
            sub_input = sum(len(c.get("sub_comments", [])) for c in comments if isinstance(c, dict))
            logger.info(f"开始过滤评论 - 主评论: {total_input}条, 子评论: {sub_input}条, 总计: {total_input + sub_input}条")

            # 确保已初始化
            if not self.is_initialized:
                self.initialize()

            # 分批处理评论（每批最多30条主评论）
            batch_size = 30
            all_relevant_comments = []
            all_filtered_out_comments = []
            total_processed = 0

            for batch_idx in range(0, len(comments), batch_size):
                batch_comments = comments[batch_idx:batch_idx + batch_size]
                batch_num = batch_idx // batch_size + 1
                total_batches = (len(comments) + batch_size - 1) // batch_size

                logger.info(f"\n处理第 {batch_num}/{total_batches} 批评论（{len(batch_comments)}条主评论）")

                # 临时修改state中的评论为当前批次
                batch_state = state.copy()
                batch_state["comments"] = batch_comments

                # 构建过滤消息
                messages = self._build_filter_messages(batch_state)

                # 调用 LLM 进行过滤
                response = self.model.invoke(messages)

                # 解析响应
                filtered_result = self._parse_filter_response(response)

                # 分类评论
                for comment in filtered_result.get("filtered_comments", []):
                    if comment.get("is_relevant", False):
                        all_relevant_comments.append(comment)
                    else:
                        all_filtered_out_comments.append(comment)

                total_processed += len(filtered_result.get("filtered_comments", []))

            # 打印详细的过滤结果
            logger.info(f"\n{'='*60}")
            logger.info(f"评论过滤完成 - 总数: {total_input + sub_input}, 相关: {len(all_relevant_comments)}, 过滤: {len(all_filtered_out_comments)}")
            logger.info(f"{'='*60}")

            # 打印相关评论详情
            if all_relevant_comments:
                logger.info(f"\n✅ 保留的相关评论 ({len(all_relevant_comments)}条)：")
                for idx, comment in enumerate(all_relevant_comments, 1):
                    logger.info(f"  [{idx}] {comment.get('content', '')}")
                    logger.info(f"      理由: {comment.get('reason', '')}")

            # 打印被过滤的评论详情
            if all_filtered_out_comments:
                logger.info(f"\n❌ 被过滤的无关评论 ({len(all_filtered_out_comments)}条)：")
                for idx, comment in enumerate(all_filtered_out_comments, 1):
                    logger.info(f"  [{idx}] {comment.get('content', '')}")
                    logger.info(f"      理由: {comment.get('reason', '')}")

            logger.info(f"\n{'='*60}\n")

            return all_relevant_comments

        except Exception as e:
            logger.error(f"评论过滤失败: {e}, 返回所有评论", exc_info=True)
            # 失败时返回所有评论
            return state.get("comments", [])

    def _parse_filter_response(self, response: Any) -> Dict[str, Any]:
        """解析评论过滤响应"""
        try:
            # 提取响应内容
            if hasattr(response, 'content'):
                content = response.content
            else:
                content = str(response)

            # 尝试提取JSON
            content = content.strip()
            if "```json" in content:
                content = content.split("```json")[1].split("```")[0].strip()
            elif "```" in content:
                content = content.split("```")[1].split("```")[0].strip()

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

            if "filtered_comments" not in result:
                raise ValueError("响应缺少filtered_comments字段")

            return result

        except Exception as e:
            logger.error(f"解析过滤响应失败: {e}")
            logger.error(f"原始响应内容: {response}")
            return {"filtered_comments": [], "summary": {"total": 0, "relevant": 0, "filtered_out": 0}}

    def _build_filter_messages(self, state: WhatDeconstructionState) -> List[Dict[str, Any]]:
        """构建评论过滤的消息（第1步）

        包含帖子内容（文本+图片）和所有评论
        """
        messages = [
            {"role": "system", "content": self.filter_system_prompt}
        ]

        # 构建用户消息内容列表（支持多模态）
        content_parts = []

        # 添加帖子文本内容
        user_prompt = "# 帖子内容\n\n"

        text_data = state.get("text", {})
        if text_data.get("title"):
            user_prompt += f"**标题**：{text_data['title']}\n\n"

        if text_data.get("body"):
            body = text_data['body']
            user_prompt += f"**正文**：{body}\n\n"

        if text_data.get("hashtags"):
            hashtags_str = " ".join(text_data["hashtags"])
            user_prompt += f"**话题标签**：{hashtags_str}\n\n"

        # 添加图片
        images = state.get("images", [])
        if images:
            user_prompt += f"**图片**：帖子包含 {len(images)} 张图片，如下所示：\n\n"

        # 添加文本部分
        content_parts.append({
            "type": "text",
            "text": user_prompt
        })

        # 添加图片（多模态）
        if images:
            for idx, image_url in enumerate(images, 1):
                content_parts.append({
                    "type": "image_url",
                    "image_url": {"url": image_url}
                })
                # 在每张图片后添加标注
                content_parts.append({
                    "type": "text",
                    "text": f"↑ 第{idx}张图片\n\n"
                })

        # 添加评论内容
        comment_text = self._format_comments(state)
        content_parts.append({
            "type": "text",
            "text": comment_text
        })

        # 构建多模态消息
        messages.append({
            "role": "user",
            "content": content_parts
        })

        return messages

    def _build_extraction_messages(self, state: WhatDeconstructionState) -> List[Dict[str, Any]]:
        """构建维度提取的消息（第2步）

        包含帖子内容（文本+图片）和过滤后的评论
        """
        messages = [
            {"role": "system", "content": self.system_prompt}
        ]

        # 构建用户消息内容列表（支持多模态）
        content_parts = []

        # 添加帖子文本内容
        user_prompt = "# 帖子内容\n\n"

        text_data = state.get("text", {})
        if text_data.get("title"):
            user_prompt += f"**标题**：{text_data['title']}\n\n"

        if text_data.get("body"):
            body = text_data['body']
            user_prompt += f"**正文**：{body}\n\n"

        if text_data.get("hashtags"):
            hashtags_str = " ".join(text_data["hashtags"])
            user_prompt += f"**话题标签**：{hashtags_str}\n\n"

        # 添加图片
        images = state.get("images", [])
        if images:
            user_prompt += f"**图片**：帖子包含 {len(images)} 张图片，如下所示：\n\n"

        # 添加文本部分
        content_parts.append({
            "type": "text",
            "text": user_prompt
        })

        # 添加图片（多模态）
        if images:
            for idx, image_url in enumerate(images, 1):
                content_parts.append({
                    "type": "image_url",
                    "image_url": {"url": image_url}
                })
                # 在每张图片后添加标注
                content_parts.append({
                    "type": "text",
                    "text": f"↑ 第{idx}张图片\n\n"
                })

        # 添加评论内容（已过滤的相关评论）
        comment_text = self._format_comments(state)
        comment_text += "\n\n# 任务说明\n\n"
        comment_text += "以上评论已经过筛选，都是与帖子内容相关、描述物理特征的评论。\n\n"
        comment_text += "**你的任务**：从这些评论中提取帖子元素物理拆解所需的维度\n\n"
        comment_text += "**要求**：\n"
        comment_text += "- 只提取与元素物理拆解相关的维度（如：处理状态、色彩参数、材质特征、尺寸规格、结构组成、工艺细节等）\n"
        comment_text += "- 完全排除主观感受类、情感类维度\n"
        comment_text += "- 严格按照JSON格式输出，包含所有必需字段\n"
        comment_text += "- 如果评论中没有涉及物理特征的内容，返回空的 deconstruction_dimensions 列表"

        content_parts.append({
            "type": "text",
            "text": comment_text
        })

        # 构建多模态消息
        messages.append({
            "role": "user",
            "content": content_parts
        })

        return messages

    def _format_comments(self, state: WhatDeconstructionState) -> str:
        """格式化评论文本"""
        comment_text = ""
        comments = state.get("comments", [])

        if not comments:
            comment_text += "\n# 评论区\n\n（暂无评论）\n"
            return comment_text

        # 计算总评论数
        total_count = len(comments)
        for comment in comments:
            if isinstance(comment, dict):
                total_count += len(comment.get("sub_comments", []))

        # 调试日志：打印实际评论数量
        logger.info(f"格式化评论 - 主评论数: {len(comments)}, 总评论数（含子评论）: {total_count}")

        comment_text += f"\n# 评论区（共 {total_count} 条相关评论）\n\n"

        # 遍历所有评论
        comment_idx = 1
        for comment_data in comments:
            if isinstance(comment_data, dict):
                # 处理完整的评论对象
                user_name = comment_data.get("user_nickname") or comment_data.get("user", "匿名用户")
                content = comment_data.get("content", "")

                if content:
                    comment_text += f"{comment_idx}. **{user_name}**：{content}\n"
                    comment_idx += 1

                # 处理子评论
                sub_comments = comment_data.get("sub_comments", [])
                if sub_comments:
                    for sub_comment in sub_comments:
                        sub_user_name = sub_comment.get("user_nickname") or sub_comment.get("user", "匿名用户")
                        sub_content = sub_comment.get("content", "")

                        if sub_content:
                            comment_text += f"  {comment_idx}. **{sub_user_name}**（回复）：{sub_content}\n"
                            comment_idx += 1
            else:
                # 处理简单的评论内容
                comment_text += f"{comment_idx}. {comment_data}\n"
                comment_idx += 1

        logger.info(f"实际格式化的评论数: {comment_idx - 1}")
        return comment_text

    def _build_messages(self, state: WhatDeconstructionState) -> List[Dict[str, Any]]:
        """构建消息（保留以兼容父类，实际使用 _build_extraction_messages）"""
        return self._build_extraction_messages(state)

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

        解析LLM返回的JSON，更新解构维度数据
        """
        try:
            # 提取响应内容
            if hasattr(response, 'content'):
                content = response.content
            else:
                content = str(response)

            # 尝试提取JSON
            content = content.strip()
            if "```json" in content:
                content = content.split("```json")[1].split("```")[0].strip()
            elif "```" in content:
                content = content.split("```")[1].split("```")[0].strip()

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

            # 验证必需字段
            if "deconstruction_dimensions" not in result:
                raise ValueError("响应缺少deconstruction_dimensions字段")

            dimensions = result["deconstruction_dimensions"]

            # 验证每个维度的必需字段
            required_fields = [
                "dimension_name",
                "dimension_description",
                "extraction_reason"
            ]

            for idx, dimension in enumerate(dimensions):
                for field in required_fields:
                    if field not in dimension:
                        logger.warning(f"维度 {idx} 缺少字段: {field}")

            # 打印详细的维度提取结果
            logger.info(f"\n{'='*60}")
            logger.info(f"维度提取完成 - 共提取 {len(dimensions)} 个解构维度")
            logger.info(f"{'='*60}")

            if dimensions:
                for idx, dimension in enumerate(dimensions, 1):
                    logger.info(f"\n📊 维度 [{idx}]：{dimension.get('dimension_name', '未命名')}")
                    logger.info(f"{'─'*60}")
                    logger.info(f"\n维度描述: {dimension.get('dimension_description', '')}")
                    logger.info(f"\n提取理由: {dimension.get('extraction_reason', '')}")
                    logger.info(f"{'─'*60}")
            else:
                logger.info("\n⚠️  未提取到任何解构维度（评论中可能没有涉及物理特征的内容）")

            logger.info(f"\n{'='*60}\n")

            # 返回更新后的字段
            return {
                "deconstruction_dimensions": dimensions
            }

        except Exception as e:
            logger.error(f"解析评论分析响应失败: {e}，原始内容: {response}")
            # 返回默认值
            return {
                "deconstruction_dimensions": []
            }
