"""StepRunner — orchestrates sequential execution of installation steps.

Drives the step machine: iterate through STEP_REGISTRY, run each step
with retry logic, and post Textual messages for UI/status updates.

Runs as an async worker on the main Textual loop via @work(exclusive=True).
"""

from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from textual.app import App

    from ..state import InstallState

from ..messages import InstallFinished, LogLine, StepCompleted, StepStarted
from .steps import STEP_REGISTRY


class StepRunner:
    """Orchestrates sequential execution of all installation steps."""

    def __init__(self, state: InstallState, app: App) -> None:
        self.state = state
        self.app = app
        self.steps = list(STEP_REGISTRY)

    async def run_all_steps(self) -> None:
        """Execute every step in order.  Stops on unrecoverable failure."""
        self.state.is_running = True
        self.state.total_steps = len(self.steps)

        for i, step in enumerate(self.steps, start=1):
            self.state.current_step_index = i
            self.state.current_step_name = step.name

            # --- Notify UI ---
            self.app.post_message(StepStarted(i, step.name, len(self.steps)))
            self.app.post_message(
                LogLine(
                    f"\n{'=' * 50}\n"
                    f"  Step {i}/{len(self.steps)}: {step.name}\n"
                    f"  {step.description}\n"
                    f"{'=' * 50}"
                )
            )

            # --- Precondition check ---
            ok, reason = step.validate_preconditions()
            if not ok:
                self.app.post_message(
                    LogLine(f"[SKIP] Precondition failed: {reason}", is_stderr=True)
                )
                self.app.post_message(
                    StepCompleted(i, step.name, success=False, error=reason)
                )
                self.state.has_error = True
                break

            # --- Execute with retries ---
            success = False
            last_error = ""
            attempts = (step.max_retries + 1) if step.allow_retry else 1

            for attempt in range(1, attempts + 1):
                if attempt > 1:
                    self.app.post_message(
                        LogLine(f"  [RETRY] Attempt {attempt}/{attempts}...")
                    )

                success = await step.execute(self.state, self.app)

                if success:
                    break

                last_error = f"Attempt {attempt}/{attempts} failed (exit code non-zero)"

            # --- Post result ---
            if success:
                self.app.post_message(
                    LogLine(f"  [OK] {step.name} completed successfully.")
                )
            else:
                self.app.post_message(
                    LogLine(
                        f"  [FAIL] {step.name} — {last_error}",
                        is_stderr=True,
                    )
                )

            self.app.post_message(
                StepCompleted(i, step.name, success, last_error)
            )

            if not success:
                self.state.has_error = True
                # Stop the pipeline — in Phase 3 the agent can intervene
                break

        self.state.is_running = False
        self.state.is_complete = True
        self.app.post_message(InstallFinished(success=not self.state.has_error))

    async def resume_from(self, from_step: int) -> None:
        """Resume the pipeline from a specific step number (1-based).

        Resets error/completion state, then runs steps from from_step onward
        using the same logic as run_all_steps.
        """
        self.state.is_running = True
        self.state.is_complete = False
        self.state.has_error = False
        self.state.total_steps = len(self.steps)

        self.app.post_message(
            LogLine(
                f"\n{'#' * 50}\n"
                f"  Resuming pipeline from step {from_step}\n"
                f"{'#' * 50}"
            )
        )

        for i, step in enumerate(self.steps, start=1):
            if i < from_step:
                continue

            self.state.current_step_index = i
            self.state.current_step_name = step.name

            # --- Notify UI ---
            self.app.post_message(StepStarted(i, step.name, len(self.steps)))
            self.app.post_message(
                LogLine(
                    f"\n{'=' * 50}\n"
                    f"  Step {i}/{len(self.steps)}: {step.name}\n"
                    f"  {step.description}\n"
                    f"{'=' * 50}"
                )
            )

            # --- Precondition check ---
            ok, reason = step.validate_preconditions()
            if not ok:
                self.app.post_message(
                    LogLine(f"[SKIP] Precondition failed: {reason}", is_stderr=True)
                )
                self.app.post_message(
                    StepCompleted(i, step.name, success=False, error=reason)
                )
                self.state.has_error = True
                break

            # --- Execute with retries ---
            success = False
            last_error = ""
            attempts = (step.max_retries + 1) if step.allow_retry else 1

            for attempt in range(1, attempts + 1):
                if attempt > 1:
                    self.app.post_message(
                        LogLine(f"  [RETRY] Attempt {attempt}/{attempts}...")
                    )

                success = await step.execute(self.state, self.app)

                if success:
                    break

                last_error = f"Attempt {attempt}/{attempts} failed (exit code non-zero)"

            # --- Post result ---
            if success:
                self.app.post_message(
                    LogLine(f"  [OK] {step.name} completed successfully.")
                )
            else:
                self.app.post_message(
                    LogLine(
                        f"  [FAIL] {step.name} — {last_error}",
                        is_stderr=True,
                    )
                )

            self.app.post_message(
                StepCompleted(i, step.name, success, last_error)
            )

            if not success:
                self.state.has_error = True
                break

        self.state.is_running = False
        self.state.is_complete = True
        self.app.post_message(InstallFinished(success=not self.state.has_error))
