/**
 * timmy-watch — Live terminal feed of Timmy's inner world.
 *
 * Connects to the /api/ws WebSocket and streams a human-readable,
 * ANSI-coloured log to stdout. Works in any tmux pane or Emacs buffer
 * (shell-mode, comint, vterm, eat, etc.).
 *
 * Usage:
 *   pnpm --filter @workspace/scripts timmy-watch
 *   node --import tsx/esm scripts/src/timmy-watch.ts
 *
 * Env / flags:
 *   PORT=<n>         API server port (default 3000)
 *   --port <n>       Override PORT
 *   --host <h>       Override host (default 127.0.0.1)
 *   --no-color       Disable ANSI colour
 */

// ── ANSI helpers ──────────────────────────────────────────────────────────────

const NO_COLOR =
  process.argv.includes("--no-color") ||
  process.env["NO_COLOR"] !== undefined ||
  process.env["TERM"] === "dumb";

function esc(code: string, text: string): string {
  return NO_COLOR ? text : `\x1b[${code}m${text}\x1b[0m`;
}

const c = {
  dim:     (s: string) => esc("2",     s),
  bold:    (s: string) => esc("1",     s),
  cyan:    (s: string) => esc("36",    s),
  yellow:  (s: string) => esc("33",    s),
  green:   (s: string) => esc("32",    s),
  red:     (s: string) => esc("31",    s),
  magenta: (s: string) => esc("35",    s),
  blue:    (s: string) => esc("34",    s),
  white:   (s: string) => esc("97",    s),
  bgRed:   (s: string) => esc("41;97", s),
  bCyan:   (s: string) => esc("1;36",  s),
  bGreen:  (s: string) => esc("1;32",  s),
  bYellow: (s: string) => esc("1;33",  s),
};

// ── CLI args ──────────────────────────────────────────────────────────────────

function argAfter(flag: string): string | undefined {
  const idx = process.argv.indexOf(flag);
  return idx !== -1 ? process.argv[idx + 1] : undefined;
}

const host = argAfter("--host") ?? "127.0.0.1";
const port = Number(argAfter("--port") ?? process.env["PORT"] ?? 3000);
const wsUrl = `ws://${host}:${port}/api/ws`;

// ── Timestamp ─────────────────────────────────────────────────────────────────

function ts(): string {
  return c.dim(new Date().toLocaleTimeString("en-US", { hour12: false }));
}

// ── Agent display ─────────────────────────────────────────────────────────────

const AGENT_LABELS: Record<string, string> = {
  alpha: "α orchestrator",
  beta:  "β eval        ",
  gamma: "γ work        ",
  delta: "δ lightning   ",
  timmy: "✦ timmy       ",
};

const agentStates: Record<string, string> = {
  alpha: "idle", beta: "idle", gamma: "idle", delta: "idle",
};

function agentLabel(id: string): string {
  return AGENT_LABELS[id] ?? id;
}

function stateChip(state: string): string {
  switch (state) {
    case "working":   return c.bGreen(`[${state}]`);
    case "thinking":  return c.bCyan(`[${state}]`);
    case "active":    return c.bYellow(`[${state}]`);
    case "idle":      return c.dim(`[${state}   ]`);
    default:          return c.dim(`[${state}]`);
  }
}

function agentRow(): string {
  return Object.entries(agentStates)
    .map(([id, st]) => `${c.bold(id)} ${stateChip(st)}`)
    .join("  ");
}

// ── Event pretty-printer ──────────────────────────────────────────────────────

type WsMsg = Record<string, unknown>;

function printLine(label: string, detail: string): void {
  process.stdout.write(`${ts()}  ${label}  ${detail}\n`);
}

