"""
Recursive Text Deconstruction Agent.

文本递归解构Agent：递归解构文本元素（标题/正文/标签）。
"""

from typing import Dict, Any, List, Optional
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 RecursiveTextDeconstructionAgent(BaseLLMAgent):
    """文本递归解构Agent

    功能：
    - 递归解构文本元素（最多10层）
    - what字段必须是文本原文，不可改写
    - 使用大模型自带知识获取描述维度
    - 根据大模型判断是否继续切分
    - 递归处理子文本元素

    实现方式：BaseLLMAgent（简化版本）
    """

    def __init__(
        self,
        name: str = "recursive_text_deconstruction_agent",
        description: str = "递归解构文本元素",
        model_provider: str = "google_genai",
        temperature: float = 0.1,
        max_tokens: int = 10240,
        max_depth: int = 10
    ):
        system_prompt = """你是一个专业的文本分析专家。你的任务是深入分析文本内容，提取核心特征。

你需要：
1. 理解文本的语义和结构
2. 根据提供的描述维度，分析文本特征
3. 判断文本是否需要进一步切分为子元素
4. 识别文本的重要性权重

要求：
- 客观准确，不要臆测
- 描述要具体详细
- 判断要合理
"""

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

        self.max_depth = max_depth

    def process(self, state: WhatDeconstructionState, config: Optional[Any] = None) -> Dict[str, Any]:

        return {
                "what": "",
                "description": {},
                "relationships": [],
                "importance_weight": 0.0,
                "children": []
            }

        """处理文本递归解构

        重写process方法以实现递归逻辑
        """
        current_text = state.get("current_text", "")
        text_type = state.get("text_type", "body")
        parent_id = state.get("parent_id", "")
        depth = state.get("depth", 0)
        deconstruction_dimensions = state.get("deconstruction_dimensions", [])  # 获取解构维度

        logger.info(f"开始解构文本 - 深度: {depth}, 父节点: {parent_id}, 类型: {text_type}")
        if deconstruction_dimensions:
            logger.info(f"解构维度数量: {len(deconstruction_dimensions)}")

        # 检查递归深度
        if depth >= self.max_depth:
            logger.warning(f"达到最大递归深度 {self.max_depth}，停止解构")
            return {
                "what": current_text,
                "description": {"说明": "达到最大深度"},
                "relationships": [],
                "importance_weight": 0.5,
                "children": []
            }

        # 检查文本长度
        if not current_text or len(current_text.strip()) == 0:
            logger.warning("文本为空，停止解构")
            return {
                "what": "",
                "description": {},
                "relationships": [],
                "importance_weight": 0.0,
                "children": []
            }

        # Step 1: what字段直接使用原文
        what_value = current_text

        # Step 2: 从知识库获取描述维度（利用解构维度）
        description_dimensions = self._get_description_dimensions(what_value, text_type, parent_id, deconstruction_dimensions)

        # Step 3: 根据描述维度分析文本
        description_values = self._analyze_text(current_text, description_dimensions, text_type)

        # Step 4: 先用LLM切分文本（执行分割）
        # PRD变更：先分割，再用代码判断是否需要递归
        sub_texts = self._llm_split_text(current_text, text_type, depth)

        # Step 5: 基于分割结果用代码判断是否需要继续递归
        # 判断原则：如果切分出2个或更多子文本，则需要递归
        should_split = len(sub_texts) >= 2

        logger.info(f"切分结果 - 子文本数量: {len(sub_texts)}, 是否继续递归: {should_split}")

        # Step 5: 构建结果
        # 计算权重并获取理由
        weight_info = self._calculate_importance(description_values, what_value, text_type)

        result = {
            "what": what_value,
            "description": description_values,
            "relationships": [],
            "importance_weight": weight_info.get("score", 0.5) if isinstance(weight_info, dict) else weight_info,
            "weight_reason": weight_info.get("reason", "") if isinstance(weight_info, dict) else "",
            "children": []
        }

        # Step 6: 如需递归，处理子文本元素
        if should_split and depth < self.max_depth - 1:
            children = []
            for idx, sub_text in enumerate(sub_texts):
                if not sub_text or len(sub_text.strip()) == 0:
                    logger.warning(f"子元素 {idx + 1} 为空，跳过")
                    continue

                child_state = {
                    "current_text": sub_text.strip(),
                    "text_type": text_type,
                    "parent_id": f"{parent_id}_{idx + 1}",
                    "depth": depth + 1,
                    "deconstruction_dimensions": deconstruction_dimensions  # 传递解构维度
                }

                logger.info(f"递归处理子元素 {idx + 1}/{len(sub_texts)}")
                child_result = self.process(child_state)
                children.append(child_result)

            result["children"] = children
            result["relationships"] = self._extract_relationships(children)

        logger.info(f"文本解构完成 - 深度: {depth}, 子元素数: {len(result.get('children', []))}")

        return result

    def _get_description_dimensions(
        self,
        text: str,
        text_type: str,
        parent_id: str = "",
        deconstruction_dimensions: List[Dict[str, Any]] = None
    ) -> List[str]:
        """使用大模型获取描述维度，优先考虑解构维度

        Args:
            text: 文本内容
            text_type: 文本类型（title/body/hashtags）
            parent_id: 父节点ID（如"title", "body", "hashtags"）
            deconstruction_dimensions: 解构维度列表

        Returns:
            描述维度列表
        """
        deconstruction_dimensions = deconstruction_dimensions or []

        # 如果有解构维度，使用LLM筛选相关维度并生成补充维度（合并为一步）
        if deconstruction_dimensions and text:
            dimensions = self._llm_generate_dimensions_with_filter(text, text_type, deconstruction_dimensions)
            if dimensions:
                return dimensions

        # 如果没有解构维度或筛选失败，使用原有逻辑（LLM生成）
        # 限制文本长度用于query
        text_preview = text[:500] if len(text) > 500 else text

        try:
            prompt = f"""请分析以下{text_type}类型的文本，列出用于刻画描述其核心特征的角度和维度。

文本内容：
{text_preview}

要求：
1. 提取3-6个最核心、最有价值的描述维度
2. 维度名称要简洁明了（2-5个字）
3. 维度应该具体可操作，能够用于分析文本
4. 尽可能不重不漏

请输出JSON格式：
{{
    "dimensions": ["维度1", "维度2", "维度3", ...]
}}
"""

            messages = [
                {"role": "system", "content": self.system_prompt},
                {"role": "user", "content": prompt}
            ]

            if not self.is_initialized:
                self.initialize()

            response = self.model.invoke(messages)
            content = response.content.strip()

            # 提取JSON
            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)
            dimensions = result.get("dimensions", [])

            # 验证维度列表
            if dimensions and len(dimensions) >= 3:
                logger.info(f"获取到 {len(dimensions)} 个描述维度，维度列表: {dimensions}")
                return dimensions
            else:
                logger.warning("LLM返回的维度列表不符合要求，使用默认维度")
                return self._get_default_dimensions(text_type)

        except Exception as e:
            logger.error(f"获取描述维度失败: {e}")
            return self._get_default_dimensions(text_type)

    def _llm_generate_dimensions_with_filter(
        self,
        text: str,
        text_type: str,
        deconstruction_dimensions: List[Dict[str, Any]]
    ) -> List[str]:
        """使用LLM从解构维度中筛选相关维度并生成补充维度（一步完成）

        Args:
            text: 当前文本内容
            text_type: 文本类型（title/body/hashtags）
            deconstruction_dimensions: 所有解构维度列表

        Returns:
            完整的维度列表（筛选后的相关维度 + 补充维度）
        """
        try:
            # 构建维度信息列表
            dimensions_info = []
            for idx, dimension in enumerate(deconstruction_dimensions):
                dimension_name = dimension.get("dimension_name", "")
                dimension_description = dimension.get("dimension_description", "")

                if dimension_name:
                    dimensions_info.append({
                        "index": idx + 1,
                        "dimension_name": dimension_name,
                        "dimension_description": dimension_description
                    })

            if not dimensions_info:
                logger.info("没有可用的解构维度")
                return []

            # 构建维度信息文本
            dimensions_text = "\n".join([
                f"{d['index']}. 维度: {d['dimension_name']}\n   说明: {d['dimension_description']}"
                for d in dimensions_info
            ])

            # 限制文本长度
            text_preview = text[:500] if len(text) > 500 else text

            prompt = f"""请分析以下文本元素，完成两个任务：
1. 从提供的解构维度中筛选出与当前元素相关的维度
2. 在此基础上，补充其他重要的描述维度

**当前要分析的{text_type}类型文本：**
{text_preview}

**可选的解构维度列表：**
{dimensions_text}

**任务1：筛选相关维度**
- 分析当前文本元素的内容和特征
- 逐一判断每个维度是否与当前文本元素相关
- 判断标准：
  * 维度名称（dimension_name）是否适用于当前元素
  * 维度说明（dimension_description）中描述的内容是否有助于分析当前元素

**任务2：生成补充维度**
- 在筛选出的相关维度基础上
- 补充其他重要的描述维度
- 总共提取5-8个描述维度
- 维度名称要简洁明了（2-5个字）
- 维度应该具体可操作，能够用于分析文本

**输出要求：**
请输出JSON格式，包含完整的维度列表：
{{
    "dimensions": ["维度1", "维度2", "维度3", ...],
    "relevant_count": 筛选出的相关维度数量,
    "reason": "简要说明筛选和补充的理由"
}}

**重要**：dimensions数组中应包含所有维度（筛选的相关维度 + 补充的新维度）"""

            messages = [
                {"role": "system", "content": self.system_prompt},
                {"role": "user", "content": prompt}
            ]

            if not self.is_initialized:
                self.initialize()

            response = self.model.invoke(messages)
            content = response.content.strip()

            # 提取JSON
            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)
            dimensions = result.get("dimensions", [])
            relevant_count = result.get("relevant_count", 0)
            reason = result.get("reason", "未提供理由")

            if dimensions and len(dimensions) >= 3:
                logger.info(
                    f"LLM生成维度 - 总数: {len(dimensions)}, "
                    f"相关维度: {relevant_count}, 理由: {reason}"
                )
                return dimensions
            else:
                logger.warning(f"LLM返回的维度列表不符合要求，使用默认维度")
                return []

        except Exception as e:
            logger.error(f"LLM筛选并生成维度失败: {e}")
            return []

    def _get_default_dimensions(self, text_type: str) -> List[str]:
        """获取默认描述维度"""
        if text_type == "title":
            return ["核心主题", "吸引力", "关键词", "情感倾向"]
        elif text_type == "hashtags":
            return ["话题类型", "热度", "相关性"]
        else:  # body
            return ["核心内容", "结构", "语言风格", "信息密度", "价值点"]


    def _analyze_text(self, text: str, dimensions: List[str], text_type: str) -> Dict[str, Any]:
        """根据维度分析文本

        Args:
            text: 文本内容
            dimensions: 描述维度列表
            text_type: 文本类型

        Returns:
            描述值字典
        """
        # 构建分析prompt
        prompt = f"""请分析以下{text_type}文本，按照给定维度提取特征：

文本内容：
{text}

分析维度：
{chr(10).join(f'- {dim}' for dim in dimensions)}

请输出JSON格式：
{{
    "维度1": "值1",
    "维度2": "值2",
    ...
}}
"""

        try:
            # 调用LLM分析
            messages = [
                {"role": "system", "content": self.system_prompt},
                {"role": "user", "content": prompt}
            ]

            if not self.is_initialized:
                self.initialize()

            response = self.model.invoke(messages)
            content = response.content.strip()

            # 提取JSON
            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)
            return result

        except Exception as e:
            logger.error(f"文本分析失败: {e}")
            # 返回空描述
            return {dim: "分析失败" for dim in dimensions}

    def _calculate_importance(
        self,
        description: Dict[str, Any],
        what_value: str = "",
        text_type: str = "body"
    ) -> Dict[str, Any]:
        """使用大模型计算元素重要性权重

        权重策略：
        - 节点权重值：大于0且小于等于1的数值
        - 衡量该节点的内容对读者观众的吸引力大小
        - 帖子中越是亮点、越是吸引观众关注的元素节点，权重值越大

        注意：同一父节点下的各个子节点的权重值之和应固定为1
        （归一化在 _split_and_recurse 方法中统一处理）

        Args:
            description: 节点的详细描述
            what_value: 文本原文
            text_type: 文本类型

        Returns:
            包含score和reason的字典：{"score": 吸引力得分, "reason": "评分理由"}
        """
        return {"score": 0.5, "reason": "默认权重"}

    def _llm_split_text(
        self,
        text: str,
        text_type: str,
        depth: int
    ) -> List[str]:
        """使用LLM进行智能文本切分

        Args:
            text: 文本内容
            text_type: 文本类型
            depth: 当前深度

        Returns:
            切分后的子文本列表
        """
        try:
            # 根据文本类型提供切分指导
            if text_type == "title":
                split_guidance = """标题文本切分指导：
- 一般不需要切分，除非标题过长且包含多个独立主题
- 可以按照标点符号（如顿号、分号）切分
- 保持每个子标题的语义完整性
"""
            elif text_type == "hashtags":
                split_guidance = """标签文本切分指导：
- 按照标签分隔符（如#、空格、逗号）切分
- 每个子元素应该是一个完整的标签
- 保持标签的独立性
"""
            else:  # body
                split_guidance = """正文文本切分指导：
- 优先按照段落分隔符（\\n\\n）切分
- 如果段落过长，可以按照句子分隔符（。！？）切分
- 每个子元素应该是语义完整的段落或句子
- 避免在关键词、短语中间切分
- 保持逻辑连贯性
"""

            prompt = f"""请根据切分指导，将以下文本切分成多个子元素。

**文本类型：** {text_type}
**当前深度：** {depth}

**原始文本：**
{text}

**切分指导：**
{split_guidance}

**切分要求：**
1. 根据文本类型和切分指导，选择合适的切分点
2. 保证每个子元素的语义完整性
3. 避免过度切分，每个子元素应该有独立的意义
4. 如果文本无需切分或已经足够简单，返回包含原文的单元素列表
5. 切分后的子元素必须保持原文内容，不可改写或省略

**输出格式：**
请输出JSON格式，包含切分后的子文本列表：
{{
    "sub_texts": [
        "子文本1（原文内容，不可改写）",
        "子文本2（原文内容，不可改写）",
        ...
    ],
    "split_reason": "切分理由（简要说明为什么这样切分）"
}}

**重要提示：** 子文本必须是原文的精确摘取，不允许改写、总结或省略！
"""

            messages = [
                {"role": "system", "content": self.system_prompt},
                {"role": "user", "content": prompt}
            ]

            if not self.is_initialized:
                self.initialize()

            response = self.model.invoke(messages)
            content = response.content.strip()

            # 提取JSON
            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)
            sub_texts = result.get("sub_texts", [])
            split_reason = result.get("split_reason", "未提供理由")

            if not sub_texts:
                logger.warning("LLM未返回有效切分结果")
                return []

            logger.info(f"LLM切分 - 子文本数: {len(sub_texts)}, 理由: {split_reason}")

            # 验证切分结果的合理性
            total_length = sum(len(s) for s in sub_texts)
            original_length = len(text)

            # 如果切分后的总长度与原文长度差异过大，说明可能有问题
            if abs(total_length - original_length) > original_length * 0.3:
                logger.warning(f"LLM切分结果长度异常（原文:{original_length}, 切分后:{total_length}），使用降级策略")
                return []

            return sub_texts

        except Exception as e:
            logger.error(f"LLM切分失败: {e}")
            return []

    def _extract_relationships(self, children: List[Dict[str, Any]]) -> List[str]:
        """提取子节点之间的关系

        使用LLM根据子元素的what和description，智能分析子节点之间的关系

        Args:
            children: 子节点列表

        Returns:
            关系描述列表
        """
        if not children:
            return []

        if len(children) == 1:
            return ["单一子元素"]

        try:
            # 构建子元素信息摘要
            children_summary = []
            for idx, child in enumerate(children):
                what = child.get("what", "")
                desc = child.get("description", {})

                # 限制what的长度
                what_preview = what + "..." if len(what) > 100 else what

                # 提取关键描述信息
                desc_summary = ", ".join([f"{k}: {v}" for k, v in list(desc.items())])

                children_summary.append(f"子元素{idx + 1}: {what_preview}\n  描述: {desc_summary}")

            children_text = "\n\n".join(children_summary)

            # 构建LLM prompt
            prompt = f"""请分析以下子元素之间的关系，识别它们的组织结构、逻辑关系、以及整体特征。

**子元素列表（共{len(children)}个）：**
{children_text}

**分析要求：**
1. 识别子元素之间的逻辑关系（如：并列、递进、因果、对比、总分等）
2. 分析子元素的组织结构（如：时间顺序、空间顺序、重要性排序等）
3. 总结子元素的整体特征（如：风格统一、主题一致、层次分明等）
4. 每个关系描述要简洁明了，控制在10-20字

**输出格式：**
请输出JSON格式，包含3-5个关系描述：
{{
    "relationships": [
        "关系描述1",
        "关系描述2",
        "关系描述3",
        ...
    ]
}}

示例输出：
{{
    "relationships": [
        "子元素按时间顺序排列",
        "前后元素存在因果关系",
        "整体呈现递进式论述结构",
        "各元素围绕同一主题展开"
    ]
}}
"""

            messages = [
                {"role": "system", "content": self.system_prompt},
                {"role": "user", "content": prompt}
            ]

            if not self.is_initialized:
                self.initialize()

            response = self.model.invoke(messages)
            content = response.content.strip()

            # 提取JSON
            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)
            relationships = result.get("relationships", [])

            if relationships and len(relationships) > 0:
                logger.info(f"LLM提取到 {len(relationships)} 个子节点关系")
                return relationships
            else:
                logger.warning("LLM未返回有效关系，使用默认关系")
                return self._get_default_relationships(len(children))

        except Exception as e:
            logger.error(f"提取子节点关系失败: {e}")
            return self._get_default_relationships(len(children))

    def _get_default_relationships(self, num_children: int) -> List[str]:
        """获取默认关系描述

        Args:
            num_children: 子节点数量

        Returns:
            默认关系列表
        """
        return [
            f"包含 {num_children} 个子元素",
            "子元素按顺序排列",
            "子元素共同构成完整内容"
        ]

    def _build_messages(self, state: WhatDeconstructionState) -> List[Dict[str, Any]]:
        """构建消息（继承自BaseLLMAgent，但在process中不使用）"""
        return []

    def _update_state(self, state: WhatDeconstructionState, response: Any) -> Dict[str, Any]:
        """更新状态（继承自BaseLLMAgent，但在process中不使用）"""
        return {}
