"""RollbackHandlerMixin — 依赖失败传播 + 阶段回滚（G9/U4）。

# TYPE_CHECKING: 由 TeamOrchestrator 组合，访问 self 共享状态
"""

from __future__ import annotations

import logging
from typing import TYPE_CHECKING

from agentkit.orchestrator.rollback import RollbackExecutor

from .plan import PhaseStatus, PlanPhase, TeamPlan

if TYPE_CHECKING:
    from .team import ExpertTeam

logger = logging.getLogger(__name__)


class RollbackHandlerMixin:
    """Mixin: 依赖失败级联标记 + 验收/回滚命令执行。由 TeamOrchestrator 组合。"""

    # Shared state provided by TeamOrchestrator (annotations only)
    _team: ExpertTeam
    _workspace_root: str | None
    _rollback_timeout: float

    async def _mark_dependents_failed(
        self, failed_phase_id: str, plan: TeamPlan, phase_results: dict[str, dict[str, object]]
    ) -> None:
        """Mark all phases that depend on the failed phase as FAILED."""
        for ph in plan.phases:
            if ph.status != PhaseStatus.PENDING:
                continue
            if failed_phase_id in ph.depends_on:
                ph.status = PhaseStatus.FAILED
                ph.result = {"error": f"Dependency phase '{failed_phase_id}' failed"}
                phase_results[ph.id] = {"error": f"Dependency '{failed_phase_id}' failed"}
                # Emit phase_failed event for cascaded failure
                await self._broadcast_event(
                    "phase_failed",
                    {
                        "phase_id": ph.id,
                        "phase_name": ph.name,
                        "error": f"Dependency phase '{failed_phase_id}' failed",
                    },
                )
                # Recursively mark their dependents
                await self._mark_dependents_failed(ph.id, plan, phase_results)

    async def _run_phase_rollback(self, plan: TeamPlan, ph: PlanPhase) -> bool:
        """G9/U4: run validation_command + rollback_command for a failed phase.

        Returns True if checkpoint save should proceed (R21 ordering).
        - Validation passes → save checkpoint (phase state recoverable)
        - Validation fails, rollback passes → save checkpoint (rolled back state)
        - Validation fails, rollback fails → skip checkpoint (broken state)
        - Subprocess spawn failure or timeout → skip checkpoint
        """
        executor = RollbackExecutor(
            working_dir=self._workspace_root,
            timeout=self._rollback_timeout,
        )
        await self._broadcast_event(
            "phase_rollback_started",
            {
                "plan_id": plan.id,
                "phase_id": ph.id,
                "phase_name": ph.name,
                "validation_command": ph.validation_command,
                "rollback_command": ph.rollback_command,
            },
        )
        # ponytail: validate first; if validation passes, rollback is skipped (no need).
        validation = await executor.validate(ph.validation_command or "")
        if validation.passed:
            await self._broadcast_event(
                "phase_rollback_completed",
                {
                    "plan_id": plan.id,
                    "phase_id": ph.id,
                    "phase_name": ph.name,
                    "rollback_executed": False,
                    "validation_passed": True,
                },
            )
            return True

        rollback = await executor.execute(ph.rollback_command or "")
        if rollback.passed:
            await self._broadcast_event(
                "phase_rollback_completed",
                {
                    "plan_id": plan.id,
                    "phase_id": ph.id,
                    "phase_name": ph.name,
                    "rollback_executed": True,
                    "validation_passed": False,
                    "rollback_stdout": rollback.stdout,
                },
            )
            return True

        logger.error(
            f"Rollback failed for phase {ph.id} ({ph.name}): exit={rollback.exit_code} stderr={rollback.stderr}"
        )
        await self._broadcast_event(
            "phase_rollback_failed",
            {
                "plan_id": plan.id,
                "phase_id": ph.id,
                "phase_name": ph.name,
                "validation_passed": False,
                "rollback_exit_code": rollback.exit_code,
                "rollback_stderr": rollback.stderr,
            },
        )
        return False
