"""PathOptimizer - 执行路径优化器

发现更优执行路径时自动更新经验库中的推荐路径。

核心逻辑：
1. 对比新路径与现有最优路径（综合耗时和成功率）
2. 新路径成功率更高 → 更新推荐路径
3. 成功率相近但耗时更短 → 更新推荐路径
4. 样本量不足 → 不更新，记录待观察
"""

from __future__ import annotations

import logging
import uuid
from dataclasses import dataclass, field
from datetime import datetime, timezone
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from agentkit.evolution.experience_store import InMemoryExperienceStore

logger = logging.getLogger(__name__)


@dataclass
class ExecutionPath:
    """执行路径数据模型

    记录特定任务类型的执行路径信息，用于路径优化比较。

    Attributes:
        path_id: 路径唯一标识
        task_type: 任务类型
        steps: 执行步骤名称列表
        total_duration: 总耗时（秒）
        success_rate: 成功率（0.0 ~ 1.0）
        sample_count: 样本数量
        is_recommended: 是否为当前推荐路径
        created_at: 创建时间
    """

    path_id: str = ""
    task_type: str = ""
    steps: list[str] = field(default_factory=list)
    total_duration: float = 0.0
    success_rate: float = 0.0
    sample_count: int = 0
    is_recommended: bool = False
    created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))


@dataclass
class PathUpdateResult:
    """路径更新结果

    Attributes:
        updated: 是否更新了推荐路径
        old_path: 更新前的推荐路径（未更新时为 None）
        new_path: 更新后的推荐路径（未更新时为 None）
        reason: 更新/未更新的原因说明
    """

    updated: bool = False
    old_path: ExecutionPath | None = None
    new_path: ExecutionPath | None = None
    reason: str = ""


