using System.Text.Json;
using ClawdDotNet.Core.Config;

namespace ClawdDotNet.Core.Security;

/// <summary>
/// Weiß, welche Felder einer Konfiguration Zugangsdaten enthalten, und wendet den
/// <see cref="SecretProtector"/> darauf an.
///
/// Beide Richtungen sind gefahrlos mehrfach anwendbar: Schützen überspringt bereits
/// geschützte Werte, Entschlüsseln gibt Klartext unverändert zurück. Dadurch werden
/// bestehende Konfigurationen beim ersten Speichern automatisch übernommen.
/// </summary>
public static class ConfigSecrets
{
    /// <summary>
    /// Schlüsselnamen in Tool-Konfigurationen, deren Werte als Zugangsdaten gelten.
    /// Die Tool-Konfiguration ist ein freies Wörterbuch — deshalb wird hier nach
    /// Namen entschieden.
    /// </summary>
    private static readonly HashSet<string> SecretKeys = new(StringComparer.OrdinalIgnoreCase)
    {
        "password", "password2fa", "passwort",
        "apikey", "api_key", "xapikey", "openrouterapikey", "apihash", "apisecret",
        "token", "accesstoken", "bottoken", "authtoken",
        "secret", "clientsecret",
        "connectionstring"
    };

    public static bool IsSecretKey(string key) => SecretKeys.Contains(key);

    // ─── Instanz ───

    public static void Protect(InstanceConfig config) => Apply(config, SecretProtector.Protect);

    public static void Unprotect(InstanceConfig config) => Apply(config, SecretProtector.Unprotect);

    private static void Apply(InstanceConfig config, Func<string?, string?> transform)
    {
        config.OpenRouterApiKey = transform(config.OpenRouterApiKey) ?? "";

        if (config.TelegramClient is { } telegram)
        {
            telegram.ApiHash = transform(telegram.ApiHash) ?? "";
            telegram.Password2FA = transform(telegram.Password2FA);
        }

        if (config.Watchdog is { } watchdog)
            watchdog.AgentToken = transform(watchdog.AgentToken) ?? "";

        foreach (var agent in config.Agents)
            Apply(agent, transform);
    }

    // ─── Agent ───

    public static void Protect(AgentConfig agent) => Apply(agent, SecretProtector.Protect);

    public static void Unprotect(AgentConfig agent) => Apply(agent, SecretProtector.Unprotect);

    private static void Apply(AgentConfig agent, Func<string?, string?> transform)
    {
        foreach (var tool in agent.Tools.Values)
        {
            foreach (var key in tool.Keys.ToList())
            {
                if (!IsSecretKey(key))
                    continue;

                if (ReadString(tool[key]) is not { } current || current.Length == 0)
                    continue;

                tool[key] = transform(current);
            }
        }
    }

    /// <summary>
    /// Liest einen Zeichenkettenwert. Nach dem Deserialisieren stecken die Werte als
    /// JsonElement im Wörterbuch, nach einer Bearbeitung als einfache Zeichenkette.
    /// </summary>
    private static string? ReadString(object? value) => value switch
    {
        null => null,
        string s => s,
        JsonElement { ValueKind: JsonValueKind.String } je => je.GetString(),
        _ => null
    };
}
