"""
评估反馈优化器

基于评估结果自动驱动系统优化:
1. 记忆反馈: 将失败模式和成功经验写入记忆系统
2. 技能反馈: 触发新技能生成或改进现有技能
3. Prompt 反馈: 调整 Agent 指导原则
4. 工具反馈: 更新工具描述中的警告信息

通过闭环反馈实现 Agent 的持续自我改进。
"""

import asyncio
import json
import logging
import os
import time
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any

from .metrics import EvalMetrics, EvalResult

logger = logging.getLogger(__name__)


@dataclass
class OptimizationAction:
    """优化动作记录"""

    action_type: str  # "memory", "skill", "prompt", "tool"
    description: str
    details: dict[str, Any] = field(default_factory=dict)
    timestamp: float = field(default_factory=time.time)
    applied: bool = False


class FeedbackAnalyzer:
    """
    反馈分析器。

    分析评估结果，识别改进机会，
    生成具体的优化建议。
    """

    # 阈值配置
    COMPLETION_THRESHOLD = 0.8  # 完成率低于此值触发分析
    TOOL_ACCURACY_THRESHOLD = 0.7  # 工具准确率低于此值触发分析
    JUDGE_SCORE_THRESHOLD = 0.6  # Judge 评分低于此值关注
    LOOP_RATE_THRESHOLD = 0.1  # 循环率高于此值告警

    def analyze(
        self,
        metrics: EvalMetrics,
        results: list[EvalResult],
    ) -> list[OptimizationAction]:
        """
        分析评估结果，生成优化动作。

        Returns:
            优化动作列表
        """
        actions: list[OptimizationAction] = []

        # 1. 任务完成率分析
        if metrics.task_completion_rate < self.COMPLETION_THRESHOLD:
            failure_analysis = self._analyze_failures(results)
            actions.append(OptimizationAction(
                action_type="memory",
                description=(
                    f"任务完成率 ({metrics.task_completion_rate:.1%}) 低于阈值 "
                    f"({self.COMPLETION_THRESHOLD:.0%})，需要记录失败模式"
                ),
                details={
                    "completion_rate": metrics.task_completion_rate,
                    "failure_patterns": failure_analysis,
                },
            ))

        # 2. 工具准确率分析
        if metrics.tool_selection_accuracy < self.TOOL_ACCURACY_THRESHOLD:
            tool_analysis = self._analyze_tool_errors(results)
            actions.append(OptimizationAction(
                action_type="tool",
                description=(
                    f"工具准确率 ({metrics.tool_selection_accuracy:.1%}) 低于阈值，"
                    f"需要更新工具描述"
                ),
                details={
                    "accuracy": metrics.tool_selection_accuracy,
                    "error_tools": tool_analysis,
                },
            ))

        # 3. 循环检测率分析
        if metrics.loop_detection_rate > self.LOOP_RATE_THRESHOLD:
            actions.append(OptimizationAction(
                action_type="prompt",
                description=(
                    f"循环检测率 ({metrics.loop_detection_rate:.1%}) 过高，"
                    f"需要调整推理指导"
                ),
                details={
                    "loop_rate": metrics.loop_detection_rate,
                    "loop_traces": [
                        r.trace_id for r in results if r.metrics.loop_detected
                    ],
                },
            ))

        # 4. 效率分析
        if metrics.avg_iterations > 15:
            actions.append(OptimizationAction(
                action_type="prompt",
                description=(
                    f"平均迭代次数 ({metrics.avg_iterations:.1f}) 过高，"
                    f"需要优化推理效率"
                ),
                details={"avg_iterations": metrics.avg_iterations},
            ))

        # 5. Judge 建议汇总
        all_suggestions: list[str] = []
        all_patterns: list[str] = []
        for r in results:
            all_suggestions.extend(r.judge_suggestions)

        if all_suggestions:
            # 去重并取出现频率高的建议
            suggestion_count: dict[str, int] = {}
            for s in all_suggestions:
                suggestion_count[s] = suggestion_count.get(s, 0) + 1

            frequent = [
                s for s, c in sorted(
                    suggestion_count.items(), key=lambda x: x[1], reverse=True
                )
                if c >= 2
            ][:5]

            if frequent:
                actions.append(OptimizationAction(
                    action_type="skill",
                    description="Judge 频繁建议的能力改进",
                    details={"suggestions": frequent},
                ))

        return actions

    def _analyze_failures(self, results: list[EvalResult]) -> list[dict]:
        """分析失败模式。"""
        failed = [r for r in results if not r.metrics.task_completed]
        patterns: list[dict] = []

        # 按标签分组统计
        tag_counts: dict[str, int] = {}
        for r in failed:
            for tag in r.tags:
                tag_counts[tag] = tag_counts.get(tag, 0) + 1

        for tag, count in sorted(tag_counts.items(), key=lambda x: x[1], reverse=True):
            patterns.append({
                "pattern": tag,
                "count": count,
                "percentage": count / max(len(failed), 1),
            })

        return patterns

    def _analyze_tool_errors(self, results: list[EvalResult]) -> list[dict]:
        """分析工具错误分布。"""
        tool_error_count: dict[str, int] = {}
        tool_total_count: dict[str, int] = {}

        for r in results:
            for tool in r.metrics.tools_used:
                tool_total_count[tool] = tool_total_count.get(tool, 0) + 1

        # 简化: 按 trace 级别统计有错误的工具
        for r in results:
            if r.metrics.tool_errors > 0:
                for tool in set(r.metrics.tools_used):
                    tool_error_count[tool] = tool_error_count.get(tool, 0) + 1

        error_tools = []
        for tool, errors in sorted(
            tool_error_count.items(), key=lambda x: x[1], reverse=True
        ):
            total = tool_total_count.get(tool, errors)
            error_tools.append({
                "tool": tool,
                "error_traces": errors,
                "total_traces": total,
            })

        return error_tools[:10]


