import { logger } from '../logger.js';

/**
 * Per-user LLM usage event, emitted by OpenAICompatClient once per
 * successful chat completion (gateway-routed and direct alike). The
 * recorder is a process-global sink so every call site — agent-loop,
 * title generation, piece classification, reflection — is covered
 * without threading a Repository handle through every client
 * construction. This is the single chokepoint the design relies on to
 * avoid the propagation leaks this codebase has repeatedly hit.
 *
 * Exception: the ReadImage vision tool (engine/tools/image.ts) issues a
 * raw, non-streaming fetch to its own vision endpoint rather than going
 * through OpenAICompatClient, so it calls recordLlmUsage() directly.
 *
 * Spec: docs/superpowers/specs/2026-06-11-llm-usage-aggregation-design.md
 */
export interface LlmUsageEvent {
  /** Owner id, or 'local' (no-auth) / 'system' (ownerless) sentinel. NOT NULL. */
  userId: string;
  source: 'gateway' | 'direct';
  /** Real model name (chunk.model) with routing-key / 'unknown' fallback. */
  model: string;
  /** Backend server name (gateway backendId / direct host) or 'unknown'. */
  route: string;
  tokensIn: number;
  tokensOut: number;
}

export type LlmUsageRecorder = (event: LlmUsageEvent) => void;

let recorder: LlmUsageRecorder | null = null;

/**
 * Install (or clear with null) the process-global usage recorder. Called
 * once during bootstrap with a thin wrapper over Repository.incrementLlmUsageHourly.
 */
export function setLlmUsageRecorder(fn: LlmUsageRecorder | null): void {
  recorder = fn;
}

/**
 * Record one successful completion. No-op when no recorder is installed
 * (e.g. unit tests, gateway server process). Writes are best-effort: a DB
 * hiccup must never kill the agent's stream, so failures are swallowed
 * with a warn.
 */
export function recordLlmUsage(event: LlmUsageEvent): void {
  if (!recorder) return;
  try {
    recorder(event);
  } catch (err) {
    const msg = err instanceof Error ? err.message : String(err);
    logger.warn(`[usage-recorder] record failed (non-fatal): ${msg}`);
  }
}
