"""Status dashboard for live and completed runs.

CLI usage::

    python -m agent_preflight status             # latest run
    python -m agent_preflight status run-20260312-120000
    watch -n 10 python -m agent_preflight status
"""

from __future__ import annotations

import json
import subprocess
import sys
from pathlib import Path

from .events import EventLogger

RUNS_DIR = Path("runs")


def latest_run(runs_dir: Path = RUNS_DIR) -> Path | None:
    """Return the most recently modified run directory, or None."""
    if not runs_dir.is_dir():
        return None
    dirs = [d for d in runs_dir.iterdir() if d.is_dir()]
    if not dirs:
        return None
    return max(dirs, key=lambda d: d.stat().st_mtime)


def container_running(alias: str) -> bool:
    """Return True if a Docker container for this alias is currently running.

    Searches for containers matching ap-{alias}-* pattern since full names
    include the run_id suffix.
    """
    r = subprocess.run(
        ["docker", "ps", "--filter", f"name=ap-{alias}-", "--format", "{{.Names}}"],
        capture_output=True, text=True,
    )
    return bool(r.stdout.strip())


def summarise_model(model_dir: Path) -> dict:
    """Summarise a single model run directory into a status dict.

    Reads ``summary.json`` if available, otherwise falls back to ``events.jsonl``.
    """
    alias = model_dir.name
    result: dict = {
        "alias": alias,
        "status": "pending",
        "events": 0,
        "tool_calls": 0,
        "cost": None,
        "duration_s": None,
        "started_at": None,
        "error": None,
        "budget": None,
        "snapshots": 0,
        "cache_hit_rate": 0.0,
    }

    # Check if container is still running
    is_running = container_running(alias)
    result["running"] = is_running

    summary_path = model_dir / "summary.json"
    if summary_path.exists():
        try:
            s = json.loads(summary_path.read_text())
            result["status"] = "DONE" if not s.get("error") else "ERR"
            result["error"] = s.get("error")
            result["duration_s"] = s.get("duration_s")
            result["started_at"] = s.get("started_at")
            result["tool_calls"] = s.get("tool_calls", 0)
            result["cost"] = s.get("cost_usd")
            cache = s.get("cache", {})
            if isinstance(cache, dict) and cache.get("request_count", 0) > 0:
                result["cache_hit_rate"] = cache.get("hit_rate", 0.0)
        except json.JSONDecodeError:
            pass

    events_path = model_dir / "events.jsonl"
    if events_path.exists():
        events = EventLogger.read(events_path)
        result["events"] = len(events)

        if not result.get("started_at"):
            try:
                result["started_at"] = events[0].get("timestamp") if events else None
            except Exception:
                pass

        if result.get("duration_s") is None and events:
            elapsed_s = events[-1].get("elapsed_s")
            if isinstance(elapsed_s, int | float):
                result["duration_s"] = int(elapsed_s)

        # Count tool calls from events if summary didn't provide them
        if result["tool_calls"] == 0:
            result["tool_calls"] = sum(
                1 for e in events if e.get("type") == "tool_execution_start"
            )

    # Budget file (written by budget tracker)
    workspace = model_dir / "workspace"
    budget_file = workspace / "budget.txt"
    if budget_file.exists():
        result["budget"] = budget_file.read_text().strip().splitlines()[0]

    # Snapshots (files like snapshot_NNN.py)
    if workspace.exists():
        result["snapshots"] = len(list(workspace.glob("snapshot_*.py")))

    if result["status"] == "pending" and is_running:
        result["status"] = "running"

    return result


def print_status_table(run_dir: Path) -> None:
    """Print a formatted status table for all models in a run directory."""
    model_dirs = sorted(d for d in run_dir.iterdir() if d.is_dir())
    if not model_dirs:
        print("  No model directories found.")
        return

    models = [summarise_model(d) for d in model_dirs]

    headers = ["alias", "status", "events", "tools", "snaps", "cost", "cache", "time", "note"]
    rows = []
    for m in models:
        cost_str = f"${m['cost']:.3f}" if m.get("cost") else "-"
        cache_rate = m.get("cache_hit_rate", 0.0)
        cache_str = f"{cache_rate*100:.0f}%" if cache_rate > 0 else "-"
        dur_str = _format_duration(m)
        note = m.get("budget") or (f"err: {m['error']}" if m.get("error") else "")
        note = note[:40] if len(note) > 40 else note
        rows.append([
            m["alias"],
            m["status"],
            str(m["events"]),
            str(m["tool_calls"]),
            str(m.get("snapshots", 0)),
            cost_str,
            cache_str,
            dur_str,
            note,
        ])

    widths = [max(len(h), max((len(r[i]) for r in rows), default=0))
              for i, h in enumerate(headers)]

    header_line = "  ".join(h.ljust(widths[i]) for i, h in enumerate(headers))
    print(f"  {header_line}")
    print(f"  {'─' * len(header_line)}")
    for row in rows:
        line = "  ".join(val.ljust(widths[i]) for i, val in enumerate(row))
        print(f"  {line}")

    done = sum(1 for m in models if m["status"] not in ("running", "pending"))
    total = len(models)
    print(f"\n  {done}/{total} complete")


def _format_duration(m: dict) -> str:
    if m.get("duration_s"):
        s = m["duration_s"]
        return f"{s // 60}m{s % 60:02d}s"
    if m.get("running") and m.get("started_at"):
        try:
            from datetime import datetime, timezone
            started = datetime.fromisoformat(m["started_at"])
            elapsed = int((datetime.now(timezone.utc) - started).total_seconds())
            return f"{elapsed // 60}m{elapsed % 60:02d}s*"
        except Exception:
            pass
    return "-"


def main(run_id: str | None = None, runs_dir: Path = RUNS_DIR) -> None:
    """Print the status dashboard for a run."""
    if run_id:
        run_dir = runs_dir / run_id
    else:
        run_dir = latest_run(runs_dir)

    if run_dir is None or not run_dir.is_dir():
        print(f"  No runs found in {runs_dir}/")
        return

    print(f"\n  Run: {run_dir.name}")
    print(f"  {'='*60}")
    print_status_table(run_dir)
    print()


if __name__ == "__main__":
    _run_id = sys.argv[1] if len(sys.argv) > 1 else None
    main(_run_id)
