/**
 * Phase 3b — shared Prometheus registry plumbing.
 *
 * Both gateway mode and worker mode get their own Registry instance
 * (different prefixes, different metric sets) but they share this
 * factory so the wiring stays consistent: every registry gets the
 * default `process_*` / `nodejs_*` metrics from prom-client so SREs see
 * cpu / event-loop / heap metrics on every AAO process without any
 * extra config.
 *
 * Design:
 * - `createGatewayRegistry()` / `createWorkerRegistry()` return distinct
 *   Registry instances. Tests pass these directly to avoid the
 *   process-singleton trap (one Vitest worker running many tests must
 *   not share metric state across cases).
 * - `getDefaultGatewayRegistry()` / `getDefaultWorkerRegistry()` lazily
 *   create a singleton per process; called from bootstrap / bridge.
 * - `resetAllRegistries()` is a test escape hatch. Production code never
 *   calls it.
 *
 * The label cardinality budget is enforced by call-site discipline (see
 * the per-metric files). Prom-client doesn't cap labels itself, so a
 * runaway labelset would silently grow memory until the next scrape —
 * we keep label sets narrow and well-typed in the metric factory funcs.
 */
import { Registry, collectDefaultMetrics } from 'prom-client';

let defaultGatewayRegistry: Registry | null = null;
let defaultWorkerRegistry: Registry | null = null;

/**
 * Stand up a fresh Registry with `process_*` / `nodejs_*` collected on
 * scrape. The `prefix` param is forwarded to collectDefaultMetrics so
 * default metric names match the gateway / worker namespace (e.g.
 * `aao_gateway_process_cpu_user_seconds_total`).
 */
function createRegistry(prefix: string): Registry {
  const reg = new Registry();
  // Default labels let Grafana queries select by deployment without
  // having to label every counter individually. Empty for now — operators
  // typically add `instance` / `job` via the Prometheus scrape config.
  reg.setDefaultLabels({});
  collectDefaultMetrics({
    register: reg,
    // Slightly verbose names but keeps the namespace consistent.
    prefix: `${prefix}_`,
  });
  return reg;
}

/**
 * Build the gateway-side Registry. Use this from
 * gateway/bootstrap.ts. Tests create their own to stay isolated.
 */
export function createGatewayRegistry(prefix: string = 'aao_gateway'): Registry {
  return createRegistry(prefix);
}

/**
 * Build the worker-side Registry. Use this from bridge/server.ts.
 */
export function createWorkerRegistry(prefix: string = 'aao_worker'): Registry {
  return createRegistry(prefix);
}

/**
 * Lazily create + return the process-singleton gateway registry. Bootstrap
 * calls this once at startup; admin handlers reach in (rare) via
 * getDefaultGatewayRegistry() to read current metric values.
 */
export function getDefaultGatewayRegistry(prefix: string = 'aao_gateway'): Registry {
  if (!defaultGatewayRegistry) {
    defaultGatewayRegistry = createGatewayRegistry(prefix);
  }
  return defaultGatewayRegistry;
}

/**
 * Lazily create + return the process-singleton worker registry.
 */
export function getDefaultWorkerRegistry(prefix: string = 'aao_worker'): Registry {
  if (!defaultWorkerRegistry) {
    defaultWorkerRegistry = createWorkerRegistry(prefix);
  }
  return defaultWorkerRegistry;
}

/**
 * Test-only escape hatch: drop both singletons so the next call rebuilds
 * a fresh registry. Production code MUST NOT call this — clearing a
 * live registry mid-scrape produces zeroed counters that look like
 * regressions in Grafana.
 */
export function resetAllRegistries(): void {
  defaultGatewayRegistry = null;
  defaultWorkerRegistry = null;
}
