/**
 * Token-usage → USD cost math, built on the offline pricing table.
 *
 * `costOfUsage` is the single multiply-by-price primitive every other helper
 * (and issue #346's budget pre-flight estimator) routes through. It returns 0
 * for unknown/unpriced models rather than throwing — cost is additive, and a
 * single unknown model in a session shouldn't blow up the whole rollup.
 */
import { getModelPricing } from './table.js';
/**
 * USD cost of one usage record. Returns 0 when the model is missing or unpriced
 * (cost is additive — an unknown model contributes nothing, not NaN). Cache
 * read/write tokens are priced at their dedicated rates when the table exposes
 * them, otherwise they fall back to the input rate (the standard LiteLLM
 * convention for models that don't publish a separate cache price).
 */
export function costOfUsage(u) {
    if (!u.model)
        return 0;
    const pricing = getModelPricing(u.model);
    if (!pricing)
        return 0;
    const input = u.inputTokens ?? 0;
    const output = u.outputTokens ?? 0;
    const cacheRead = u.cacheReadTokens ?? 0;
    const cacheWrite = u.cacheCreationTokens ?? 0;
    const cacheReadRate = pricing.cacheReadPerToken ?? pricing.inputPerToken;
    const cacheWriteRate = pricing.cacheWritePerToken ?? pricing.inputPerToken;
    return (input * pricing.inputPerToken +
        output * pricing.outputPerToken +
        cacheRead * cacheReadRate +
        cacheWrite * cacheWriteRate);
}
/**
 * USD cost of one usage record priced as if caching were OFF: cache-read and
 * cache-write tokens are billed at the model's full INPUT rate rather than their
 * discounted cache rates. This is the "what would this have cost with no prompt
 * caching?" scenario that `agents insights output --pricing no-cache` models. Output and
 * uncached input are unchanged — only the cache tokens are repriced. Returns 0
 * for a missing/unpriced model, exactly like {@link costOfUsage}.
 */
export function costOfUsageNoCache(u) {
    if (!u.model)
        return 0;
    const pricing = getModelPricing(u.model);
    if (!pricing)
        return 0;
    const input = u.inputTokens ?? 0;
    const output = u.outputTokens ?? 0;
    const cacheRead = u.cacheReadTokens ?? 0;
    const cacheWrite = u.cacheCreationTokens ?? 0;
    // Cache tokens repriced at the input rate — no caching discount applied.
    return (input * pricing.inputPerToken +
        output * pricing.outputPerToken +
        cacheRead * pricing.inputPerToken +
        cacheWrite * pricing.inputPerToken);
}
/** Sum the USD cost of every usage record in a session. */
export function costOfSession(usages) {
    let total = 0;
    for (const u of usages)
        total += costOfUsage(u);
    return total;
}
/**
 * Format a USD amount for human display. Cents-precise, with a "<$0.01" floor
 * so tiny-but-nonzero sessions don't render as "$0.00" and read as free.
 */
export function formatUsd(usd) {
    if (!Number.isFinite(usd) || usd <= 0)
        return '$0.00';
    if (usd < 0.01)
        return '<$0.01';
    return `$${usd.toFixed(2)}`;
}
/**
 * Pre-flight cost estimate for a model + token bundle (issue #346's budget
 * gate). Returns the resolved canonical model id (`modelMatched`) so callers
 * can warn when an estimate fell back to $0 because the model is unpriced.
 */
export function estimateCost(model, tokens) {
    const pricing = getModelPricing(model);
    const usd = costOfUsage({ model, ...tokens });
    // modelMatched is the input model when priced, null when unknown — callers
    // only need the priced/unpriced signal, not the internal canonical key.
    return { usd, modelMatched: pricing ? model : null };
}
/** Actual (post-hoc) cost of a model + observed usage. Thin alias over costOfUsage. */
export function actualCost(model, usage) {
    return { usd: costOfUsage({ model, ...usage }) };
}
/** True when the model resolves to a priced entry in the table. */
export function isModelPriced(model) {
    return getModelPricing(model) !== null;
}
