"""Terminal display helpers for GenWorker.

Provides ANSI-coloured, Unicode box-drawing output that degrades gracefully:
- In a real terminal   → full colour + box-drawing characters
- Inside the GUI       → plain emojis + text (stdout.isatty() == False)
- Piped / redirected   → same plain fallback

Nothing here imports from genworker.config to keep the dependency graph clean.
"""

from __future__ import annotations

import shutil
import sys
import textwrap
import time


# ── ANSI helpers ───────────────────────────────────────────────────────────────

def _tty() -> bool:
    """Re-checked every call so it works correctly inside the GUI thread."""
    return getattr(sys.stdout, "isatty", lambda: False)()


def _esc(code: str, text: str) -> str:
    return f"\033[{code}m{text}\033[0m" if _tty() else text


def bold(t: str)    -> str: return _esc("1",  t)
def dim(t: str)     -> str: return _esc("2",  t)
def red(t: str)     -> str: return _esc("91", t)
def green(t: str)   -> str: return _esc("92", t)
def yellow(t: str)  -> str: return _esc("93", t)
def blue(t: str)    -> str: return _esc("94", t)
def magenta(t: str) -> str: return _esc("95", t)
def cyan(t: str)    -> str: return _esc("96", t)
def white(t: str)   -> str: return _esc("97", t)


# ── Terminal width ─────────────────────────────────────────────────────────────

def _w() -> int:
    """Clamped terminal width (60–100 chars)."""
    return max(60, min(shutil.get_terminal_size((80, 24)).columns, 100))


# ── Box-drawing primitives ─────────────────────────────────────────────────────

def _hline(ch: str, width: int) -> str:
    return ch * width


def _box_top(width: int, dbl: bool = False) -> str:
    tl, tr, h = ("╔", "╗", "═") if dbl else ("┌", "┐", "─")
    return tl + _hline(h, width - 2) + tr


def _box_bot(width: int, dbl: bool = False) -> str:
    bl, br, h = ("╚", "╝", "═") if dbl else ("└", "┘", "─")
    return bl + _hline(h, width - 2) + br


def _box_div(width: int, dbl: bool = False) -> str:
    ml, mr, h = ("╠", "╣", "═") if dbl else ("├", "┤", "─")
    return ml + _hline(h, width - 2) + mr


def _box_row(text: str, width: int, dbl: bool = False) -> str:
    v = "║" if dbl else "│"
    inner = width - 4            # 2 border chars + 2 padding spaces
    padded = text.ljust(inner)[:inner]
    return f"{v} {padded} {v}"


# ── Public formatting functions ────────────────────────────────────────────────

def banner(
    version: str,
    model: str,
    monitor_index: int,
    monitor_name: str,
    monitor_res: str,
    thinking: bool,
    grid: bool,
    timeout: int,
    max_steps: int,
    task: str,
) -> None:
    """Print the startup banner."""
    w = _w()

    title  = f"🤖  G E N W O R K E R  {version}"
    sub    = "Desktop AI Agent  ·  Claude Computer Use"
    mon    = f"🖥   Monitor [{monitor_index}]  {monitor_name}  {monitor_res}"
    mdl    = f"🧠  Model: {model}  ·  Thinking: {'ON' if thinking else 'OFF'}"
    grd    = f"🔲  Grid overlay: {'ON' if grid else 'OFF'}  ·  Timeout: {timeout}s  ·  Max steps: {max_steps}"

    # Wrap long task lines
    task_lines = textwrap.wrap(task, width=w - 6) or [task]

    print()
    print(cyan(bold(_box_top(w, dbl=True))))
    print(cyan(bold(_box_row("", w, dbl=True))))
    print(cyan(bold(_box_row(f"  {title}", w, dbl=True))))
    print(cyan(_box_row(f"  {sub}", w, dbl=True)))
    print(cyan(bold(_box_row("", w, dbl=True))))
    print(cyan(_box_div(w, dbl=True)))
    print(cyan(_box_row(f"  {mon}", w, dbl=True)))
    print(cyan(_box_row(f"  {mdl}", w, dbl=True)))
    print(cyan(_box_row(f"  {grd}", w, dbl=True)))
    print(cyan(_box_div(w, dbl=True)))
    print(cyan(_box_row(f"  📋  Task:", w, dbl=True)))
    for line in task_lines:
        print(cyan(_box_row(f"     {line}", w, dbl=True)))
    print(cyan(bold(_box_bot(w, dbl=True))))
    print()
    print(dim("  🛑  Emergency stop: Ctrl+C  |  or move mouse to the top-left corner"))
    print()


def print_plan(plan: str) -> None:
    """Display the execution plan with a framed header."""
    w = _w()
    print()
    print(yellow(_box_top(w)))
    print(yellow(_box_row("  📋  EXECUTION PLAN", w)))
    print(yellow(_box_div(w)))
    # Print each non-empty line indented
    for line in plan.splitlines():
        chunks = textwrap.wrap(line, width=w - 6) if line.strip() else [""]
        for chunk in chunks:
            print(yellow(_box_row(f"  {chunk}", w)))
    print(yellow(_box_bot(w)))
    print()


