"""SkillPipeline - 技能编排，将多个 Skill 串联为 Pipeline 执行

复用 PipelineEngine 的设计理念，支持：
- 顺序执行（skill A → skill B → skill C）
- 条件分支（if skill A output contains X, run skill B, else skip）
- 输出映射（将上一步输出字段映射到下一步输入字段）
"""

import logging
import re
from typing import Any, Callable, Coroutine

from agentkit.skills.base import Skill, SkillConfig
from agentkit.skills.registry import SkillRegistry

logger = logging.getLogger(__name__)


class SkillPipeline:
    """将多个 Skill 串联为 Pipeline 执行

    每个步骤定义包含：
    - skill_name: str (必需) — 要执行的 Skill 名称
    - input_mapping: dict | None — 将上一步输出映射到当前步骤输入
    - condition: str | None — 条件表达式，不满足则跳过
    """

    def __init__(
        self,
        name: str,
        steps: list[dict[str, Any]],
        skill_registry: SkillRegistry | None = None,
    ):
        """
        Args:
            name: Pipeline 名称
            steps: 步骤定义列表，每项包含 skill_name、input_mapping、condition
            skill_registry: 用于查找 Skill 的注册中心
        """
        self.name = name
        self._steps = steps
        self._skill_registry = skill_registry

    async def execute(
        self,
        input_data: dict[str, Any],
        agent_factory: Callable[..., Coroutine] | None = None,
    ) -> dict[str, Any]:
        """顺序执行 Pipeline 中所有步骤

        Args:
            input_data: 初始输入数据
            agent_factory: 可选的 Agent 工厂函数，签名为
                async (skill_name: str, input_data: dict) -> dict

        Returns:
            包含 pipeline 名称、各步骤结果和最终输出的字典
        """
        success = True
        current_input: dict[str, Any] = input_data
        results: list[dict[str, Any]] = []

        for i, step_def in enumerate(self._steps):
            skill_name = step_def["skill_name"]

            # 条件检查
            condition = step_def.get("condition")
            if condition and not self._evaluate_condition(condition, current_input, results):
                results.append({
                    "step": i,
                    "skill": skill_name,
                    "status": "skipped",
                    "reason": f"Condition not met: {condition}",
                })
                continue

            # 输入映射
            input_mapping = step_def.get("input_mapping")
            step_input = (
                self._map_input(current_input, input_mapping, results)
                if input_mapping
                else current_input
            )

            # 执行 Skill
            try:
                step_result = await self._execute_skill(skill_name, step_input, agent_factory)
                results.append({
                    "step": i,
                    "skill": skill_name,
                    "output": step_result,
                    "status": "success",
                })
                current_input = step_result
            except Exception as e:
                results.append({
                    "step": i,
                    "skill": skill_name,
                    "error": str(e),
                    "status": "failed",
                })
                success = False
                break

        return {
            "pipeline": self.name,
            "steps": results,
            "final_output": current_input if success else None,
            "success": success,
        }

    async def _execute_skill(
        self,
        skill_name: str,
        input_data: dict[str, Any],
        agent_factory: Callable[..., Coroutine] | None = None,
    ) -> dict[str, Any]:
        """执行单个 Skill

        优先使用 agent_factory，其次通过 SkillRegistry 查找 Skill 并创建 Agent 执行。
        """
        if agent_factory:
            return await agent_factory(skill_name, input_data)

        if self._skill_registry:
            try:
                skill = self._skill_registry.get(skill_name)
            except Exception:
                raise ValueError(f"Skill '{skill_name}' not found in registry")

            from agentkit.core.config_driven import ConfigDrivenAgent
            from agentkit.core.protocol import TaskMessage
            from datetime import datetime, timezone

            agent = ConfigDrivenAgent(config=skill.config)
            task = TaskMessage(
                task_id=f"pipeline-{skill_name}",
                agent_name=skill_name,
                task_type=skill.config.agent_type,
                priority=0,
                input_data=input_data,
                callback_url=None,
                created_at=datetime.now(timezone.utc),
            )
            return await agent.handle_task(task)

        raise ValueError(
            f"Cannot execute skill '{skill_name}': "
            "no agent_factory or skill_registry provided"
        )

    def _evaluate_condition(
        self,
        condition: str,
        current_input: dict[str, Any],
        results: list[dict[str, Any]],
    ) -> bool:
        """评估简单条件表达式

        支持格式：
        - "key.path == 'value'" — 字符串相等
        - "key.path > 0.5" — 数值大于
        """
        try:
            eq_match = re.match(r'^([\w.]+)\s*==\s*(.+)$', condition.strip())
            if eq_match:
                path = eq_match.group(1)
                value = eq_match.group(2).strip().strip("'\"")
                actual = self._resolve_path(path, current_input)
                return str(actual) == value
            gt_match = re.match(r'^([\w.]+)\s*>\s*(.+)$', condition.strip())
            if gt_match:
                path = gt_match.group(1)
                value = float(gt_match.group(2).strip())
                actual = float(self._resolve_path(path, current_input))
                return actual > value
        except (ValueError, TypeError, AttributeError, KeyError) as e:
            logger.warning(f"Condition evaluation failed for '{condition}': {e}")
            return False
        return False

    @staticmethod
    def _resolve_path(path: str, data: dict[str, Any]) -> Any:
        """解析点号路径，如 'output.score'"""
        parts = path.split(".")
        obj: Any = data
        for part in parts:
            if isinstance(obj, dict):
                obj = obj.get(part)
            else:
                return None
        return obj

    def _map_input(
        self,
        current_input: dict[str, Any],
        mapping: dict[str, str],
        results: list[dict[str, Any]],
    ) -> dict[str, Any]:
        """根据映射规则将上一步输出映射到当前步骤输入

        mapping 格式: {"target_key": "source.path"}
        """
        mapped: dict[str, Any] = {}
        for target_key, source_path in mapping.items():
            value = self._resolve_path(source_path, current_input)
            if value is not None:
                mapped[target_key] = value
        return mapped
