"""
Review Manager

审核管理器 - 负责执行内容审核，整合多个审核维度
"""
import json
import uuid
from typing import List, Dict, Any, Optional
from datetime import datetime

from app.models.review import (
    ReviewConfig,
    ReviewResult,
    ReviewIssue,
    DimensionScore,
    DimensionType,
    SeverityLevel,
    IssueType,
    Location,
    CustomRule
)
from app.models.project import SeriesProject, Episode
from app.core.llm.glm_client import GLMClient
from app.core.skills.skill_manager import SkillManager
from app.utils.logger import get_logger


logger = get_logger(__name__)


class ReviewManager:
    """
    审核管理器

    核心功能：
    1. 执行完整的审核流程
    2. 调用多个审核Skill
    3. 聚合各维度结果
    4. 检查自定义规则
    5. 计算总分
    """

    # 内置维度定义
    DIMENSIONS = {
        DimensionType.consistency: {
            "name": "一致性审核",
            "description": "检查人物、场景、剧情的前后一致性",
            "default_strictness": 0.7,
            "skill_ids": ["consistency_checker"]
        },
        DimensionType.character_consistency: {
            "name": "角色一致性审核",
            "description": "检查角色性格、行为、对话风格的前后一致性",
            "default_strictness": 0.7,
            "skill_ids": ["consistency_checker"]
        },
        DimensionType.plot_coherence: {
            "name": "剧情连贯性审核",
            "description": "检查剧情逻辑、时间线、因果关系的一致性",
            "default_strictness": 0.7,
            "skill_ids": []
        },
        DimensionType.dialogue_quality: {
            "name": "对话质量审核",
            "description": "检查对话的自然度、角色声音的区分度",
            "default_strictness": 0.6,
            "skill_ids": []
        },
        DimensionType.quality: {
            "name": "质量审核",
            "description": "检查文字质量、表达流畅度",
            "default_strictness": 0.6,
            "skill_ids": []
        },
        DimensionType.pacing: {
            "name": "节奏审核",
            "description": "检查剧情节奏、叙事速度",
            "default_strictness": 0.5,
            "skill_ids": []
        },
        DimensionType.dialogue: {
            "name": "对话审核",
            "description": "检查对话质量和人物口吻",
            "default_strictness": 0.7,
            "skill_ids": []
        },
        DimensionType.character: {
            "name": "人物审核",
            "description": "检查人物塑造和性格表现",
            "default_strictness": 0.6,
            "skill_ids": []
        },
        DimensionType.plot: {
            "name": "剧情审核",
            "description": "检查剧情逻辑和完整性",
            "default_strictness": 0.7,
            "skill_ids": []
        },
        DimensionType.emotional_depth: {
            "name": "情感深度审核",
            "description": "检查情感表达的真实性、感染力",
            "default_strictness": 0.7,
            "skill_ids": []
        },
        DimensionType.thematic_strength: {
            "name": "主题强度审核",
            "description": "检查主题表达的一致性、深度",
            "default_strictness": 0.7,
            "skill_ids": []
        },
        DimensionType.custom: {
            "name": "自定义审核",
            "description": "用户自定义的审核维度",
            "default_strictness": 0.5,
            "skill_ids": []
        }
    }

    def __init__(
        self,
        glm_client: Optional[GLMClient] = None,
        skill_manager: Optional[SkillManager] = None
    ):
        """
        初始化审核管理器

        Args:
            glm_client: GLM客户端实例
            skill_manager: Skill管理器实例
        """
        self.glm_client = glm_client
        self.skill_manager = skill_manager

        if not self.glm_client:
            from app.core.llm.glm_client import get_glm_client
            self.glm_client = get_glm_client()

        if not self.skill_manager:
            from app.core.skills.skill_manager import get_skill_manager
            self.skill_manager = get_skill_manager()

        logger.info("审核管理器初始化完成")

    async def review_episode(
        self,
        project: SeriesProject,
        episode: Episode,
        config: ReviewConfig,
        dimensions: Optional[List[DimensionType]] = None
    ) -> ReviewResult:
        """
        执行完整的审核流程

        Args:
            project: 项目信息
            episode: 剧集内容
            config: 审核配置
            dimensions: 指定要审核的维度（可选，不指定则审核所有启用的维度）

        Returns:
            ReviewResult: 审核结果
        """
        start_time = datetime.now()
        result_id = str(uuid.uuid4())

        logger.info(f"开始审核剧集: {episode.id} - EP{episode.number}")

        # 确定要审核的维度
        if dimensions is None:
            # 审核所有启用的维度
            dimensions = [
                dim for dim, dim_config in config.dimension_settings.items()
                if dim_config.enabled
            ]
        else:
            # 过滤出启用的指定维度
            dimensions = [
                dim for dim in dimensions
                if dim in config.dimension_settings and config.dimension_settings[dim].enabled
            ]

        logger.info(f"审核维度: {[d.value for d in dimensions]}")

        # 初始化结果
        result = ReviewResult(
            id=result_id,
            episode_id=episode.id,
            episode_number=episode.number,
            project_id=project.id,
            dimension_scores=[],
            issues=[],
            overall_score=0.0,
            passed=False,
            reviewed_at=start_time
        )

        # 执行各维度审核
        for dimension in dimensions:
            try:
                logger.info(f"执行维度审核: {dimension.value}")

                dimension_score = await self._run_dimension_review(
                    project=project,
                    episode=episode,
                    dimension=dimension,
                    config=config,
                    dim_config=config.dimension_settings.get(dimension)
                )

                result.dimension_scores.append(dimension_score)
                result.issues.extend(dimension_score.issues)

                logger.info(
                    f"维度 {dimension.value} 审核完成: "
                    f"分数={dimension_score.score}, "
                    f"问题数={len(dimension_score.issues)}"
                )

            except Exception as e:
                logger.error(f"维度 {dimension.value} 审核失败: {str(e)}")
                # 创建失败结果
                result.dimension_scores.append(
                    DimensionScore(
                        dimension=dimension,
                        score=0.0,
                        passed=False,
                        issue_count=0,
                        issues=[]
                    )
                )

        # 检查自定义规则
        if config.custom_rules:
            try:
                custom_issues = await self._check_custom_rules(
                    episode=episode,
                    rules=config.custom_rules,
                    config=config
                )
                result.issues.extend(custom_issues)
                logger.info(f"自定义规则检查完成，发现 {len(custom_issues)} 个问题")
            except Exception as e:
                logger.error(f"自定义规则检查失败: {str(e)}")

        # 聚合结果
        self._aggregate_results(result, config)

        # 计算耗时
        result.review_duration = (datetime.now() - start_time).total_seconds()

        logger.info(
            f"审核完成: 总分={result.overall_score:.1f}, "
            f"通过={'是' if result.passed else '否'}, "
            f"问题数={result.issue_count}"
        )

        return result

    async def _run_dimension_review(
        self,
        project: SeriesProject,
        episode: Episode,
        dimension: DimensionType,
        config: ReviewConfig,
        dim_config: Any
    ) -> DimensionScore:
        """
        运行单个维度的审核

        Args:
            project: 项目信息
            episode: 剧集内容
            dimension: 审核维度
            config: 总体审核配置
            dim_config: 维度配置

        Returns:
            DimensionScore: 维度分数
        """
        issues = []

        # 获取维度信息
        dim_info = self.DIMENSIONS.get(dimension)
        if not dim_info:
            logger.warning(f"未知维度: {dimension}")
            return DimensionScore(
                dimension=dimension,
                score=0.0,
                passed=True,
                issue_count=0,
                issues=[]
            )

        # 尝试使用Skill进行审核
        if dim_info["skill_ids"]:
            for skill_id in dim_info["skill_ids"]:
                if skill_id in config.enabled_review_skills:
                    try:
                        skill_issues = await self._run_review_skill(
                            project=project,
                            episode=episode,
                            skill_id=skill_id,
                            dimension=dimension,
                            strictness=dim_config.strictness if dim_config else config.overall_strictness
                        )
                        issues.extend(skill_issues)
                    except Exception as e:
                        logger.error(f"Skill {skill_id} 审核失败: {str(e)}")

        # 如果没有Skill或Skill失败，使用LLM通用审核
        if not issues and dim_config:
            try:
                llm_issues = await self._run_llm_review(
                    project=project,
                    episode=episode,
                    dimension=dimension,
                    strictness=dim_config.strictness
                )
                issues.extend(llm_issues)
            except Exception as e:
                logger.error(f"LLM审核失败: {str(e)}")

        # 计算分数
        score = self.calculate_dimension_score(issues, dim_config.strictness if dim_config else config.overall_strictness)

        # 判断是否通过（分数 >= 75 视为通过）
        passed = score >= 75.0

        return DimensionScore(
            dimension=dimension,
            score=score,
            passed=passed,
            issue_count=len(issues),
            issues=issues
        )

    async def _run_review_skill(
        self,
        project: SeriesProject,
        episode: Episode,
        skill_id: str,
        dimension: DimensionType,
        strictness: float
    ) -> List[ReviewIssue]:
        """
        运行审核Skill

        Args:
            project: 项目信息
            episode: 剧集内容
            skill_id: Skill ID
            dimension: 审核维度
            strictness: 严格程度

        Returns:
            List[ReviewIssue]: 发现的问题列表
        """
        # 加载Skill
        skill = await self.skill_manager.load_skill(skill_id)
        if not skill:
            logger.warning(f"Skill未找到: {skill_id}")
            return []

        logger.info(f"使用Skill进行审核: {skill.name}")

        # 构建审核提示词
        review_prompt = self._build_review_prompt(
            project=project,
            episode=episode,
            dimension=dimension,
            strictness=strictness
        )

        # 调用Skill
        try:
            response = await self.glm_client.chat_with_skill(
                skill_behavior=skill.behavior_guide,
                user_input=review_prompt,
                context={
                    "project": project.name,
                    "episode": episode.number,
                    "dimension": dimension.value,
                    "strictness": strictness
                },
                temperature=0.3  # 审核使用较低温度以保证稳定性
            )

            # 解析响应
            return self._parse_review_response(
                response=response,
                dimension=dimension,
                episode_number=episode.number
            )

        except Exception as e:
            logger.error(f"Skill {skill_id} 调用失败: {str(e)}")
            return []

    async def _run_llm_review(
        self,
        project: SeriesProject,
        episode: Episode,
        dimension: DimensionType,
        strictness: float
    ) -> List[ReviewIssue]:
        """
        使用LLM进行通用审核（当没有专门Skill时）

        Args:
            project: 项目信息
            episode: 剧集内容
            dimension: 审核维度
            strictness: 严格程度

        Returns:
            List[ReviewIssue]: 发现的问题列表
        """
        dim_info = self.DIMENSIONS.get(dimension, {})
        dim_name = dim_info.get("name", dimension.value)

        system_prompt = f"""你是专业的内容审核专家，负责审核剧集创作中的{dim_name}问题。

审核严格程度: {strictness:.1f} (0-1)

请仔细分析以下剧集内容，找出{dim_name}方面的问题。

以JSON格式返回结果：
{{
  "issues": [
    {{
      "type": "问题类型",
      "severity": "high/medium/low",
      "description": "问题描述",
      "suggestion": "修改建议",
      "location": {{
        "scene": 场景编号,
        "context": "相关上下文"
      }}
    }}
  ]
}}"""

        user_prompt = f"""项目名称: {project.name}
集数: EP{episode.number}
标题: {episode.title or '未命名'}

{episode.global_context_summary() if hasattr(episode, 'global_context_summary') else ''}

剧集内容:
{episode.content}

请分析{dim_name}问题。"""

        try:
            response = await self.glm_client.chat(
                messages=[
                    {"role": "system", "content": system_prompt},
                    {"role": "user", "content": user_prompt}
                ],
                temperature=0.3
            )

            content = response["choices"][0]["message"]["content"]
            return self._parse_review_response(
                response=content,
                dimension=dimension,
                episode_number=episode.number
            )

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

    def _build_review_prompt(
        self,
        project: SeriesProject,
        episode: Episode,
        dimension: DimensionType,
        strictness: float
    ) -> str:
        """构建审核提示词"""
        dim_info = self.DIMENSIONS.get(dimension, {})

        prompt_parts = [
            f"请审核以下剧集内容的{dim_info.get('name', dimension.value)}。",
            f"",
            f"项目: {project.name}",
            f"集数: EP{episode.number}",
            f"标题: {episode.title or '未命名'}",
            f"",
            f"审核严格程度: {strictness:.1f}",
            f"",
        ]

        # 添加全局上下文
        if project.globalContext:
            prompt_parts.extend([
                "全局设定:",
                f"- 世界观: {project.globalContext.worldSetting or '无'}",
                f"- 人物: {', '.join(project.globalContext.characterProfiles.keys())}",
                f"- 场景: {', '.join(project.globalContext.sceneSettings.keys())}",
                f""
            ])

        # 添加历史记忆
        if project.memory and project.memory.eventTimeline:
            prompt_parts.extend([
                f"历史事件 ({len(project.memory.eventTimeline)} 条):",
                ""
            ])

        # 添加剧集内容
        prompt_parts.extend([
            "剧集内容:",
            episode.content or "（内容为空）"
        ])

        return "\n".join(prompt_parts)

    def _parse_review_response(
        self,
        response: str,
        dimension: DimensionType,
        episode_number: int
    ) -> List[ReviewIssue]:
        """
        解析审核响应

        Args:
            response: Skill或LLM的响应
            dimension: 审核维度
            episode_number: 集数

        Returns:
            List[ReviewIssue]: 解析出的问题列表
        """
        issues = []

        try:
            # 尝试提取JSON
            json_start = response.find("{")
            json_end = response.rfind("}") + 1

            if json_start >= 0 and json_end > json_start:
                json_str = response[json_start:json_end]
                data = json.loads(json_str)

                # 解析issues数组
                raw_issues = data.get("issues", [])

                # 兼容consistency_checker的输出格式
                if not raw_issues and "dimension_scores" in data:
                    # 从dimension_scores提取issues
                    for dim_name, dim_data in data.get("dimension_scores", {}).items():
                        raw_issues.extend(dim_data.get("issues", []))

                for raw_issue in raw_issues:
                    try:
                        # 解析位置
                        location_data = raw_issue.get("location", {})
                        if isinstance(location_data, dict):
                            location = Location(
                                episode=location_data.get("episode", episode_number),
                                scene=location_data.get("scene"),
                                line=location_data.get("line"),
                                context=location_data.get("context")
                            )
                        else:
                            location = Location(episode=episode_number)

                        # 映射问题类型
                        issue_type_str = raw_issue.get("type", "custom")
                        try:
                            issue_type = IssueType(issue_type_str)
                        except ValueError:
                            issue_type = IssueType.custom

                        # 映射严重程度
                        severity_str = raw_issue.get("severity", "medium").lower()
                        try:
                            severity = SeverityLevel(severity_str)
                        except ValueError:
                            severity = SeverityLevel.medium

                        issue = ReviewIssue(
                            id=str(uuid.uuid4()),
                            type=issue_type,
                            dimension=dimension,
                            severity=severity,
                            location=location,
                            description=raw_issue.get("description", ""),
                            suggestion=raw_issue.get("suggestion", "")
                        )
                        issues.append(issue)

                    except Exception as e:
                        logger.warning(f"解析单个问题失败: {str(e)}")
                        continue

        except json.JSONDecodeError as e:
            logger.warning(f"JSON解析失败: {str(e)}")
            # 尝试从文本中提取问题
            issues = self._extract_issues_from_text(response, dimension, episode_number)

        except Exception as e:
            logger.error(f"解析审核响应失败: {str(e)}")

        return issues

    def _extract_issues_from_text(
        self,
        text: str,
        dimension: DimensionType,
        episode_number: int
    ) -> List[ReviewIssue]:
        """从文本中提取问题（当JSON解析失败时）"""
        issues = []

        # 简单的文本模式匹配
        lines = text.split('\n')
        current_issue = None

        for line in lines:
            line = line.strip()

            # 查找问题标记
            if any(marker in line.lower() for marker in ['问题', 'issue', '错误', 'error']):
                current_issue = {
                    "description": line,
                    "severity": SeverityLevel.medium
                }
            elif current_issue and '建议' in line:
                current_issue["suggestion"] = line
                issues.append(current_issue)
                current_issue = None

        return issues

    async def _check_custom_rules(
        self,
        episode: Episode,
        rules: List[CustomRule],
        config: ReviewConfig
    ) -> List[ReviewIssue]:
        """
        检查自定义规则

        Args:
            episode: 剧集内容
            rules: 自定义规则列表
            config: 审核配置

        Returns:
            List[ReviewIssue]: 违反规则的问题
        """
        issues = []

        # 只检查启用的规则
        enabled_rules = [r for r in rules if r.enabled]

        if not enabled_rules:
            return issues

        logger.info(f"检查 {len(enabled_rules)} 条自定义规则")

        for rule in enabled_rules:
            try:
                # 使用LLM检查规则
                rule_issues = await self._check_single_rule(episode, rule, config)
                issues.extend(rule_issues)

            except Exception as e:
                logger.error(f"检查规则 {rule.id} 失败: {str(e)}")

        return issues

    async def _check_single_rule(
        self,
        episode: Episode,
        rule: CustomRule,
        config: ReviewConfig
    ) -> List[ReviewIssue]:
        """
        检查单条自定义规则

        Args:
            episode: 剧集内容
            rule: 自定义规则
            config: 审核配置

        Returns:
            List[ReviewIssue]: 违反该规则的问题
        """
        system_prompt = f"""你是内容审核专家，检查内容是否符合特定规则。

规则: {rule.name}
规则描述: {rule.description}
触发条件: {rule.trigger_condition}

请分析以下内容，判断是否违反该规则。

以JSON格式返回：
{{
  "violated": true/false,
  "violations": [
    {{
      "description": "具体描述哪里违反了规则",
      "location": {{"scene": 场景编号, "context": "上下文"}},
      "suggestion": "修改建议"
    }}
  ]
}}"""

        user_prompt = f"""集数: EP{episode.number}
内容:
{episode.content}

请检查是否违反规则: {rule.name}"""

        try:
            response = await self.glm_client.chat(
                messages=[
                    {"role": "system", "content": system_prompt},
                    {"role": "user", "content": user_prompt}
                ],
                temperature=0.2
            )

            content = response["choices"][0]["message"]["content"]
            data = json.loads(content)

            if not data.get("violated", False):
                return []

            issues = []
            for violation in data.get("violations", []):
                location_data = violation.get("location", {})
                issue = ReviewIssue(
                    id=str(uuid.uuid4()),
                    type=IssueType.custom,
                    dimension=rule.dimension,
                    severity=rule.severity,
                    location=Location(
                        episode=episode.number,
                        scene=location_data.get("scene"),
                        context=location_data.get("context")
                    ),
                    description=violation.get("description", ""),
                    suggestion=violation.get("suggestion", ""),
                    rule_id=rule.id
                )
                issues.append(issue)

            return issues

        except Exception as e:
            logger.error(f"规则检查失败 {rule.id}: {str(e)}")
            return []

    def _aggregate_results(self, result: ReviewResult, config: ReviewConfig):
        """
        聚合各维度结果为总体分数

        Args:
            result: 审核结果
            config: 审核配置
        """
        if not result.dimension_scores:
            result.overall_score = 0.0
            result.passed = False
            return

        # 加权平均计算总分
        total_weight = 0.0
        weighted_sum = 0.0

        for dim_score in result.dimension_scores:
            dim_config = config.dimension_settings.get(dim_score.dimension)
            weight = dim_config.weight if dim_config else 1.0

            weighted_sum += dim_score.score * weight
            total_weight += weight

        if total_weight > 0:
            result.overall_score = weighted_sum / total_weight
        else:
            result.overall_score = sum(ds.score for ds in result.dimension_scores) / len(result.dimension_scores)

        # 判断是否通过
        result.passed = (
            result.overall_score >= config.pass_threshold and
            result.high_severity_count == 0
        )

        # 计算统计信息
        result.calculate_statistics()

    def calculate_dimension_score(
        self,
        issues: List[ReviewIssue],
        strictness: float
    ) -> float:
        """
        计算维度分数

        Args:
            issues: 问题列表
            strictness: 严格程度

        Returns:
            float: 分数 (0-100)
        """
        if not issues:
            return 100.0

        # 基础分数
        score = 100.0

        # 根据问题严重程度扣分
        for issue in issues:
            if issue.severity == SeverityLevel.high:
                score -= 20 * strictness
            elif issue.severity == SeverityLevel.medium:
                score -= 10 * strictness
            else:  # low
                score -= 5 * strictness

        # 确保分数在 0-100 范围内
        return max(0.0, min(100.0, score))

    def get_dimension_info(self, dimension: DimensionType) -> Optional[Dict[str, Any]]:
        """
        获取维度信息

        Args:
            dimension: 维度类型

        Returns:
            维度信息字典
        """
        return self.DIMENSIONS.get(dimension)

    def list_all_dimensions(self) -> List[DimensionType]:
        """列出所有支持的维度"""
        return list(self.DIMENSIONS.keys())


# 全局单例
_review_manager: Optional[ReviewManager] = None


def get_review_manager() -> ReviewManager:
    """获取审核管理器单例"""
    global _review_manager
    if _review_manager is None:
        _review_manager = ReviewManager()
    return _review_manager


# 便捷导出
review_manager = get_review_manager()