class FeedbackOptimizer:
    """
    反馈优化器。

    执行 FeedbackAnalyzer 生成的优化动作:
    - 将经验写入记忆系统 (MEMORY.md / memory storage)
    - 更新工具描述
    - 生成改进建议报告
    """

    def __init__(
        self,
        memory_file: str = "data/identity/MEMORY.md",
        output_dir: str = "data/evaluation",
    ) -> None:
        self._memory_file = memory_file
        self._output_dir = output_dir
        self._applied_actions: list[OptimizationAction] = []

    async def apply_actions(
        self,
        actions: list[OptimizationAction],
        *,
        dry_run: bool = False,
    ) -> list[OptimizationAction]:
        """
        执行优化动作。

        Args:
            actions: 优化动作列表
            dry_run: 如果为 True，只记录不实际执行

        Returns:
            已执行的动作列表
        """
        applied = []

        for action in actions:
            try:
                if action.action_type == "memory":
                    await self._apply_memory_feedback(action, dry_run=dry_run)
                elif action.action_type == "tool":
                    await self._apply_tool_feedback(action, dry_run=dry_run)
                elif action.action_type == "prompt":
                    await self._apply_prompt_feedback(action, dry_run=dry_run)
                elif action.action_type == "skill":
                    await self._apply_skill_feedback(action, dry_run=dry_run)

                action.applied = not dry_run
                applied.append(action)

            except Exception as e:
                logger.error(
                    f"[Optimizer] Failed to apply action '{action.action_type}': {e}"
                )

        # 保存动作记录
        await self._save_action_log(applied)
        self._applied_actions.extend(applied)

        return applied

    async def _apply_memory_feedback(
        self,
        action: OptimizationAction,
        dry_run: bool = False,
    ) -> None:
        """将失败经验写入记忆文件。"""
        patterns = action.details.get("failure_patterns", [])
        if not patterns:
            return

        memory_entry = [
            "",
            f"## 评估反馈 ({time.strftime('%Y-%m-%d')})",
            "",
            f"任务完成率: {action.details.get('completion_rate', 0):.1%}",
            "",
            "### 失败模式分析",
            "",
        ]
        for p in patterns:
            memory_entry.append(
                f"- **{p['pattern']}**: 出现 {p['count']} 次 "
                f"(占比 {p['percentage']:.0%})"
            )

        memory_entry.extend([
            "",
            "### 改进方向",
            "",
            "- 针对高频失败模式优化推理策略",
            "- 加强工具错误后的恢复能力",
            "",
        ])

        content = "\n".join(memory_entry)

        if dry_run:
            logger.info(f"[Optimizer][DryRun] Would append to {self._memory_file}:\n{content}")
            return

        # 追加到 MEMORY.md
        os.makedirs(os.path.dirname(self._memory_file), exist_ok=True)
        with open(self._memory_file, "a", encoding="utf-8") as f:
            f.write(content)

        logger.info(f"[Optimizer] Appended memory feedback to {self._memory_file}")

    async def _apply_tool_feedback(
        self,
        action: OptimizationAction,
        dry_run: bool = False,
    ) -> None:
        """记录工具改进建议。"""
        error_tools = action.details.get("error_tools", [])
        if not error_tools:
            return

        # 保存工具反馈报告
        report = {
            "type": "tool_feedback",
            "timestamp": time.strftime("%Y-%m-%dT%H:%M:%S"),
            "accuracy": action.details.get("accuracy", 0),
            "error_tools": error_tools,
            "recommendations": [
                f"检查工具 '{t['tool']}' 的错误处理逻辑"
                for t in error_tools[:5]
            ],
        }

        if dry_run:
            logger.info(f"[Optimizer][DryRun] Tool feedback: {json.dumps(report, ensure_ascii=False)}")
            return

        os.makedirs(self._output_dir, exist_ok=True)
        feedback_path = os.path.join(
            self._output_dir,
            f"tool_feedback_{time.strftime('%Y%m%d')}.json",
        )
        with open(feedback_path, "w", encoding="utf-8") as f:
            json.dump(report, f, ensure_ascii=False, indent=2)

        logger.info(f"[Optimizer] Saved tool feedback to {feedback_path}")

    async def _apply_prompt_feedback(
        self,
        action: OptimizationAction,
        dry_run: bool = False,
    ) -> None:
        """记录 Prompt 改进建议。"""
        report = {
            "type": "prompt_feedback",
            "timestamp": time.strftime("%Y-%m-%dT%H:%M:%S"),
            "description": action.description,
            "details": action.details,
        }

        if dry_run:
            logger.info(f"[Optimizer][DryRun] Prompt feedback: {action.description}")
            return

        os.makedirs(self._output_dir, exist_ok=True)
        feedback_path = os.path.join(
            self._output_dir,
            f"prompt_feedback_{time.strftime('%Y%m%d')}.json",
        )

        # 追加模式 (同一天可能有多次反馈)
        existing = []
        if os.path.exists(feedback_path):
            try:
                with open(feedback_path, "r", encoding="utf-8") as f:
                    existing = json.load(f)
                    if not isinstance(existing, list):
                        existing = [existing]
            except Exception:
                pass

        existing.append(report)
        with open(feedback_path, "w", encoding="utf-8") as f:
            json.dump(existing, f, ensure_ascii=False, indent=2)

        logger.info(f"[Optimizer] Saved prompt feedback to {feedback_path}")

    async def _apply_skill_feedback(
        self,
        action: OptimizationAction,
        dry_run: bool = False,
    ) -> None:
        """记录技能改进建议。"""
        suggestions = action.details.get("suggestions", [])
        if not suggestions:
            return

        report = {
            "type": "skill_feedback",
            "timestamp": time.strftime("%Y-%m-%dT%H:%M:%S"),
            "suggestions": suggestions,
        }

        if dry_run:
            logger.info(f"[Optimizer][DryRun] Skill feedback: {suggestions}")
            return

        os.makedirs(self._output_dir, exist_ok=True)
        feedback_path = os.path.join(
            self._output_dir,
            f"skill_feedback_{time.strftime('%Y%m%d')}.json",
        )
        with open(feedback_path, "w", encoding="utf-8") as f:
            json.dump(report, f, ensure_ascii=False, indent=2)

        logger.info(f"[Optimizer] Saved skill feedback to {feedback_path}")

    async def _save_action_log(self, actions: list[OptimizationAction]) -> None:
        """保存优化动作日志。"""
        if not actions:
            return

        os.makedirs(self._output_dir, exist_ok=True)
        log_path = os.path.join(
            self._output_dir,
            f"optimization_log_{time.strftime('%Y%m%d')}.json",
        )

        # 追加模式
        existing: list[dict] = []
        if os.path.exists(log_path):
            try:
                with open(log_path, "r", encoding="utf-8") as f:
                    existing = json.load(f)
            except Exception:
                pass

        for action in actions:
            existing.append({
                "action_type": action.action_type,
                "description": action.description,
                "applied": action.applied,
                "timestamp": action.timestamp,
            })

        with open(log_path, "w", encoding="utf-8") as f:
            json.dump(existing, f, ensure_ascii=False, indent=2)


