using System.Collections.Concurrent;
using ClawdDotNet.Core.Api.Models;

namespace ClawdDotNet.Core.Api;

/// <summary>Kostenschätzung eines Laufs — oder die Auskunft, dass keine möglich ist.</summary>
public sealed record CostEstimate(decimal Usd, bool IsKnown, string Model)
{
    public static CostEstimate Unknown(string model) => new(0m, false, model);
}

/// <summary>
/// Hält die Modellpreise von OpenRouter.
///
/// Hintergrund (B4): Die Preise standen fest im Code — eine Tabelle mit einer Handvoll
/// Modelle, die bereits veraltet war. Das Standardmodell der Agenten fehlte darin, und
/// <c>CalculateCost</c> gab für unbekannte Modelle stillschweigend 0 zurück. Die
/// Kostenanzeige war damit nicht bloß ungenau, sondern blind: Sie zeigte 0 € an,
/// während echte Kosten anfielen.
///
/// Deshalb kommen die Preise jetzt vom Anbieter, und ein unbekanntes Modell wird als
/// solches gemeldet, statt eine Null vorzutäuschen.
/// </summary>
public sealed class ModelPricingCatalog
{
    private readonly ConcurrentDictionary<string, ModelPricing> _prices =
        new(StringComparer.OrdinalIgnoreCase);

    public DateTime? LastUpdated { get; private set; }

    public int Count => _prices.Count;

    /// <summary>Übernimmt die Preise aus einer Modellliste. Modelle ohne Angaben werden übersprungen.</summary>
    public void Load(IEnumerable<ModelInfo> models)
    {
        foreach (var model in models)
        {
            if (model.Pricing is { } pricing && !string.IsNullOrWhiteSpace(model.Id))
                _prices[model.Id] = pricing;
        }

        LastUpdated = DateTime.Now;
    }

    public ModelPricing? Get(string model)
        => _prices.TryGetValue(model, out var pricing) ? pricing : null;

    public bool IsKnown(string model) => _prices.ContainsKey(model);

    /// <summary>
    /// Berechnet die Kosten eines Laufs. Bei unbekanntem Modell wird das im Ergebnis
    /// ausgewiesen — der Aufrufer soll das sichtbar machen, nicht als 0 verbuchen.
    /// </summary>
    public CostEstimate Estimate(string model, int promptTokens, int completionTokens)
    {
        if (Get(model) is not { } pricing)
            return CostEstimate.Unknown(model);

        var cost = promptTokens / 1_000_000m * pricing.InputPer1M
                 + completionTokens / 1_000_000m * pricing.OutputPer1M;

        return new CostEstimate(cost, true, model);
    }
}
