import type { CanonicalUsage } from "../protocol/canonical.js";

export function normalizeAnthropicUsage(raw: unknown): CanonicalUsage | undefined {
  if (!isRecord(raw)) {
    return undefined;
  }

  const inputTokens = readNumber(raw.input_tokens);
  const outputTokens = readNumber(raw.output_tokens);
  const cacheReadTokens = readNumber(raw.cache_read_input_tokens);
  const cacheWriteTokens = readNumber(raw.cache_creation_input_tokens);

  return compactUsage({
    inputTokens,
    outputTokens,
    cacheReadTokens,
    cacheWriteTokens,
    totalTokens: sumDefined(inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens),
  });
}

export function normalizeOpenAIUsage(raw: unknown): CanonicalUsage | undefined {
  if (!isRecord(raw)) {
    return undefined;
  }

  const promptTokens = readNumber(raw.prompt_tokens) ?? readNumber(raw.input_tokens);
  const outputTokens = readNumber(raw.completion_tokens) ?? readNumber(raw.output_tokens);
  const nativeCost =
    readNumber(raw.cost) ??
    readNumber(raw.total_cost) ??
    readNumber(raw.estimated_cost);

  const details = isRecord(raw.prompt_tokens_details) ? raw.prompt_tokens_details : undefined;
  const cacheReadTokens = readNumber(details?.cached_tokens) ?? readNumber(raw.cache_read_input_tokens);
  const cacheWriteTokens = readNumber(details?.cache_write_tokens) ?? readNumber(raw.cache_creation_input_tokens);

  const inputTokens = promptTokens != null
    ? promptTokens - (cacheReadTokens ?? 0) - (cacheWriteTokens ?? 0)
    : undefined;

  const totalTokens = readNumber(raw.total_tokens) ?? sumDefined(promptTokens, outputTokens);

  return compactUsage({
    inputTokens,
    outputTokens,
    cacheReadTokens,
    cacheWriteTokens,
    totalTokens,
    nativeCost,
  });
}

function compactUsage(usage: CanonicalUsage): CanonicalUsage | undefined {
  return Object.values(usage).some((value) => value !== undefined) ? usage : undefined;
}

function readNumber(value: unknown): number | undefined {
  return typeof value === "number" && Number.isFinite(value) ? value : undefined;
}

function sumDefined(...values: Array<number | undefined>): number | undefined {
  const defined = values.filter((value): value is number => value !== undefined);
  return defined.length > 0 ? defined.reduce((total, value) => total + value, 0) : undefined;
}

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}