import {
  gatherConsoleState
} from "./chunk-TYEOAFH3.js";

// src/console/tui.ts
var ANSI_CLEAR_HOME = "\x1B[2J\x1B[H";
var ANSI_HIDE_CURSOR = "\x1B[?25l";
var ANSI_SHOW_CURSOR = "\x1B[?25h";
var FRAME_INNER_WIDTH = 70;
var DEFAULT_REFRESH_INTERVAL_MS = 2e3;
function runConsoleTui(orchestrator, options = {}) {
  const refreshIntervalMs = Math.max(
    50,
    options.refreshIntervalMs ?? DEFAULT_REFRESH_INTERVAL_MS
  );
  const output = options.output ?? process.stdout;
  const now = options.now ?? (() => Date.now());
  const installSigintHandler = options.installSigintHandler ?? true;
  let stopped = false;
  let inFlight = false;
  let inFlightTickPromise = null;
  let traceWritePending = false;
  let traceFramesDropped = 0;
  let resolveDone;
  const done = new Promise((resolve) => {
    resolveDone = resolve;
  });
  safeWrite(output, ANSI_HIDE_CURSOR);
  const tick = async () => {
    if (stopped || inFlight) return;
    inFlight = true;
    try {
      let snapshot = null;
      let renderError = null;
      try {
        snapshot = await gatherConsoleState(orchestrator);
      } catch (err) {
        renderError = describeError(err);
      }
      if (stopped) return;
      let frame;
      try {
        frame = renderFrame({ snapshot, renderError, now });
      } catch (err) {
        frame = `remnic console: render failed: ${describeError(err)}
`;
      }
      safeWrite(output, ANSI_CLEAR_HOME);
      safeWrite(output, frame);
      if (snapshot && options.traceRecorder && !traceWritePending) {
        traceWritePending = true;
        void options.traceRecorder.append(snapshot).catch(() => {
        }).finally(() => {
          traceWritePending = false;
        });
      } else if (snapshot && options.traceRecorder) {
        traceFramesDropped += 1;
      }
    } finally {
      inFlight = false;
    }
  };
  const launchTick = () => {
    const p = runTickSafely(tick);
    inFlightTickPromise = p;
    void p.then(() => {
      if (inFlightTickPromise === p) {
        inFlightTickPromise = null;
      }
    });
  };
  launchTick();
  const handle = setInterval(launchTick, refreshIntervalMs);
  const sigintHandler = () => {
    stop();
  };
  if (installSigintHandler) {
    process.on("SIGINT", sigintHandler);
  }
  const stop = () => {
    if (stopped) return;
    stopped = true;
    clearInterval(handle);
    if (installSigintHandler) {
      try {
        process.removeListener("SIGINT", sigintHandler);
      } catch {
      }
    }
    safeWrite(output, ANSI_SHOW_CURSOR);
    void (async () => {
      try {
        const pendingTick = inFlightTickPromise;
        if (pendingTick) {
          await pendingTick;
        }
      } finally {
        resolveDone();
      }
    })();
  };
  return {
    stop,
    done,
    getDroppedTraceFrames: () => traceFramesDropped
  };
}
function renderFrame(input) {
  const ts = new Date(input.now()).toISOString();
  const lines = [];
  lines.push(renderHeader(ts));
  for (const panel of renderPanels(input)) {
    lines.push(panel);
  }
  lines.push(renderFooter());
  return lines.join("\n") + "\n";
}
function renderHeader(ts) {
  const title = " remnic console ";
  const trailing = ` ${ts} `;
  const fillerLen = Math.max(
    1,
    FRAME_INNER_WIDTH - title.length - trailing.length
  );
  const filler = "\u2550".repeat(fillerLen);
  return `\u2554${title}${filler}${trailing}\u2557`;
}
function renderFooter() {
  return `\u255A${"\u2550".repeat(FRAME_INNER_WIDTH)}\u255D`;
}
function renderPanels(input) {
  const lines = [];
  const snap = input.snapshot;
  if (input.renderError !== null) {
    lines.push(panelLine("Error", `refresh failed: ${input.renderError}`));
    lines.push(panelLine("Buffer", "(unavailable)"));
    lines.push(panelLine("Extraction", "(unavailable)"));
    lines.push(panelLine("Dedup", "(unavailable)"));
    lines.push(panelLine("Maintenance", "(unavailable)"));
    lines.push(panelLine("QMD", "(unavailable)"));
    return lines;
  }
  if (snap) {
    lines.push(
      panelLine(
        "Buffer",
        `turns=${snap.bufferState.turnsCount} bytes=${snap.bufferState.byteCount}`
      )
    );
    const verdicts = snap.extractionQueue.recentVerdicts;
    const accepts = verdicts.filter((v) => v.kind === "accept").length;
    const rejects = verdicts.filter((v) => v.kind === "reject").length;
    lines.push(
      panelLine(
        "Extraction",
        `queue=${snap.extractionQueue.depth}  recent verdicts: accept(${accepts})/reject(${rejects})`
      )
    );
    const dedupSummary = formatDedupSummary(snap.dedupRecent, input.now);
    lines.push(panelLine("Dedup", dedupSummary));
    const maintSummary = formatMaintenanceTail(snap.maintenanceLedgerTail);
    lines.push(panelLine("Maintenance", maintSummary));
    lines.push(panelLine("QMD", formatQmdSummary(snap)));
  }
  if (snap && snap.errors.length > 0) {
    const head = snap.errors[0];
    lines.push(panelLine("Errors", head ?? ""));
  }
  return lines;
}
function formatDedupSummary(decisions, now) {
  if (decisions.length === 0) return "no recent decisions";
  const last = decisions[decisions.length - 1];
  if (!last) return "no recent decisions";
  const ageMs = ageMsFromIso(last.ts, now);
  const ageStr = ageMs === null ? "T-?" : `T-${Math.round(ageMs / 1e3)}s`;
  const fp = last.fingerprint ? `hash=${last.fingerprint}` : "hash=?";
  return `recent: ${fp} decision=${last.decision} (${ageStr})`;
}
function formatMaintenanceTail(events) {
  if (events.length === 0) return "no events";
  const last = events[events.length - 1];
  if (!last) return "no events";
  return `n=${events.length}  last: ${last.category} ${truncate(last.summary, 40)}`;
}
function formatQmdSummary(snap) {
  const probe = snap.qmdProbe.available ? "ok" : "down";
  const uptimeH = snap.daemon.uptimeMs / 36e5;
  const uptimeStr = uptimeH < 1 ? `${Math.round(snap.daemon.uptimeMs / 1e3)}s` : `${uptimeH.toFixed(1)}h`;
  const mode = snap.qmdProbe.daemonMode ? "daemon" : "cli";
  return `probe=${probe}  mode=${mode}  uptime=${uptimeStr}`;
}
function panelLine(label, value) {
  const LABEL_WIDTH = 13;
  const labelCol = padRight(label, LABEL_WIDTH);
  const remaining = FRAME_INNER_WIDTH - LABEL_WIDTH - 2;
  const valueCol = padRight(truncate(value, remaining), remaining);
  return `\u2551 ${labelCol}${valueCol} \u2551`;
}
function padRight(s, width) {
  if (s.length >= width) return s;
  return s + " ".repeat(width - s.length);
}
function truncate(s, max) {
  if (max <= 0) return "";
  if (s.length <= max) return s;
  if (max <= 1) return s.slice(0, max);
  return s.slice(0, max - 1) + "\u2026";
}
function ageMsFromIso(iso, now) {
  const ms = Date.parse(iso);
  if (!Number.isFinite(ms)) return null;
  const delta = now() - ms;
  return delta < 0 ? 0 : delta;
}
async function runTickSafely(tick) {
  try {
    await tick();
  } catch {
  }
}
function safeWrite(output, chunk) {
  try {
    output.write(chunk);
  } catch {
  }
}
function describeError(err) {
  if (err instanceof Error) return err.message;
  try {
    return String(err);
  } catch {
    return "unknown error";
  }
}
function stripAnsi(s) {
  return s.replace(/\x1b\[[0-9;?]*[A-Za-z]/g, "");
}

export {
  runConsoleTui,
  renderFrame,
  stripAnsi
};
//# sourceMappingURL=chunk-32KD5IHZ.js.map