"""Expert Team 计划数据模型 - 流水线模式

支持两种模式：
1. hub-and-spoke（向后兼容）：Lead Expert 分解为并行子任务（SubTask）
2. pipeline（新）：Lead Expert 分解为阶段（PlanPhase），阶段间有依赖关系

流水线模式核心：
- Lead Expert 分解任务为阶段（phases），每个阶段有 assigned_expert 和 depends_on
- 执行时按依赖拓扑排序，无依赖的阶段并行，有依赖的串行
- 阶段间数据通过 SharedWorkspace 传递
"""

from __future__ import annotations

import enum
import uuid
from dataclasses import dataclass, field
from typing import Any


class MergeStrategy(str, enum.Enum):
    """合并策略 - Lead Expert 用于选择最佳结果

    仅保留 BEST 策略：Lead Expert 从所有结果中选择或综合出最佳结果。
    """

    BEST = "best"


class PlanStatus(str, enum.Enum):
    """计划状态"""

    DRAFT = "draft"
    EXECUTING = "executing"
    COMPLETED = "completed"
    FAILED = "failed"
    FALLBACK = "fallback"


class SubTaskStatus(str, enum.Enum):
    """子任务状态（hub-and-spoke 模式）"""

    PENDING = "pending"
    RUNNING = "running"
    COMPLETED = "completed"
    FAILED = "failed"


class PhaseStatus(str, enum.Enum):
    """阶段状态（流水线模式）"""

    PENDING = "pending"
    RUNNING = "in_progress"
    COMPLETED = "completed"
    FAILED = "failed"


class PhaseType(str, enum.Enum):
    """阶段类型

    EXECUTION: 标准执行阶段，专家独立完成分配的任务
    DEBATE: 辩论阶段，Lead 主导指定专家就分歧点交锋，Lead 裁决
    """

    EXECUTION = "execution"
    DEBATE = "debate"


@dataclass
class SubTask:
    """Lead Expert 分解出的子任务（hub-and-spoke 模式，向后兼容）

    Attributes:
        id: 子任务标识符
        description: 子任务描述
        assigned_expert: 分配的 Expert 名称
        status: 当前状态
        result: 子任务输出结果
    """

    id: str = field(default_factory=lambda: str(uuid.uuid4()))
    description: str = ""
    assigned_expert: str = ""
    status: SubTaskStatus = SubTaskStatus.PENDING
    result: dict[str, Any] | None = None

    def to_dict(self) -> dict[str, Any]:
        """序列化为字典"""
        return {
            "id": self.id,
            "description": self.description,
            "assigned_expert": self.assigned_expert,
            "status": self.status.value,
            "result": self.result,
        }

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> SubTask:
        """从字典创建 SubTask"""
        return cls(
            id=data.get("id", str(uuid.uuid4())),
            description=data.get("description", ""),
            assigned_expert=data.get("assigned_expert", ""),
            status=SubTaskStatus(data.get("status", SubTaskStatus.PENDING.value)),
            result=data.get("result"),
        )


@dataclass
class CollaborationContract:
    """协作契约 — 定义专家间的协作关系

    Lead 在分解任务时为每个阶段定义协作契约，明确哪些专家需要协作、协作内容是什么。

    Attributes:
        from_expert: 提供协作内容的专家名称
        to_expert: 接收协作内容的专家名称
        content_description: 协作内容描述（如"API 定义"、"数据模型"）
        status: 契约状态（pending/delivered/received）
    """

    from_expert: str = ""
    to_expert: str = ""
    content_description: str = ""
    status: str = "pending"

    def to_dict(self) -> dict[str, Any]:
        """序列化为字典"""
        return {
            "from_expert": self.from_expert,
            "to_expert": self.to_expert,
            "content_description": self.content_description,
            "status": self.status,
        }

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> CollaborationContract:
        """从字典创建 CollaborationContract"""
        return cls(
            from_expert=data.get("from_expert", ""),
            to_expert=data.get("to_expert", ""),
            content_description=data.get("content_description", ""),
            status=data.get("status", "pending"),
        )


