"""Global mutable state shared across installer, UI, and agent.

Created once in the App and passed by reference to StepRunner,
AgentBrain, and UI widgets.

The rolling log_buffer (deque) is the agent's "eyes" — it silently
receives the last 100 lines of subprocess output, which gets injected
into the system prompt with every agent call (Phase 3).

The pid_lock prevents races when the agent's interrupt_active_step
tool kills a process while the StepRunner is also manipulating it.
"""

from __future__ import annotations

import asyncio
from collections import deque
from dataclasses import dataclass, field
from typing import Optional


@dataclass
class InstallState:
    """Shared mutable state for the entire installer lifecycle."""

    # -- Subprocess tracking --
    active_pid: Optional[int] = None
    active_process: Optional[asyncio.subprocess.Process] = None

    # -- Step tracking --
    current_step_index: int = 0
    total_steps: int = 0
    current_step_name: str = ""
    is_running: bool = False
    is_paused: bool = False
    is_complete: bool = False
    has_error: bool = False

    # -- Step results (name, success) for UI tracking --
    step_results: list = field(default_factory=list)

    # -- Rolling log buffer (agent reads this every prompt) --
    log_buffer: deque = field(
        default_factory=lambda: deque(maxlen=100)
    )

    # -- Lock for safe PID read/write across installer + agent --
    pid_lock: asyncio.Lock = field(default_factory=asyncio.Lock)

    # -- Main event loop reference (for agent thread -> main loop calls) --
    main_loop: Optional[asyncio.AbstractEventLoop] = None

    def snapshot_logs(self) -> str:
        """Return the rolling buffer as a single string for prompt injection."""
        return "\n".join(self.log_buffer)

    async def set_active_process(
        self, proc: asyncio.subprocess.Process
    ) -> None:
        """Register the currently running subprocess (under lock)."""
        async with self.pid_lock:
            self.active_process = proc
            self.active_pid = proc.pid

    async def clear_active_process(self) -> None:
        """Clear the active subprocess reference (under lock)."""
        async with self.pid_lock:
            self.active_process = None
            self.active_pid = None
