"""Desktop dataclass mirror of Avatar ExecutionPlan (Phase A)."""

from __future__ import annotations

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

from iuve_connect_agents.shared.confirm_type import ConfirmType
from iuve_connect_agents.shared.execution_plan_types import ALLOWED_EXECUTION_PLAN_COMMAND_TYPES
from iuve_connect_agents.shared.stop_zone import StopZone, confirm_type_for_stop_zone


@dataclass(slots=True)
class ExecutionPlanStep:
    step_id: str
    command_type: str
    payload: dict[str, Any] = field(default_factory=dict)
    depends_on: list[str] = field(default_factory=list)
    stop_zone: StopZone | None = None


@dataclass(slots=True)
class ConfirmGate:
    step_id: str
    confirm_type: ConfirmType
    stop_zone: StopZone | None = None
    reason: str = ""


@dataclass(slots=True)
class ExecutionPlan:
    goal_summary: str
    steps: list[ExecutionPlanStep]
    plan_id: str = field(default_factory=lambda: str(uuid4()))
    confirm_gates: list[ConfirmGate] = field(default_factory=list)
    planner_trace: dict[str, Any] = field(default_factory=dict)

    def __post_init__(self) -> None:
        self._validate()

    def _validate(self) -> None:
        if not self.goal_summary.strip():
            raise ValueError("missing_goal_summary")
        if not self.steps:
            raise ValueError("missing_steps")

        step_ids = [step.step_id for step in self.steps]
        if len(step_ids) != len(set(step_ids)):
            raise ValueError("duplicate_step_id")

        known = set(step_ids)
        for step in self.steps:
            command_type = str(step.command_type or "").strip()
            if command_type not in ALLOWED_EXECUTION_PLAN_COMMAND_TYPES:
                raise ValueError(f"unknown_command_type:{command_type}")
            missing = [dep for dep in step.depends_on if dep not in known]
            if missing:
                raise ValueError(f"missing_depends_on:{','.join(missing)}")

        if not self.confirm_gates:
            self.confirm_gates = derive_confirm_gates(self.steps)

    def to_dict(self) -> dict[str, Any]:
        return {
            "plan_id": self.plan_id,
            "goal_summary": self.goal_summary,
            "steps": [
                {
                    "step_id": step.step_id,
                    "command_type": step.command_type,
                    "payload": dict(step.payload),
                    "depends_on": list(step.depends_on),
                    **({"stop_zone": step.stop_zone.value} if step.stop_zone else {}),
                }
                for step in self.steps
            ],
            "confirm_gates": [
                {
                    "step_id": gate.step_id,
                    "confirm_type": gate.confirm_type.value,
                    **({"stop_zone": gate.stop_zone.value} if gate.stop_zone else {}),
                    **({"reason": gate.reason} if gate.reason else {}),
                }
                for gate in self.confirm_gates
            ],
            "planner_trace": dict(self.planner_trace),
        }

    @classmethod
    def from_dict(cls, payload: dict[str, Any]) -> ExecutionPlan:
        steps = [
            ExecutionPlanStep(
                step_id=str(item["step_id"]),
                command_type=str(item["command_type"]),
                payload=dict(item.get("payload") or {}),
                depends_on=[str(dep) for dep in (item.get("depends_on") or [])],
                stop_zone=StopZone(item["stop_zone"]) if item.get("stop_zone") else None,
            )
            for item in (payload.get("steps") or [])
        ]
        confirm_gates = [
            ConfirmGate(
                step_id=str(item["step_id"]),
                confirm_type=ConfirmType(str(item["confirm_type"])),
                stop_zone=StopZone(item["stop_zone"]) if item.get("stop_zone") else None,
                reason=str(item.get("reason") or ""),
            )
            for item in (payload.get("confirm_gates") or [])
        ]
        return cls(
            goal_summary=str(payload.get("goal_summary") or ""),
            steps=steps,
            plan_id=str(payload.get("plan_id") or str(uuid4())),
            confirm_gates=confirm_gates,
            planner_trace=dict(payload.get("planner_trace") or {}),
        )


def derive_confirm_gates(steps: list[ExecutionPlanStep]) -> list[ConfirmGate]:
    gates: list[ConfirmGate] = []
    for step in steps:
        if step.stop_zone is None:
            continue
        gates.append(
            ConfirmGate(
                step_id=step.step_id,
                confirm_type=confirm_type_for_stop_zone(step.stop_zone),
                stop_zone=step.stop_zone,
                reason=f"stop_zone:{step.stop_zone.value}",
            )
        )
    return gates