@dataclass
class PlanPhase:
    """流水线模式中的执行阶段

    Lead Expert 将任务分解为多个阶段，阶段间通过 depends_on 建立依赖关系。
    执行时按拓扑排序，同层无依赖阶段并行，层间串行。

    Attributes:
        id: 阶段标识符（用于 depends_on 引用）
        name: 阶段名称（如"规划"、"前端"、"后端"、"QA"、"评审"）
        assigned_expert: 分配的 Expert 名称
        task_description: 阶段任务描述
        depends_on: 前置阶段 ID 列表（空列表表示无依赖）
        status: 当前状态
        result: 阶段输出结果
        phase_type: 阶段类型（EXECUTION 或 DEBATE）
        debate_config: 辩论阶段配置（仅 DEBATE 类型使用）：
            - topic: 辩论主题
            - participants: 参与专家名称列表
            - max_rounds: 最大辩论轮次（默认 2，硬上限 4）
            - skip: 是否跳过辩论（逃生舱）
        collaboration_contracts: 协作契约列表，定义该阶段涉及的专家协作关系
        rework_count: 返工次数（Lead 验收不合格后重新执行的次数）
        review_feedback: Lead 验收反馈（不合格时的修改要求）
    """

    id: str = field(default_factory=lambda: str(uuid.uuid4()))
    name: str = ""
    assigned_expert: str = ""
    task_description: str = ""
    depends_on: list[str] = field(default_factory=list)
    status: PhaseStatus = PhaseStatus.PENDING
    result: dict[str, Any] | None = None
    phase_type: PhaseType = PhaseType.EXECUTION
    debate_config: dict[str, Any] | None = None
    collaboration_contracts: list[CollaborationContract] = field(default_factory=list)
    rework_count: int = 0
    review_feedback: str | None = None

    def to_dict(self) -> dict[str, Any]:
        """序列化为字典"""
        # Serialize result to string to match frontend ITeamPlanPhase.result type
        result_str: str | None = None
        if self.result is not None:
            if isinstance(self.result, dict):
                result_str = self.result.get("content", str(self.result))
            else:
                result_str = str(self.result)
        return {
            "id": self.id,
            "name": self.name,
            "assigned_expert": self.assigned_expert,
            "task_description": self.task_description,
            "depends_on": list(self.depends_on),
            "status": self.status.value,
            "result": result_str,
            "phase_type": self.phase_type.value,
            "debate_config": self.debate_config,
            "collaboration_contracts": [c.to_dict() for c in self.collaboration_contracts],
            "rework_count": self.rework_count,
            "review_feedback": self.review_feedback,
        }

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> PlanPhase:
        """从字典创建 PlanPhase"""
        contracts_data = data.get("collaboration_contracts", [])
        if not isinstance(contracts_data, list):
            contracts_data = []
        contracts = [
            CollaborationContract.from_dict(c) if isinstance(c, dict) else CollaborationContract()
            for c in contracts_data
        ]
        return cls(
            id=data.get("id", str(uuid.uuid4())),
            name=data.get("name", ""),
            assigned_expert=data.get("assigned_expert", ""),
            task_description=data.get("task_description", ""),
            depends_on=list(data.get("depends_on", [])),
            status=PhaseStatus(data.get("status", PhaseStatus.PENDING.value)),
            result=data.get("result"),
            phase_type=PhaseType(data.get("phase_type", PhaseType.EXECUTION.value)),
            debate_config=data.get("debate_config"),
            collaboration_contracts=contracts,
            rework_count=data.get("rework_count", 0),
            review_feedback=data.get("review_feedback"),
        )


