using System.Text.Json;

namespace ClawdDotNet.Core.Config;

public static class ConfigLoader
{
    private static readonly JsonSerializerOptions JsonOptions = new()
    {
        ReadCommentHandling = JsonCommentHandling.Skip,
        AllowTrailingCommas = true,
        PropertyNameCaseInsensitive = true
    };

    public static async Task<InstanceConfig> LoadAsync(string filePath, CancellationToken ct = default)
    {
        if (!File.Exists(filePath))
            throw new FileNotFoundException($"Config file not found: {filePath}");

        await using var stream = File.OpenRead(filePath);
        var config = await JsonSerializer.DeserializeAsync<InstanceConfig>(stream, JsonOptions, ct)
            ?? throw new InvalidOperationException($"Config file is empty or invalid: {filePath}");

        Migrate(config);
        // Verschlüsselte Zugangsdaten für die Laufzeit lesbar machen. Klartext aus
        // älteren Konfigurationen bleibt unverändert und wird beim Speichern übernommen.
        Security.ConfigSecrets.Unprotect(config);
        Validate(config, filePath);
        return config;
    }

    /// <summary>
    /// Hebt alte Konfigurationen auf das aktuelle Schema.
    ///
    /// "maxTokens" wurde früher als Kontextgrenze verstanden, zählte aber kumulativ
    /// über alle Schritte — dadurch brachen normale Läufe vorzeitig ab. Der Wert wird
    /// nicht übernommen, sondern durch den großzügigen Default für das Kostenbudget
    /// ersetzt; die Kontextsteuerung übernimmt maxContextTokens.
    /// </summary>
    public static void Migrate(InstanceConfig config)
    {
        foreach (var agent in config.Agents)
            Migrate(agent);
    }

    /// <summary>
    /// Migration für einen einzeln geladenen Agenten (AgentSettings.json).
    /// </summary>
    public static void Migrate(AgentConfig agent)
    {
        var guard = agent.LoopGuard;
        if (guard.LegacyMaxTokens is not { } legacy)
            return;

        // Nur übernehmen, wenn bewusst großzügiger konfiguriert als der Default.
        if (legacy > guard.MaxCumulativeTokens)
            guard.MaxCumulativeTokens = legacy;

        guard.LegacyMaxTokens = null;
    }

    private static void Validate(InstanceConfig config, string filePath)
    {
        if (string.IsNullOrWhiteSpace(config.OpenRouterApiKey))
            throw new InvalidOperationException($"'openRouterApiKey' is required in {filePath}");

        var agentIds = new HashSet<string>();
        foreach (var agent in config.Agents)
        {
            if (string.IsNullOrWhiteSpace(agent.AgentId))
                throw new InvalidOperationException($"Every agent must have an 'agentId' in {filePath}");

            if (!agentIds.Add(agent.AgentId))
                throw new InvalidOperationException($"Duplicate agentId '{agent.AgentId}' in {filePath}");
        }
    }
}