class DailyEvaluator:
    """
    每日自动评估器。

    与自检系统 (selfcheck) 集成，自动运行评估流水线并执行反馈闭环。

    Usage:
        evaluator = DailyEvaluator(brain=brain)
        await evaluator.run_daily_eval()
    """

    def __init__(
        self,
        brain: Any = None,
        traces_dir: str = "data/traces",
        output_dir: str = "data/evaluation",
        memory_file: str = "data/identity/MEMORY.md",
    ) -> None:
        from .judge import Judge
        from .runner import EvalRunner
        from .reporter import Reporter

        self._judge = Judge(brain=brain)
        self._runner = EvalRunner(traces_dir=traces_dir, judge=self._judge)
        self._reporter = Reporter(output_dir=output_dir)
        self._analyzer = FeedbackAnalyzer()
        self._optimizer = FeedbackOptimizer(
            memory_file=memory_file,
            output_dir=output_dir,
        )

    def set_brain(self, brain: Any) -> None:
        """设置 LLM 客户端"""
        self._judge.set_brain(brain)

    async def run_daily_eval(self, dry_run: bool = False) -> dict[str, Any]:
        """
        运行每日评估。

        Returns:
            评估摘要 dict
        """
        logger.info("[DailyEval] Starting daily evaluation...")

        # 1. 运行评估
        metrics, results = await self._runner.run_evaluation()

        if not results:
            logger.info("[DailyEval] No traces to evaluate")
            return {"status": "no_data"}

        # 2. 保存报告
        report_path = await self._reporter.save(metrics, results)

        # 3. 分析改进机会
        actions = self._analyzer.analyze(metrics, results)

        # 4. 执行优化
        applied = await self._optimizer.apply_actions(actions, dry_run=dry_run)

        summary = {
            "status": "completed",
            "traces_evaluated": len(results),
            "metrics": metrics.to_dict(),
            "optimization_actions": len(applied),
            "report_path": report_path,
        }

        logger.info(
            f"[DailyEval] Complete: {len(results)} traces, "
            f"{len(applied)} optimizations applied"
        )

        return summary
