from __future__ import annotations

from collections import deque
from dataclasses import dataclass, field
from pathlib import Path

from rich.console import Group
from rich.layout import Layout
from rich.live import Live
from rich.panel import Panel
from rich.rule import Rule
from rich.table import Table
from rich.text import Text

from dont_hallucinate.runtime import is_within_workspace
from dont_hallucinate.burn import load_snark_map, pick_roast
from dont_hallucinate.watcher import FailureReport, tail_jsonl, to_failure_report


@dataclass(slots=True)
class UIState:
    logs: deque[str] = field(default_factory=lambda: deque(maxlen=18))
    latest_report: FailureReport | None = None
    latest_roast: str = ""


def _short_stderr(stderr: str) -> str:
    line = (stderr or "").strip().splitlines()
    first = line[0] if line else "(no stderr)"
    return first if len(first) <= 120 else first[:117] + "..."


def _event_log_line(report: FailureReport) -> str:
    event = report.event
    return (
        f"[{event.ts}] {event.actor or 'unknown'} {event.cwd or '?'} exit={event.exit_code} cmd=`{event.command}` "
        f"-> {_short_stderr(event.stderr)}"
    )


def _banner() -> Text:
    return Text(
        "\n".join(
            [
                " _   _    _    _     _     _   _  ____ ___ _   _    _  _____ _____ ",
                "| | | |  / \\  | |   | |   | | | |/ ___|_ _| \\ | |  / \\|_   _| ____|",
                "| |_| | / _ \\ | |   | |   | | | | |    | ||  \\| | / _ \\ | | |  _|  ",
                "|  _  |/ ___ \\| |___| |___| |_| | |___ | || |\\  |/ ___ \\| | | |___ ",
                "|_| |_/_/   \\_\\_____|_____|\\___/ \\____|___|_| \\_/_/   \\_\\_| |_____|",
            ]
        ),
        style="bold cyan",
    )


def _recent_lines(state: UIState) -> Text:
    if not state.logs:
        return Text("No intercepted failures yet.", style="dim")
    return Text("\n".join(list(state.logs)[-6:]))


def _build_main_panel(state: UIState) -> Panel:
    if not state.latest_report:
        return Panel(
            Group(
                _banner(),
                Text(""),
                Text("Watching this workspace for shell failures...", style="dim"),
            ),
            title="Hallucinate",
            border_style="cyan",
        )

    report = state.latest_report
    parsed = report.parsed
    verify = report.verification

    table = Table.grid(padding=(0, 1))
    table.add_column(style="bold")
    table.add_column()
    table.add_row("Actor", report.event.actor or "unknown")
    table.add_row("Error Type", parsed.error_type)
    table.add_row("Caught", report.event.command.strip() or "(empty)")
    table.add_row("Fix", verify.corrected_command or verify.suggested_flag or "No safe correction found.")

    if parsed.hallucinated_flag:
        table.add_row("Bad Flag", parsed.hallucinated_flag)
    if verify.suggested_flag:
        table.add_row("Suggested", verify.suggested_flag)
    if verify.corrected_command:
        table.add_row("Corrected", verify.corrected_command)
    table.add_row("Verified Via", verify.source)

    roast = Text(state.latest_roast, style="bold yellow")
    return Panel(
        Group(
            _banner(),
            Text(""),
            table,
            Text(""),
            roast,
            Text(""),
            Rule("Recent Failures", style="cyan"),
            _recent_lines(state),
        ),
        title="Hallucinate",
        border_style="cyan",
    )


def _render_layout(state: UIState) -> Layout:
    layout = Layout()
    layout.update(_build_main_panel(state))
    return layout


def run_ui(
    stream_file: Path,
    poll_interval: float = 0.2,
    from_start: bool = False,
    workspace: Path | None = None,
) -> None:
    state = UIState()
    snark = load_snark_map()

    with Live(_render_layout(state), refresh_per_second=8, screen=False) as live:
        for event in tail_jsonl(stream_file, poll_interval=poll_interval, from_start=from_start):
            if event.exit_code == 0:
                continue
            if not is_within_workspace(event.cwd, workspace):
                continue
            report = to_failure_report(event)
            state.latest_report = report
            state.latest_roast = pick_roast(snark, report.parsed.error_type)
            state.logs.append(_event_log_line(report))
            live.update(_render_layout(state))