function handleMessage(msg: WsMsg): void {
  const type = msg["type"] as string;

  switch (type) {
    case "ping":
      return; // handled externally; suppress output

    case "world_state": {
      const timmy = msg["timmyState"] as { mood: string; activity: string } | undefined;
      const agents = msg["agentStates"] as Record<string, string> | undefined;
      const events = msg["recentEvents"] as Array<{ type: string; summary: string }> | undefined;

      if (agents) Object.assign(agentStates, agents);

      printLine(
        c.bCyan("◉ connected  "),
        `${wsUrl}`,
      );
      printLine(
        c.cyan("  world     "),
        timmy
          ? `Timmy is ${c.bold(timmy.mood)} / ${c.bold(timmy.activity)}`
          : "state unknown",
      );
      printLine(c.cyan("  agents    "), agentRow());

      if (events && events.length > 0) {
        printLine(c.dim("  history   "), c.dim(`last ${events.length} events:`));
        for (const ev of events) {
          printLine(c.dim("           "), c.dim(`${ev.type}  ${ev.summary}`));
        }
      }
      printLine(c.dim("─────────────"), c.dim("live feed starting ↓"));
      break;
    }

    case "agent_state": {
      const id    = String(msg["agentId"] ?? "?");
      const state = String(msg["state"]   ?? "?");
      const prev  = agentStates[id];
      if (prev === state) return; // skip no-op transitions
      agentStates[id] = state;
      printLine(
        c.yellow("  agent     "),
        `${c.bold(agentLabel(id))}  ${c.dim(prev ?? "?")} → ${stateChip(state)}`,
      );
      break;
    }

    case "job_started": {
      const jobId   = String(msg["jobId"]   ?? "?").slice(0, 8);
      const agentId = String(msg["agentId"] ?? "?");
      printLine(
        c.bYellow("▶ job start "),
        `job ${c.bold(jobId)}  via ${c.bold(agentLabel(agentId))}`,
      );
      break;
    }

    case "job_completed": {
      const jobId   = String(msg["jobId"]   ?? "?").slice(0, 8);
      const agentId = String(msg["agentId"] ?? "?");
      printLine(
        c.bGreen("✔ job done  "),
        `job ${c.bold(jobId)}  via ${c.bold(agentLabel(agentId))}`,
      );
      break;
    }

    case "chat": {
      const agentId = String(msg["agentId"] ?? "?");
      const text    = String(msg["text"]    ?? "");
      const isTimmy = agentId === "timmy";
      const isDelta = agentId === "delta";
      const isVisitor = agentId === "visitor";

      const label = isTimmy   ? c.magenta("✦ timmy     ")
                  : isDelta   ? c.green("⚡ delta     ")
                  : isVisitor ? c.blue("👤 visitor   ")
                  :             c.blue(`💬 ${agentId.padEnd(8)}  `);

      const body = isTimmy ? c.magenta(text) : isDelta ? c.green(text) : text;
      printLine(label, body);
      break;
    }

    case "visitor_count": {
      const count = msg["count"] as number;
      printLine(c.dim("  visitors  "), c.dim(`${count} connected`));
      break;
    }

    case "agent_count": {
      const count = msg["count"] as number;
      printLine(c.dim("  agents    "), c.dim(`${count} connected`));
      break;
    }

    default: {
      const json = JSON.stringify(msg);
      printLine(c.dim("  unknown   "), c.dim(json.slice(0, 120)));
    }
  }
}

// ── WebSocket connection with auto-reconnect ──────────────────────────────────

let retryDelay = 1000;
const MAX_DELAY = 30_000;

function connect(): void {
  printLine(c.dim("  connecting"), c.dim(wsUrl));

  const ws = new WebSocket(wsUrl);

  ws.addEventListener("open", () => {
    retryDelay = 1000;
    ws.send(JSON.stringify({ type: "subscribe" }));
    ws.send(JSON.stringify({ type: "visitor_enter", visitorId: "timmy-watch-cli" }));
  });

  ws.addEventListener("message", (ev) => {
    try {
      const msg = JSON.parse(String(ev.data)) as WsMsg;
      if (msg["type"] === "ping") {
        ws.send(JSON.stringify({ type: "pong" }));
        return;
      }
      handleMessage(msg);
    } catch {
      printLine(c.red("  parse err "), String(ev.data).slice(0, 80));
    }
  });

  ws.addEventListener("close", (ev) => {
    printLine(
      c.dim("  closed    "),
      c.dim(`code=${ev.code}  retrying in ${retryDelay / 1000}s…`),
    );
    setTimeout(() => {
      retryDelay = Math.min(retryDelay * 2, MAX_DELAY);
      connect();
    }, retryDelay);
  });

  ws.addEventListener("error", () => {
    printLine(c.red("  ws error  "), c.red(`cannot reach ${wsUrl}`));
  });
}

// ── Banner ────────────────────────────────────────────────────────────────────

const banner = [
  "",
  c.bold(c.magenta("  TIMMY WATCH")),
  c.dim(`  ${wsUrl}  ·  Ctrl-C to quit`),
  "",
].join("\n");

process.stdout.write(banner + "\n");
connect();
