"""
报告生成器
负责基于 AI 审查结果生成审查报告摘要
"""

import asyncio
import json
import time
import os
from typing import Dict, List, Any, Optional, Callable
from dataclasses import dataclass
from datetime import datetime

from foundation.observability.logger.loggering import review_logger as logger
from foundation.ai.agent.generate.model_generate import generate_model_client
from core.construction_review.component.reviewers.utils.prompt_loader import PromptLoader

@dataclass
class FinalReport:
    """最终报告"""
    file_id: str
    file_name: str
    total_issues: int
    high_risk_count: int
    medium_risk_count: int
    low_risk_count: int
    overall_assessment: str
    high_risk_summary: List[str]
    medium_risk_summary: List[str]
    improvement_recommendations: List[str]
    risk_alerts: List[str]
    generated_at: datetime

class ReportGenerator:
    """报告生成器 - 基于高中风险问题使用 LLM 生成摘要"""

    def __init__(self):
        """初始化报告生成器"""
        self.model_client = generate_model_client

        # 获取提示词加载器实例（使用全局单例）
        self.prompt_loader = PromptLoader()

        # 临时修改 prompt_config_dir 以加载 report 类型的 prompt
        current_dir = os.path.dirname(os.path.abspath(__file__))
        prompt_dir = os.path.join(current_dir, 'report', 'prompt')
        original_prompt_dir = self.prompt_loader.prompt_config_dir

        try:
            # 临时切换到 report prompt 目录
            self.prompt_loader.prompt_config_dir = prompt_dir

            # 手动加载 report 类型的 prompt
            self.prompt_loader._load_prompt('report', 'report_summary')
            logger.info(f"成功加载 report_summary prompt，目录：{prompt_dir}")

        except Exception as e:
            logger.error(f"加载 report_summary prompt 失败：{str(e)}", exc_info=True)
        finally:
            # 恢复原始目录
            self.prompt_loader.prompt_config_dir = original_prompt_dir

        logger.info(f"报告生成器初始化完成")

    async def generate_report(self, file_id: str, file_name: str, review_results: Dict[str, Any],
                            progress_callback: Optional[Callable[[int, str], None]] = None) -> FinalReport:
        """
        生成审查报告（基于高中风险问题）

        Args:
            file_id: 文件 ID
            file_name: 文件名称
            review_results: AI 审查结果（包含 issues 列表）
            progress_callback: 进度回调函数

        Returns:
            FinalReport: 最终报告
        """
        start_time = time.time()

        try:
            logger.info(f"开始生成报告，文件 ID: {file_id}, 文件名：{file_name}")

            if progress_callback:
                progress_callback(20, "提取高中风险问题")

            # 1. 提取高中风险问题
            high_risk_issues, medium_risk_issues, low_risk_count = await self._extract_high_medium_risk_issues(review_results)
            total_issues = len(high_risk_issues) + len(medium_risk_issues) + low_risk_count

            logger.info(f"问题统计 - 高风险：{len(high_risk_issues)}, 中风险：{len(medium_risk_issues)}, 低风险：{low_risk_count}, 总计：{total_issues}")

            if progress_callback:
                progress_callback(50, "调用 LLM 生成报告摘要")

            # 2. 调用 LLM 生成摘要（带降级处理）
            parsed_summary = None
            try:
                llm_summary = await self._call_llm_for_summary(
                    file_name=file_name,
                    high_risk_issues=high_risk_issues,
                    medium_risk_issues=medium_risk_issues,
                    total_issues=total_issues,
                    trace_id=file_id
                )

                if progress_callback:
                    progress_callback(80, "解析 LLM 响应")

                # 3. 解析 LLM 响应
                parsed_summary = self._parse_llm_response(llm_summary)
            except Exception as llm_error:
                # LLM 调用失败，使用降级方案生成默认摘要
                logger.warning(f"LLM 生成摘要失败，使用降级方案：{str(llm_error)}")
                if progress_callback:
                    progress_callback(70, "LLM 服务不可用，使用默认摘要")
                parsed_summary = self._generate_fallback_summary(
                    file_name=file_name,
                    high_risk_issues=high_risk_issues,
                    medium_risk_issues=medium_risk_issues,
                    total_issues=total_issues
                )

            if progress_callback:
                progress_callback(100, "报告生成完成")

            # 4. 构建最终报告
            final_report = FinalReport(
                file_id=file_id,
                file_name=file_name,
                total_issues=total_issues,
                high_risk_count=len(high_risk_issues),
                medium_risk_count=len(medium_risk_issues),
                low_risk_count=low_risk_count,
                overall_assessment=parsed_summary.get("overall_assessment", ""),
                high_risk_summary=parsed_summary.get("high_risk_summary", []),
                medium_risk_summary=parsed_summary.get("medium_risk_summary", []),
                improvement_recommendations=parsed_summary.get("improvement_recommendations", []),
                risk_alerts=parsed_summary.get("risk_alerts", []),
                generated_at=datetime.now()
            )

            execution_time = time.time() - start_time
            logger.info(f"报告生成完成，文件 ID: {file_id}, 耗时：{execution_time:.2f}秒")

            return final_report

        except Exception as e:
            logger.error(f"报告生成失败：{str(e)}", exc_info=True)
            # 即使是意外错误，也尝试返回一个基本的报告，而不是让任务完全失败
            try:
                logger.warning(f"尝试生成应急报告...")
                high_risk_issues, medium_risk_issues, low_risk_count = await self._extract_high_medium_risk_issues(review_results)
                total_issues = len(high_risk_issues) + len(medium_risk_issues) + low_risk_count
                parsed_summary = self._generate_fallback_summary(
                    file_name=file_name,
                    high_risk_issues=high_risk_issues,
                    medium_risk_issues=medium_risk_issues,
                    total_issues=total_issues,
                    is_emergency=True
                )
                final_report = FinalReport(
                    file_id=file_id,
                    file_name=file_name,
                    total_issues=total_issues,
                    high_risk_count=len(high_risk_issues),
                    medium_risk_count=len(medium_risk_issues),
                    low_risk_count=low_risk_count,
                    overall_assessment=parsed_summary.get("overall_assessment", ""),
                    high_risk_summary=parsed_summary.get("high_risk_summary", []),
                    medium_risk_summary=parsed_summary.get("medium_risk_summary", []),
                    improvement_recommendations=parsed_summary.get("improvement_recommendations", []),
                    risk_alerts=parsed_summary.get("risk_alerts", []),
                    generated_at=datetime.now()
                )
                logger.info(f"应急报告生成完成，文件 ID: {file_id}")
                return final_report
            except Exception as fallback_error:
                logger.error(f"应急报告生成也失败：{str(fallback_error)}")
                raise

    async def _extract_high_medium_risk_issues(self, review_results: Dict[str, Any]) -> tuple:
        """
        从审查结果中提取高中风险问题

        Args:
            review_results: AI 审查结果

        Returns:
            tuple: (high_risk_issues, medium_risk_issues, low_risk_count)
        """
        high_risk_issues = []
        medium_risk_issues = []
        low_risk_count = 0

        # 获取 issues 列表（键名为 'review_results'）
        issues = review_results.get('review_results', [])

        logger.info(f"开始提取风险问题，issues 数量：{len(issues)}")

        for issue_wrapper in issues:
            # issue_wrapper 是一个字典，key 是问题 ID，value 是问题详情
            for issue_id, issue_detail in issue_wrapper.items():
                # 获取 review_lists
                review_lists = issue_detail.get('review_lists', [])

                # 获取元数据
                metadata = issue_detail.get('metadata', {})
                location = metadata.get('review_location_label', '未知位置')

                for review_item in review_lists:
                    # 只处理 exist_issue=True 的项
                    if not review_item.get('exist_issue', False):
                        continue

                    # 获取检查结果（check_result 可能是字典或字符串）
                    check_result = review_item.get('check_result', {})

                    # 如果 check_result 是字符串，转换为字典格式
                    if isinstance(check_result, str):
                        check_result = {'issue_point': check_result}
                    elif not isinstance(check_result, dict):
                        check_result = {}

                    # 获取风险级别
                    risk_level_raw = check_result.get('risk_level', '').lower() if isinstance(check_result, dict) else ''

                    # 统一风险级别格式（支持"高风险"、"high"等多种格式）
                    if '高' in risk_level_raw or 'high' in risk_level_raw:
                        risk_level = 'high'
                    elif '中' in risk_level_raw or 'medium' in risk_level_raw:
                        risk_level = 'medium'
                    elif '低' in risk_level_raw or 'low' in risk_level_raw:
                        risk_level = 'low'
                    else:
                        # 默认按 risk_info 判断
                        risk_info = review_item.get('risk_info', {})
                        if isinstance(risk_info, dict):
                            risk_level = risk_info.get('risk_level', 'low')
                        else:
                            risk_level = 'low'

                    # 获取检查项类型和章节代码
                    check_item = review_item.get('check_item', 'unknown')
                    check_item_code = review_item.get('check_item_code', '')

                    # 从 check_item_code 中提取 chapter_code (格式：chapter_code_check_item)
                    chapter_code = check_item_code.rsplit('_', 1)[0] if '_' in check_item_code else 'unknown'

                    # 构建问题对象（只包含分析结果，不包含原文材料）
                    # 按照指定格式组织数据结构
                    issue_obj = {
                        'check_item': check_item,
                        'chapter_code': chapter_code,
                        'check_item_code': check_item_code,
                        'check_result': {
                            'issue_point': check_result.get('issue_point', '') if isinstance(check_result, dict) else str(check_result),
                            'location': check_result.get('location', location),
                            'suggestion': check_result.get('suggestion', ''),
                            'reason': check_result.get('reason', ''),
                            'risk_level': check_result.get('risk_level', risk_level)
                        }
                        # 注意：不包含 reference_source（原文材料），只传入分析结果
                    }

                    # 按风险级别分类（只处理高风险和中风险）
                    if risk_level == 'high':
                        high_risk_issues.append(issue_obj)
                    elif risk_level == 'medium':
                        medium_risk_issues.append(issue_obj)
                    elif risk_level == 'low':
                        low_risk_count += 1

        logger.info(f"风险问题提取完成 - 高风险：{len(high_risk_issues)}, 中风险：{len(medium_risk_issues)}, 低风险：{low_risk_count}")

        return high_risk_issues, medium_risk_issues, low_risk_count

    async def _call_llm_for_summary(self, file_name: str, high_risk_issues: List[Dict],
                                   medium_risk_issues: List[Dict], total_issues: int, trace_id: str) -> str:
        """
        调用 LLM 生成报告摘要（只处理高中风险问题，不传入原文材料）

        Args:
            file_name: 文件名称
            high_risk_issues: 高风险问题列表
            medium_risk_issues: 中风险问题列表
            total_issues: 总问题数
            trace_id: 追踪 ID

        Returns:
            str: LLM 生成的摘要文本（JSON 格式）
        """
        try:
            # 限制问题数量，避免 prompt 过长导致超时
            # 高风险问题最多取 10 个，中风险问题最多取 15 个
            MAX_HIGH_RISK = 10
            MAX_MEDIUM_RISK = 15

            if len(high_risk_issues) > MAX_HIGH_RISK:
                logger.warning(f"高风险问题数量过多 ({len(high_risk_issues)})，仅取前{MAX_HIGH_RISK}个")
                high_risk_issues = high_risk_issues[:MAX_HIGH_RISK]

            if len(medium_risk_issues) > MAX_MEDIUM_RISK:
                logger.warning(f"中风险问题数量过多 ({len(medium_risk_issues)})，仅取前{MAX_MEDIUM_RISK}个")
                medium_risk_issues = medium_risk_issues[:MAX_MEDIUM_RISK]

            # 构建问题清单文本
            high_risk_text = self._format_issues_text(high_risk_issues)
            medium_risk_text = self._format_issues_text(medium_risk_issues)

            # 准备提示词参数
            prompt_kwargs = {
                "file_name": file_name,
                "high_risk_count": len(high_risk_issues),
                "medium_risk_count": len(medium_risk_issues),
                "total_issues": total_issues,
                "high_risk_issues_text": high_risk_text,
                "medium_risk_issues_text": medium_risk_text
            }

            # 获取提示词模板
            task_prompt_info = {
                "task_prompt": self.prompt_loader.get_prompt_template("report", "report_summary", **prompt_kwargs),
                "task_name": "报告摘要生成"
            }

            logger.debug(f"开始调用 LLM 生成报告摘要")

            # 调用模型 - 增加超时阈值到 180 秒（3 分钟），因为报告生成需要更多时间
            # 使用 function_name 从 model_setting.yaml 加载模型配置
            model_response = await self.model_client.get_model_generate_invoke(
                trace_id=trace_id,
                task_prompt_info=task_prompt_info,
                timeout=180,
                function_name="completeness_review_generate"
            )

            logger.info(f"LLM 摘要生成成功，响应长度：{len(model_response)} 字符")

            return model_response

        except Exception as e:
            logger.error(f"调用 LLM 生成摘要失败：{str(e)}", exc_info=True)
            # 抛出异常让上层处理降级
            raise

    def _format_issues_text(self, issues: List[Dict]) -> str:
        """
        格式化问题列表为文本（按 check_item 分组，只包含分析结果）

        Args:
            issues: 问题列表（每个问题包含 check_item, chapter_code, check_item_code, check_result）

        Returns:
            str: 格式化后的问题文本
        """
        if not issues:
            return "（无）"

        # 按 check_item 分组
        from collections import defaultdict
        grouped_issues = defaultdict(list)
        for issue in issues:
            check_item = issue.get('check_item', 'unknown')
            grouped_issues[check_item].append(issue)

        # 检查项名称映射（中文）
        check_item_names = {
            'timeliness_check': '时效性审查',
            'completeness_check': '完整性审查',
            'semantic_logic_check': '语义逻辑审查',
            'reference_check': '参考文献审查',
            'sensitive_word_check': '敏感词审查',
            'mandatory_standards_check': '强制性标准审查',
            'technical_parameters_check': '技术参数审查',
            'design_values_check': '设计值审查',
            'rag_enhanced_review': 'RAG 增强审查',
            'professional_suggestion': '专业建议',
            'unknown': '其他审查'
        }

        result = []
        for check_item, item_issues in sorted(grouped_issues.items()):
            # 添加分组标题
            check_item_name = check_item_names.get(check_item, check_item)
            result.append(f"## 【{check_item_name}】({len(item_issues)}个问题)")
            result.append("")

            # 添加该组的所有问题（只传入分析结果，不包含原文材料）
            for idx, issue in enumerate(item_issues, 1):
                check_result = issue.get('check_result', {})
                result.append(f"### 问题 {idx}")
                result.append(f"- 章节：{issue.get('chapter_code', '未知')}")
                result.append(f"- 位置：{check_result.get('location', '未知')}")
                result.append(f"- 问题点：{check_result.get('issue_point', '未知')}")
                result.append(f"- 建议：{check_result.get('suggestion', '无')}")
                result.append(f"- 依据：{check_result.get('reason', '无')}")
                # 注意：不包含 reference_source（原文材料）
                result.append("")

            result.append("")

        return "\n".join(result)

    def _generate_fallback_summary(self, file_name: str, high_risk_issues: List[Dict],
                                   medium_risk_issues: List[Dict], total_issues: int,
                                   is_emergency: bool = False) -> Dict[str, Any]:
        """
        生成降级摘要（当 LLM 调用失败时使用）

        Args:
            file_name: 文件名称
            high_risk_issues: 高风险问题列表
            medium_risk_issues: 中风险问题列表
            total_issues: 总问题数
            is_emergency: 是否为应急模式（更简化的输出）

        Returns:
            Dict: 摘要字典
        """
        high_count = len(high_risk_issues)
        medium_count = len(medium_risk_issues)

        # 生成总体评价
        if is_emergency:
            overall = f"【应急报告】文档《{file_name}》的审查已完成，但在生成摘要时遇到技术问题。以下是基于原始审查结果的问题统计。"
        else:
            overall = f"文档《{file_name}》的审查已完成。共发现 {total_issues} 个问题，其中高风险 {high_count} 个，中风险 {medium_count} 个。"

        # 生成高风险摘要
        high_risk_summary = []
        if high_risk_issues:
            for i, issue in enumerate(high_risk_issues[:5], 1):
                check_result = issue.get('check_result', {})
                issue_point = check_result.get('issue_point', '未知问题')
                location = check_result.get('location', '未知位置')
                high_risk_summary.append(f"{i}. 【{location}】{issue_point}")
            if len(high_risk_issues) > 5:
                high_risk_summary.append(f"... 及其他 {len(high_risk_issues) - 5} 个高风险问题")
        else:
            high_risk_summary.append("未发现高风险问题")

        # 生成中风险摘要
        medium_risk_summary = []
        if medium_risk_issues:
            for i, issue in enumerate(medium_risk_issues[:5], 1):
                check_result = issue.get('check_result', {})
                issue_point = check_result.get('issue_point', '未知问题')
                location = check_result.get('location', '未知位置')
                medium_risk_summary.append(f"{i}. 【{location}】{issue_point}")
            if len(medium_risk_issues) > 5:
                medium_risk_summary.append(f"... 及其他 {len(medium_risk_issues) - 5} 个中风险问题")
        else:
            medium_risk_summary.append("未发现中风险问题")

        # 生成改进建议
        recommendations = []
        if high_count > 0:
            recommendations.append(f"优先处理 {high_count} 个高风险问题")
        if medium_count > 0:
            recommendations.append(f"建议处理 {medium_count} 个中风险问题")
        if not recommendations:
            recommendations.append("文档整体质量良好，继续保持")

        # 添加技术提示
        if is_emergency:
            recommendations.append("【注意】报告摘要生成服务暂时不可用，以上为自动生成的简要统计")

        # 生成风险预警
        risk_alerts = []
        if high_count > 5:
            risk_alerts.append(f"⚠️ 高风险问题较多（{high_count}个），建议立即整改")
        elif high_count > 0:
            risk_alerts.append(f"⚠️ 发现 {high_count} 个高风险问题，需要关注")
        if medium_count > 10:
            risk_alerts.append(f"⚠️ 中风险问题较多（{medium_count}个），建议尽快处理")

        logger.info(f"降级摘要生成完成 - 高风险：{high_count}, 中风险：{medium_count}")

        return {
            "overall_assessment": overall,
            "high_risk_summary": high_risk_summary,
            "medium_risk_summary": medium_risk_summary,
            "improvement_recommendations": recommendations,
            "risk_alerts": risk_alerts if risk_alerts else ["无特别风险预警"]
        }

    def _parse_llm_response(self, llm_response: str) -> Dict[str, Any]:
        """
        解析 LLM 响应

        Args:
            llm_response: LLM 返回的 JSON 字符串

        Returns:
            Dict: 解析后的摘要字典
        """
        try:
            # 移除可能的 markdown 代码块标记
            cleaned_response = llm_response.strip()
            if cleaned_response.startswith('```json'):
                cleaned_response = cleaned_response[7:]
            if cleaned_response.startswith('```'):
                cleaned_response = cleaned_response[3:]
            if cleaned_response.endswith('```'):
                cleaned_response = cleaned_response[:-3]

            cleaned_response = cleaned_response.strip()

            # 解析 JSON
            parsed = json.loads(cleaned_response)

            # 验证必需字段
            required_fields = [
                "overall_assessment",
                "high_risk_summary",
                "medium_risk_summary",
                "improvement_recommendations",
                "risk_alerts"
            ]

            for field in required_fields:
                if field not in parsed:
                    logger.warning(f"LLM 响应缺少必需字段：{field}，使用默认值")
                    if field == "overall_assessment":
                        parsed[field] = "无法生成总体评价"
                    else:
                        parsed[field] = []

            logger.info("LLM 响应解析成功")
            return parsed

        except json.JSONDecodeError as e:
            logger.error(f"解析 LLM 响应失败：{str(e)}")
            logger.error(f"原始响应：{llm_response[:500]}...")

            # 返回默认结构
            return {
                "overall_assessment": "报告生成失败，请重试",
                "high_risk_summary": [],
                "medium_risk_summary": [],
                "improvement_recommendations": [],
                "risk_alerts": []
            }
        except Exception as e:
            logger.error(f"解析 LLM 响应时发生未知错误：{str(e)}", exc_info=True)
            # 返回默认结构，不让解析错误导致整个任务失败
            return {
                "overall_assessment": "报告解析失败，请查看详细问题列表",
                "high_risk_summary": [],
                "medium_risk_summary": [],
                "improvement_recommendations": ["请重试生成报告以获取完整摘要"],
                "risk_alerts": ["报告生成过程中发生错误"]
            }
