/**
 * Text rendering for the `decide` tool result and the `/decide` command. The tool text is what the
 * model reads back, so it stays compact and machine-parseable; the full structured result travels in
 * the tool result `details`.
 */

import { BACKEND_IDS, maskKey, type BackendConfig, type DeciderConfig } from "./config.ts";
import type { JevAnswer, JevQuestion } from "./questions.ts";
import type { CompositeResult, GateRow } from "./shapes.ts";
import type { TokenCounts } from "./usage.ts";

export interface AnswerReport {
  ids: string[];
  questions: Record<string, JevQuestion>;
  answers: Record<string, JevAnswer>;
  /** Fatal per-question problems: no usable answer. */
  issues: Record<string, string>;
  /** Non-fatal per-question observations. */
  notes: Record<string, string>;
}

/** One answered batch: every question routed to the same backend + model. */
export interface GroupMeta {
  backendId: string;
  label: string;
  kind: "decisions" | "chat";
  requestedModel: string;
  servedModel?: string;
  provider?: string;
  endpoint: string;
  latencyMs: number;
  tokens: TokenCounts;
  costUsd?: number;
  ids: string[];
  notes?: string[];
}

export function formatAnswers(report: AnswerReport, metas: GroupMeta[]): string {
  const lines: string[] = [];
  for (const meta of metas) {
    const header: string[] = [
      meta.backendId,
      meta.label,
      meta.servedModel === undefined || meta.servedModel === meta.requestedModel
        ? `model ${meta.requestedModel}`
        : `model ${meta.requestedModel} → ${meta.servedModel}`,
      ...(meta.provider === undefined ? [] : [`provider ${meta.provider}`]),
      `${meta.latencyMs} ms`,
      `tokens ${meta.tokens.input ?? 0} in / ${meta.tokens.output ?? 0} out`,
    ];
    if (meta.costUsd !== undefined) header.push(`cost $${meta.costUsd.toFixed(6)}`);
    lines.push(header.join(" · "));
    if (meta.kind === "chat") {
      lines.push("note: proxy decision by a general chat model, not a calibrated System One model");
    }
    for (const note of meta.notes ?? []) lines.push(`note: ${note}`);
    for (const id of meta.ids) lines.push(...renderAnswer(id, report));
  }
  for (const [id, note] of Object.entries(report.notes)) lines.push(`note ${id}: ${note}`);
  return lines.join("\n");
}

export function formatConfigStatus(cfg: DeciderConfig, selected?: BackendConfig): string {
  const rows: Array<[string, string]> = [
    ["config", cfg.configPath],
    ["backend", `${cfg.backend} (${cfg.backendSource})${selected === undefined ? "" : ` → ${selected.id}`}`],
  ];
  for (const id of BACKEND_IDS) {
    const backend = cfg.backends[id];
    const marker = selected?.id === id ? "*" : " ";
    rows.push([
      `${marker} ${id}`,
      `${backend.label} · ${backend.kind}`,
    ]);
    rows.push([
      "   key",
      backend.apiKey === undefined ? `${backend.apiKeySource} (nothing resolved)` : `${backend.apiKeySource} ${maskKey(backend.apiKey)}`,
    ]);
    rows.push(["   url", `${backend.baseUrl}${backend.path} (${backend.baseUrlSource})`]);
    rows.push([
      "   model",
      backend.model === undefined ? "not set" : `${backend.model} (${backend.modelSource})`,
    ]);
    rows.push([
      "   limits",
      `timeout ${backend.timeoutMs} ms · retries ${backend.maxRetries} · $${backend.costPerMTokInput}/Mtok in · $${backend.costPerMTokOutput}/Mtok out`,
    ]);
  }
  rows.push(["models path", cfg.modelsPath]);
  const width = Math.max(...rows.map(([label]) => label.length));
  const lines = rows.map(([label, value]) => `${label.padEnd(width)}  ${value}`);
  for (const note of cfg.notes) lines.push(`${"note".padEnd(width)}  ${note}`);
  return lines.join("\n");
}

export function formatModelList(title: string, names: string[], limit = 40): string {
  if (names.length === 0) return `${title}: none reported`;
  const shown = names.slice(0, limit);
  const suffix = names.length > shown.length ? `\n… ${names.length - shown.length} more` : "";
  return `${title} (${names.length}):\n${shown.join("\n")}${suffix}`;
}

/** Confidence-gated routing verdicts. */
export function formatGate(rows: GateRow[], threshold: number): string {
  const lines = [`gate: ${rows.filter((row) => row.pass).length}/${rows.length} pass at ${threshold}`];
  for (const row of rows) {
    const value = row.value === undefined ? "n/a" : String(row.value);
    lines.push(`  ${row.pass ? "PASS" : "HOLD"} ${row.id} ${row.metric}=${value}`);
  }
  return lines.join("\n");
}

/** Composite scoring: normalized score × weight, then the weighted mean. */
export function formatComposite(result: CompositeResult): string {
  if (result.rows.length === 0) return "composite: no score answers to combine";
  const lines = result.rows.map(
    (row) =>
      `  ${row.id} normalized ${round(row.normalized, 4)} × weight ${row.weight} = ${round(row.contribution, 4)}`,
  );
  lines.push(`composite score ${round(result.total, 4)} (total weight ${result.totalWeight})`);
  return lines.join("\n");
}

function round(value: number, digits: number): number {
  const factor = 10 ** digits;
  return Math.round(value * factor) / factor;
}

function renderAnswer(id: string, report: AnswerReport): string[] {
  const answer = report.answers[id];
  if (answer === undefined) {
    return [`${id} [${report.questions[id].type}] ERROR ${report.issues[id] ?? "no answer"}`];
  }
  const confidence = "confidence" in answer && answer.confidence !== undefined ? ` · confidence ${answer.confidence}` : "";
  if (answer.type === "noul") return [`${id} [noul] ${answer.noul}${confidence}`];
  if (answer.type === "choice") {
    return [`${id} [choice] ${answer.choice}${confidence}`, `  probabilities: ${renderDistribution(answer.probabilities)}`];
  }
  return [
    `${id} [score] ${answer.score}${confidence}`,
    `  levels: ${Object.entries(answer.legend)
      .map(([index, level]) => `${index} ${level}`)
      .join(" · ")}`,
    `  probabilities: ${renderDistribution(answer.probabilities)}`,
  ];
}

function renderDistribution(probabilities: Record<string, number>): string {
  return Object.entries(probabilities)
    .map(([key, value]) => `${key} ${value}`)
    .join(" · ");
}
