/**
 * Token Prometheus metrics — in-memory counters with Prometheus text exposition format.
 *
 * No external dependencies. Uses simple Maps for counters and arrays for latency
 * histograms. Exported via GET /api/token/metrics in Prometheus text format.
 *
 * All metric names are prefixed with `token_` to namespace within the LLMHosts
 * monitoring stack.
 */

/* ── Counters ─────────────────────────────────────────────────────── */

/** Messages by model+intent. Key: `${model}\x00${intent}` */
const messages = new Map<string, number>();

/** Cost (USD) by model. Key: model name */
const cost = new Map<string, number>();

/** Alerts by severity. Key: severity string */
const alerts = new Map<string, number>();

/** Latency samples by model. Key: model name, Value: array of latency values in ms */
const latency = new Map<string, number[]>();

/* ── Public API ───────────────────────────────────────────────────── */

/** Increment the total messages counter for a model+intent pair. */
export function incMessages(model: string, intent: string): void {
  const key = `${model}\x00${intent}`;
  messages.set(key, (messages.get(key) ?? 0) + 1);
}

/** Accumulate cost in USD for a model. */
export function incCost(model: string, amount: number): void {
  cost.set(model, (cost.get(model) ?? 0) + amount);
}

/** Record a latency observation in milliseconds for a model. */
export function recordLatency(model: string, latencyMs: number): void {
  const samples = latency.get(model);
  if (samples) {
    samples.push(latencyMs);
  } else {
    latency.set(model, [latencyMs]);
  }
}

/** Increment the alert counter for a severity level. */
export function incAlerts(severity: string): void {
  alerts.set(severity, (alerts.get(severity) ?? 0) + 1);
}

/* ── Percentile calculation ───────────────────────────────────────── */

/**
 * Calculate a percentile from a sorted array of numbers.
 * Uses the nearest-rank method.
 */
function percentile(sorted: number[], p: number): number {
  if (sorted.length === 0) return 0;
  const idx = Math.max(0, Math.ceil((p / 100) * sorted.length) - 1);
  return sorted[idx];
}

/* ── Prometheus text exposition formatter ─────────────────────────── */

/** Escape a Prometheus label value (backslash, double-quote, newline). */
function escapeLabelValue(v: string): string {
  return v.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n");
}

/**
 * Produce the full Prometheus text exposition format for all Token metrics.
 *
 * Includes HELP and TYPE lines per metric family, and all label combinations
 * with their current values. Returns an empty string when no data has been
 * recorded (Prometheus handles this gracefully).
 */
export function formatMetrics(): string {
  const lines: string[] = [];

  // --- token_messages_total (counter) ---
  if (messages.size > 0) {
    lines.push("# HELP token_messages_total Total Token messages processed");
    lines.push("# TYPE token_messages_total counter");
    for (const [key, count] of messages) {
      const [model, intent] = key.split("\x00");
      lines.push(
        `token_messages_total{model="${escapeLabelValue(model)}",intent="${escapeLabelValue(intent)}"} ${count}`,
      );
    }
    lines.push("");
  }

  // --- token_cost_usd_total (counter) ---
  if (cost.size > 0) {
    lines.push("# HELP token_cost_usd_total Total Token cost in USD");
    lines.push("# TYPE token_cost_usd_total counter");
    for (const [model, amount] of cost) {
      lines.push(`token_cost_usd_total{model="${escapeLabelValue(model)}"} ${amount}`);
    }
    lines.push("");
  }

  // --- token_latency_ms (summary) ---
  if (latency.size > 0) {
    lines.push("# HELP token_latency_ms Token response latency in milliseconds");
    lines.push("# TYPE token_latency_ms summary");
    for (const [model, samples] of latency) {
      const sorted = [...samples].sort((a, b) => a - b);
      const sum = samples.reduce((acc, v) => acc + v, 0);
      const label = escapeLabelValue(model);

      lines.push(`token_latency_ms{model="${label}",quantile="0.5"} ${percentile(sorted, 50)}`);
      lines.push(`token_latency_ms{model="${label}",quantile="0.95"} ${percentile(sorted, 95)}`);
      lines.push(`token_latency_ms{model="${label}",quantile="0.99"} ${percentile(sorted, 99)}`);
      lines.push(`token_latency_ms_count{model="${label}"} ${samples.length}`);
      lines.push(`token_latency_ms_sum{model="${label}"} ${sum}`);
    }
    lines.push("");
  }

  // --- token_alerts_total (counter) ---
  if (alerts.size > 0) {
    lines.push("# HELP token_alerts_total Total Token alerts fired");
    lines.push("# TYPE token_alerts_total counter");
    for (const [severity, count] of alerts) {
      lines.push(`token_alerts_total{severity="${escapeLabelValue(severity)}"} ${count}`);
    }
    lines.push("");
  }

  return lines.join("\n");
}

/* ── Test-only reset ──────────────────────────────────────────────── */

/** Clear all metric counters. Exported for testing only. */
export function _resetMetrics(): void {
  messages.clear();
  cost.clear();
  alerts.clear();
  latency.clear();
}
