"""Live agent status bar for the terminal.

Tracks background agents that the master spawns via the Task tool and
prints a compact one-liner whenever the set changes:

    AGENTS  🔍 research  💻 build-ui  ⚙️ run-tests

Icons are assigned by keyword-matching the agent description:
  🔍  research / search / explore / look / find
  💻  cod / build / implement / fix / write / edit / creat
  🌐  web / fetch / brows / http / url / crawl / scrape
  ✅  done (briefly, before removal)
  🤖  everything else (general AI agent work)

The module is self-contained; nothing in it imports from the rest of the
voice package, so it can be imported anywhere without circular deps.
"""

from __future__ import annotations

import threading
import time
from dataclasses import dataclass, field


@dataclass
class _Agent:
    agent_type: str   # "worker" | "heavy" | "genius"
    desc: str
    task_id: str = ""   # SDK task_id — clears THIS agent when its status lands
    done: bool = False
    note: str = ""      # live "what it's doing right now" (agent pool reader)
    stalled: bool = False
    started_at: float = field(default_factory=time.monotonic)

    @property
    def icon(self) -> str:
        if self.done:
            return "✅"   # ✅
        if self.stalled:
            return "⚠️"  # ⚠️ — gone quiet, may be stuck
        d = self.desc.lower()
        if any(k in d for k in ("research", "search", "explor", "look", "find",
                                 "investigat", "audit", "analys", "analyz",
                                 "read", "check")):
            return "\U0001f50d"  # 🔍
        if any(k in d for k in ("cod", "build", "implement", "fix", "write",
                                 "edit", "creat", "develop", "refactor",
                                 "add", "generat", "compil")):
            return "\U0001f4bb"  # 💻
        if any(k in d for k in ("web", "fetch", "brows", "http", "url",
                                 "crawl", "scrape", "download", "open")):
            return "\U0001f310"  # 🌐
        if any(k in d for k in ("run", "execut", "test", "deploy", "start",
                                 "launch", "install", "pip", "npm")):
            return "⚙️"  # ⚙️
        return "\U0001f916"  # 🤖

    def label(self, max_chars: int = 22) -> str:
        """Short label: the task, plus what it's doing right now if known."""
        t = self.desc.strip()
        t = t if len(t) <= max_chars else t[: max_chars - 1] + "…"
        if self.note and not self.done:
            n = self.note.strip()
            n = n if len(n) <= 28 else n[:27] + "…"
            return f"{t} · {n}"
        return t


class AgentRegistry:
    """Thread-safe registry; print_bar() to stdout whenever state changes."""

    _DONE_TTL = 3.0   # seconds to keep a ✅ chip before removing it

    def __init__(self) -> None:
        self._lock = threading.Lock()
        self._agents: list[_Agent] = []
        self._last_bar: str = ""

    # ------------------------------------------------------------------
    # Public API (called from the event hook in events.py)
    # ------------------------------------------------------------------

    def add(self, agent_type: str, desc: str, task_id: str = "") -> None:
        with self._lock:
            if task_id and any(a.task_id == task_id for a in self._agents):
                return  # already shown
            self._agents.append(
                _Agent(agent_type=agent_type, desc=desc, task_id=task_id))
        self._render()

    def progress(self, task_id: str, note: str) -> None:
        """Live note from the agent pool's supervisor: what this worker is
        doing RIGHT NOW (tool by tool). Ahmed's 'no clue what it's doing'."""
        with self._lock:
            for a in self._agents:
                if a.task_id == task_id:
                    a.note = note
                    a.stalled = False
                    break
        self._render()

    def stalled(self, task_id: str) -> None:
        with self._lock:
            for a in self._agents:
                if a.task_id == task_id:
                    a.stalled = True
                    break
        self._render()

    def finish_one(self, task_id: str = "") -> None:
        """Mark the agent with `task_id` complete (else the oldest running one)
        and schedule its removal — live per-agent, no fixed timeout."""
        with self._lock:
            target = None
            if task_id:
                target = next((a for a in self._agents
                               if a.task_id == task_id and not a.done), None)
            if target is None:
                target = next((a for a in self._agents if not a.done), None)
            if target is not None:
                target.done = True
        self._render()
        # remove the ✅ chip after a brief pause
        t = threading.Thread(target=self._remove_done_after_delay, daemon=True)
        t.start()

    def _remove_done_after_delay(self) -> None:
        time.sleep(self._DONE_TTL)
        with self._lock:
            # remove the first done agent (FIFO)
            for i, a in enumerate(self._agents):
                if a.done:
                    del self._agents[i]
                    break
        self._render()

    # ------------------------------------------------------------------
    # Rendering
    # ------------------------------------------------------------------

    def _render(self) -> None:
        with self._lock:
            agents = list(self._agents)

        if not agents:
            if self._last_bar:
                # clear the old bar line
                print("", flush=True)
                self._last_bar = ""
            return

        chips = [f"{a.icon} {a.label()}" for a in agents]
        bar = "AGENTS  " + "   ".join(chips)
        if bar == self._last_bar:
            return
        self._last_bar = bar
        # Print on its own line with a separating newline so it stands out
        # between other log lines without mangling them.
        print(f"\n{bar}\n", flush=True)


# Module-level singleton — imported and used by voice/events.py
registry = AgentRegistry()
