/**
 * Cost tracking — model pricing, pre-run estimation, and post-run actuals.
 *
 * Uses a pricing table (YAML config or TS `definePricingTable()`) to compute
 * USD cost from token usage. Supports budget controls with warn/stop thresholds.
 *
 * @see docs/design-docs/architecture-overhaul/observability-telemetry.md
 */
// ---------------------------------------------------------------------------
// Pricing table
// ---------------------------------------------------------------------------
/** Default pricing table (updated periodically) */
const DEFAULT_PRICING = {
    "anthropic:messages:claude-opus-4-6": {
        input: 15.0,
        output: 75.0,
        cachedInput: 1.5,
    },
    "anthropic:messages:claude-sonnet-4-6": {
        input: 3.0,
        output: 15.0,
        cachedInput: 0.3,
    },
    "openai:chat:gpt-4.1": {
        input: 2.0,
        output: 8.0,
        cachedInput: 0.5,
    },
    "openai:chat:gpt-4.1-mini": {
        input: 0.4,
        output: 1.6,
        cachedInput: 0.1,
    },
    "openai:chat:gpt-4o": {
        input: 2.5,
        output: 10.0,
        cachedInput: 1.25,
    },
    "openai:chat:gpt-5": {
        input: 5.0,
        output: 15.0,
        cachedInput: 1.0,
    },
};
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
/**
 * Compute actual cost from token usage and model pricing.
 *
 * @param usage - Token counts from provider response
 * @param pricing - Per-model pricing (USD per 1M tokens)
 * @returns Cost in USD
 */
export function computeCost(usage, pricing) {
    const cached = usage.toolTokens ?? 0;
    const uncachedPrompt = usage.promptTokens - cached;
    const inputCost = (uncachedPrompt * pricing.input) / 1_000_000;
    const cachedCost = pricing.cachedInput !== undefined
        ? (cached * pricing.cachedInput) / 1_000_000
        : (cached * pricing.input) / 1_000_000;
    const outputCost = (usage.completionTokens * pricing.output) / 1_000_000;
    return inputCost + cachedCost + outputCost;
}
/**
 * Look up pricing for a model ID.
 *
 * Tries exact match first, then falls back to prefix matching
 * (e.g., "openai:chat:gpt-4o-2024-11-20" matches "openai:chat:gpt-4o").
 */
export function lookupPricing(modelId, customPricing) {
    // 1. Exact match in custom pricing
    if (customPricing?.[modelId])
        return customPricing[modelId];
    // 2. Exact match in defaults
    if (DEFAULT_PRICING[modelId])
        return DEFAULT_PRICING[modelId];
    // 3. Prefix match in custom pricing
    if (customPricing) {
        for (const [key, pricing] of Object.entries(customPricing)) {
            if (modelId.startsWith(key))
                return pricing;
        }
    }
    // 4. Prefix match in defaults
    for (const [key, pricing] of Object.entries(DEFAULT_PRICING)) {
        if (modelId.startsWith(key))
            return pricing;
    }
    return undefined;
}
/**
 * Estimate cost for a pipeline run before execution.
 *
 * Uses task count, estimated tokens per task complexity, and model pricing.
 */
export function estimateRunCost(taskCount, modelIds, budget, customPricing) {
    // Rough token estimates per task (empirical averages)
    const AVG_PROMPT_TOKENS = 2000;
    const AVG_COMPLETION_TOKENS = 1500;
    const perModel = modelIds.map((modelId) => {
        const pricing = lookupPricing(modelId, customPricing);
        if (!pricing) {
            return { modelId, estimatedUSD: 0 };
        }
        const estimatedUSD = computeCost({
            promptTokens: AVG_PROMPT_TOKENS * taskCount,
            completionTokens: AVG_COMPLETION_TOKENS * taskCount,
            totalTokens: (AVG_PROMPT_TOKENS + AVG_COMPLETION_TOKENS) * taskCount,
        }, pricing);
        return { modelId, estimatedUSD };
    });
    const totalUSD = perModel.reduce((sum, m) => sum + m.estimatedUSD, 0);
    return {
        totalUSD,
        perModel,
        exceedsWarning: budget?.perRun ? totalUSD >= budget.perRun.warn : false,
        exceedsStop: budget?.perRun ? totalUSD >= budget.perRun.stop : false,
    };
}
/**
 * Check if current spend exceeds budget thresholds.
 */
export function checkBudget(currentUSD, budget, level) {
    const limits = budget[level];
    if (!limits) {
        return { proceed: true, currentUSD };
    }
    if (currentUSD >= limits.stop) {
        return {
            proceed: false,
            warning: `Budget exceeded: $${currentUSD.toFixed(4)} >= $${limits.stop} (${level} stop limit)`,
            currentUSD,
            limitUSD: limits.stop,
        };
    }
    if (currentUSD >= limits.warn) {
        return {
            proceed: true,
            warning: `Budget warning: $${currentUSD.toFixed(4)} >= $${limits.warn} (${level} warn threshold)`,
            currentUSD,
            limitUSD: limits.warn,
        };
    }
    return { proceed: true, currentUSD };
}