class PathOptimizer:
    """执行路径优化器

    对比新路径与现有最优路径，决定是否更新推荐路径。
    可独立使用，也可集成到 PlanChecker 的复盘中。

    更新策略：
    1. 新路径成功率 > 现有成功率 + success_rate_threshold → 更新
    2. 成功率相近（差值 ≤ threshold）但耗时显著更短
       （duration 改善比例 > duration_improvement_threshold）→ 更新
    3. 样本量不足（< min_sample_count）→ 不更新
    4. 其他情况 → 保留现有推荐路径
    """

    def __init__(
        self,
        experience_store: InMemoryExperienceStore | None = None,
        min_sample_count: int = 3,
        success_rate_threshold: float = 0.05,
        duration_improvement_threshold: float = 0.2,
    ):
        """初始化 PathOptimizer

        Args:
            experience_store: 经验存储实例（可选）
            min_sample_count: 最小样本量，低于此值不做决策
            success_rate_threshold: 成功率提升阈值，超过此值视为显著提升
            duration_improvement_threshold: 耗时改善比例阈值，超过此值视为显著改善
        """
        self._experience_store = experience_store
        self._min_sample_count = min_sample_count
        self._success_rate_threshold = success_rate_threshold
        self._duration_improvement_threshold = duration_improvement_threshold
        self._recommended_paths: dict[str, ExecutionPath] = {}
        self._pending_paths: dict[str, list[ExecutionPath]] = {}

    def get_recommended_path(self, task_type: str) -> ExecutionPath | None:
        """获取指定任务类型的当前推荐路径

        Args:
            task_type: 任务类型

        Returns:
            推荐路径，若无则返回 None
        """
        return self._recommended_paths.get(task_type)

    async def evaluate_and_update(
        self,
        task_type: str,
        new_path: ExecutionPath,
    ) -> PathUpdateResult:
        """评估新路径并决定是否更新推荐路径

        Args:
            task_type: 任务类型
            new_path: 新的执行路径

        Returns:
            路径更新结果
        """
        # 确保新路径有 path_id
        if not new_path.path_id:
            new_path.path_id = str(uuid.uuid4())

        new_path.task_type = task_type

        # 样本量不足 → 不更新，记录待观察
        if new_path.sample_count < self._min_sample_count:
            self._pending_paths.setdefault(task_type, []).append(new_path)
            if len(self._pending_paths[task_type]) > 50:
                self._pending_paths[task_type] = self._pending_paths[task_type][-50:]
            reason = (
                f"样本量不足（{new_path.sample_count} < {self._min_sample_count}），"
                f"记录待观察"
            )
            logger.info(
                f"Path not updated for '{task_type}': {reason}"
            )
            return PathUpdateResult(
                updated=False,
                old_path=None,
                new_path=new_path,
                reason=reason,
            )

        current = self._recommended_paths.get(task_type)

        # 无现有推荐路径 → 直接设为推荐
        if current is None:
            new_path.is_recommended = True
            self._recommended_paths[task_type] = new_path
            reason = "无现有推荐路径，直接设为推荐"
            logger.info(f"Path set as recommended for '{task_type}': {reason}")
            return PathUpdateResult(
                updated=True,
                old_path=None,
                new_path=new_path,
                reason=reason,
            )

        # 比较新路径与现有推荐路径
        return self._compare_and_decide(task_type, current, new_path)

    def _compare_and_decide(
        self,
        task_type: str,
        current: ExecutionPath,
        new: ExecutionPath,
    ) -> PathUpdateResult:
        """比较新旧路径并决策

        比较逻辑：
        1. 新路径成功率 > 现有成功率 + threshold → 更新
        2. 成功率相近（差值 ≤ threshold）且新耗时显著更短 → 更新
        3. 其他 → 保留现有
        """
        sr_diff = new.success_rate - current.success_rate

        # 条件 1：成功率显著提升
        if sr_diff > self._success_rate_threshold:
            return self._apply_update(
                task_type, current, new,
                f"成功率显著提升（{new.success_rate:.2f} > {current.success_rate:.2f}，"
                f"提升 {sr_diff:.2f}）",
            )

        # 条件 2：成功率相近但耗时显著更短
        if abs(sr_diff) <= self._success_rate_threshold:
            if current.total_duration > 0:
                duration_improvement = (
                    (current.total_duration - new.total_duration) / current.total_duration
                )
                if (
                    new.total_duration < current.total_duration
                    and duration_improvement > self._duration_improvement_threshold
                ):
                    return self._apply_update(
                        task_type, current, new,
                        f"成功率相近（{new.success_rate:.2f} vs {current.success_rate:.2f}），"
                        f"耗时显著更短（{new.total_duration:.1f}s vs {current.total_duration:.1f}s，"
                        f"改善 {duration_improvement:.1%}）",
                    )
            elif current.total_duration == 0 and new.total_duration > 0:
                # 现有路径耗时为 0（不太可能），不更新
                pass
            elif current.total_duration == 0 and new.total_duration == 0:
                # 两者耗时均为 0，不更新
                pass

        # 条件 3：无明显优势 → 保留现有
        reason = (
            f"新路径无明显优势（成功率 {new.success_rate:.2f} vs {current.success_rate:.2f}，"
            f"耗时 {new.total_duration:.1f}s vs {current.total_duration:.1f}s），保留现有推荐路径"
        )
        logger.info(f"Path not updated for '{task_type}': {reason}")
        return PathUpdateResult(
            updated=False,
            old_path=current,
            new_path=new,
            reason=reason,
        )

    def _apply_update(
        self,
        task_type: str,
        old: ExecutionPath,
        new: ExecutionPath,
        reason: str,
    ) -> PathUpdateResult:
        """应用路径更新"""
        old.is_recommended = False
        new.is_recommended = True
        self._recommended_paths[task_type] = new
        logger.info(f"Path updated for '{task_type}': {reason}")
        return PathUpdateResult(
            updated=True,
            old_path=old,
            new_path=new,
            reason=reason,
        )

    def get_pending_paths(self, task_type: str) -> list[ExecutionPath]:
        """获取指定任务类型的待观察路径

        Args:
            task_type: 任务类型

        Returns:
            待观察路径列表
        """
        return list(self._pending_paths.get(task_type, []))