@dataclass
class TeamPlan:
    """Expert Team 执行计划

    支持两种模式：
    1. hub-and-spoke（向后兼容）：使用 subtasks，并行执行无依赖
    2. pipeline（新）：使用 phases，按依赖拓扑排序执行

    新代码应优先使用 phases 字段。subtasks 保留用于向后兼容。

    Attributes:
        id: 计划标识符
        task: 原始任务描述
        subtasks: 子任务列表（hub-and-spoke 模式，向后兼容）
        phases: 阶段列表（流水线模式）
        status: 计划状态
        lead_expert: 主导 Expert 名称
    """

    id: str = field(default_factory=lambda: str(uuid.uuid4()))
    task: str = ""
    subtasks: list[SubTask] = field(default_factory=list)
    phases: list[PlanPhase] = field(default_factory=list)
    status: PlanStatus = PlanStatus.DRAFT
    lead_expert: str = ""

    def to_dict(self) -> dict[str, Any]:
        """序列化为字典"""
        return {
            "id": self.id,
            "task": self.task,
            "subtasks": [st.to_dict() for st in self.subtasks],
            "phases": [ph.to_dict() for ph in self.phases],
            "status": self.status.value,
            "lead_expert": self.lead_expert,
        }

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> TeamPlan:
        """从字典创建 TeamPlan"""
        subtasks = [SubTask.from_dict(st) for st in data.get("subtasks", [])]
        phases = [PlanPhase.from_dict(ph) for ph in data.get("phases", [])]
        return cls(
            id=data.get("id", str(uuid.uuid4())),
            task=data.get("task", ""),
            subtasks=subtasks,
            phases=phases,
            status=PlanStatus(data.get("status", PlanStatus.DRAFT.value)),
            lead_expert=data.get("lead_expert", ""),
        )

    # ── SubTask 方法（hub-and-spoke 模式，向后兼容）──

    def get_subtask(self, subtask_id: str) -> SubTask | None:
        """根据 ID 获取子任务，不存在则返回 None"""
        for st in self.subtasks:
            if st.id == subtask_id:
                return st
        return None

    def update_subtask_status(
        self, subtask_id: str, status: SubTaskStatus, result: dict[str, Any] | None = None
    ) -> None:
        """更新子任务状态和可选的结果"""
        st = self.get_subtask(subtask_id)
        if st is not None:
            st.status = status
            if result is not None:
                st.result = result

    @property
    def completed_subtasks(self) -> list[SubTask]:
        """已完成的子任务列表"""
        return [st for st in self.subtasks if st.status == SubTaskStatus.COMPLETED]

    @property
    def failed_subtasks(self) -> list[SubTask]:
        """失败的子任务列表"""
        return [st for st in self.subtasks if st.status == SubTaskStatus.FAILED]

    @property
    def all_done(self) -> bool:
        """所有子任务是否都已完成（成功或失败）"""
        return all(
            st.status in (SubTaskStatus.COMPLETED, SubTaskStatus.FAILED) for st in self.subtasks
        )

    # ── PlanPhase 方法（流水线模式）──

    def get_phase(self, phase_id: str) -> PlanPhase | None:
        """根据 ID 获取阶段，不存在则返回 None"""
        for ph in self.phases:
            if ph.id == phase_id:
                return ph
        return None

    def update_phase_status(
        self, phase_id: str, status: PhaseStatus, result: dict[str, Any] | None = None
    ) -> None:
        """更新阶段状态和可选的结果"""
        ph = self.get_phase(phase_id)
        if ph is not None:
            ph.status = status
            if result is not None:
                ph.result = result

    @property
    def completed_phases(self) -> list[PlanPhase]:
        """已完成的阶段列表"""
        return [ph for ph in self.phases if ph.status == PhaseStatus.COMPLETED]

    @property
    def failed_phases(self) -> list[PlanPhase]:
        """失败的阶段列表"""
        return [ph for ph in self.phases if ph.status == PhaseStatus.FAILED]

    @property
    def all_phases_done(self) -> bool:
        """所有阶段是否都已完成（成功或失败）"""
        return all(ph.status in (PhaseStatus.COMPLETED, PhaseStatus.FAILED) for ph in self.phases)

    def get_ready_phases(self) -> list[PlanPhase]:
        """返回当前可执行的阶段（状态为 PENDING 且所有依赖已完成）

        Returns:
            可执行的阶段列表
        """
        completed_ids = {ph.id for ph in self.completed_phases}
        ready = []
        for ph in self.phases:
            if ph.status != PhaseStatus.PENDING:
                continue
            # Check if all dependencies are completed
            if all(dep_id in completed_ids for dep_id in ph.depends_on):
                ready.append(ph)
        return ready

    def topological_sort(self) -> list[list[PlanPhase]]:
        """按依赖关系拓扑排序阶段，返回执行层列表

        同层阶段无依赖关系，可并行执行；层间有依赖，需串行等待。

        Returns:
            执行层列表，每层包含可并行执行的阶段

        Raises:
            ValueError: 如果存在循环依赖
        """
        if not self.phases:
            return []

        # Build dependency graph
        phase_map = {ph.id: ph for ph in self.phases}
        all_ids = set(phase_map.keys())

        # Validate depends_on references
        for ph in self.phases:
            for dep_id in ph.depends_on:
                if dep_id not in all_ids:
                    raise ValueError(
                        f"Phase '{ph.id}' ({ph.name}) depends on non-existent phase '{dep_id}'"
                    )

        # Kahn's algorithm for topological sort
        # Compute in-degree (number of dependencies) for each phase
        in_degree: dict[str, int] = {ph.id: len(ph.depends_on) for ph in self.phases}

        # Build reverse dependency map (which phases depend on this one)
        dependents: dict[str, list[str]] = {ph.id: [] for ph in self.phases}
        for ph in self.phases:
            for dep_id in ph.depends_on:
                dependents[dep_id].append(ph.id)

        layers: list[list[PlanPhase]] = []
        processed: set[str] = set()

        while len(processed) < len(self.phases):
            # Find all phases with in_degree 0 that haven't been processed
            current_layer_ids = [
                ph_id for ph_id in in_degree if ph_id not in processed and in_degree[ph_id] == 0
            ]

            if not current_layer_ids:
                # No progress — cycle detected
                remaining = [ph_id for ph_id in in_degree if ph_id not in processed]
                raise ValueError(f"Circular dependency detected among phases: {remaining}")

            # Add current layer
            current_layer = [phase_map[ph_id] for ph_id in current_layer_ids]
            layers.append(current_layer)

            # Mark as processed and reduce in_degree for dependents
            for ph_id in current_layer_ids:
                processed.add(ph_id)
                for dep_id in dependents[ph_id]:
                    in_degree[dep_id] -= 1

        return layers