def print_analyzing() -> None:
    print(cyan("  ⏳  Analyzing task and building execution plan…"))


def step_header(step: int, max_steps: int, elapsed: float) -> None:
    """Print a visual step separator."""
    w = _w()
    elapsed_str = f"⏱  {elapsed:.0f}s"
    label       = f"  Step {step:>3}/{max_steps}  "
    fill_len    = w - len(label) - len(elapsed_str) - 4
    fill        = "─" * max(fill_len, 2)
    line        = f"┌{label}{fill}  {elapsed_str}  ┐"
    # Trim/pad to width
    line = line.ljust(w)[:w]
    print()
    print(blue(line))


def step_footer() -> None:
    w = _w()
    print(dim(blue("└" + "─" * (w - 2) + "┘")))


def print_thinking(text: str, max_chars: int = 220) -> None:
    snippet = text.strip().replace("\n", " ")[:max_chars]
    if len(text) > max_chars:
        snippet += "…"
    print(magenta(f"  ◌  {dim('thinking')}  {snippet}"))


def print_claude(text: str) -> None:
    """Print Claude's text response, word-wrapped."""
    w   = _w()
    pad = "  🧠  "
    cont = "       "
    lines = text.strip().splitlines()
    for i, raw in enumerate(lines):
        prefix = pad if i == 0 else cont
        wrapped = textwrap.wrap(raw or " ", width=w - len(cont))
        for j, chunk in enumerate(wrapped or [""]):
            pfx = prefix if (i == 0 and j == 0) else cont
            print(white(pfx + chunk))


def print_tool(emoji: str, name: str, result: str, max_chars: int = 180) -> None:
    """Print a single tool execution line."""
    snippet = result.strip().replace("\n", "↵ ")[:max_chars]
    if len(result) > max_chars:
        snippet += "…"
    label   = f"{name}"
    is_err  = result.startswith("Error") or result.startswith("⛔")
    fmt     = red if is_err else green
    prefix  = f"  {emoji}  {cyan(bold(label))} › "
    print(prefix + fmt(snippet))


def print_nudge(count: int) -> None:
    print(yellow(f"  ⚠️   Claude stopped without completing — nudge #{count}…"))


def print_max_nudges() -> None:
    print(yellow("  ⚠️   Max nudges reached — forcing final assessment…"))


def print_stuck_loop() -> None:
    print(yellow("  🔄  Stuck-loop detected — injecting recovery prompt…"))


def print_timeout(timeout: int, elapsed: float) -> None:
    print(yellow(f"\n  ⏱️   Timeout reached ({timeout}s / {elapsed:.0f}s elapsed) — requesting summary…"))


def print_api_error(error: str, attempt: int, wait: int) -> None:
    print(red(f"  ❌  API error (attempt {attempt}): {error}"))
    print(dim(f"      Retrying in {wait}s…"))


def print_api_give_up(attempts: int) -> None:
    print(red(f"  ❌  Giving up after {attempts} consecutive API failures."))


def print_context_compact(image_count: int) -> None:
    print(dim(f"  🗜   Context compaction: trimming old screenshots ({image_count} images)…"))


def print_success(steps: int, elapsed: float, in_tok: int, out_tok: int) -> None:
    w = _w()
    cost_line = f"  📊  {in_tok:,} in  /  {out_tok:,} out tokens"
    time_line = f"  ✅  Completed in {steps} step(s)  ·  {elapsed:.0f}s"
    print()
    print(green(bold(_box_top(w, dbl=True))))
    print(green(bold(_box_row("", w, dbl=True))))
    print(green(bold(_box_row("  🎉  TASK COMPLETE", w, dbl=True))))
    print(green(_box_row(time_line, w, dbl=True)))
    print(green(_box_row(cost_line, w, dbl=True)))
    print(green(bold(_box_row("", w, dbl=True))))
    print(green(bold(_box_bot(w, dbl=True))))
    print()


def print_max_steps(steps: int, elapsed: float) -> None:
    print(yellow(f"\n  ⚠️   Reached max steps ({steps}) after {elapsed:.0f}s."))


def print_final_stats(steps: int, elapsed: float, in_tok: int, out_tok: int, errors: int) -> None:
    w = _w()
    print()
    print(dim("─" * w))
    print(dim(f"  📊  Steps: {steps}  ·  Time: {elapsed:.0f}s"))
    print(dim(f"      Tokens: {in_tok:,} in  /  {out_tok:,} out"))
    if errors:
        print(dim(f"      Errors recorded: {errors}"))
    print(dim("─" * w))
    print()


def print_interrupted() -> None:
    print(yellow("\n\n  🛑  Interrupted."))


def countdown(seconds: int) -> None:
    """Show a short animated countdown."""
    if not _tty():
        print(f"  Starting in {seconds}s…")
        time.sleep(seconds)
        return
    for i in range(seconds, 0, -1):
        sys.stdout.write(f"\r  {cyan('▶')}  Starting in {bold(str(i))}s…  ")
        sys.stdout.flush()
        time.sleep(1)
    sys.stdout.write("\r" + " " * 40 + "\r")
    sys.stdout.flush()
