using System.Text.Json;

namespace Journal.Core.Services.Logging;

public static class LogRedactor
{
    private static readonly HashSet<string> SensitiveKeys = new(StringComparer.OrdinalIgnoreCase)
    {
        "password",
        "passphrase",
        "secret",
        "token",
        "apiKey",
        "api_key",
        "cloudAiApiKey",
        "content",
        "rawContent",
        "prompt",
        "audioBase64",
        "audio_base64",
        "text"
    };

    public static object? RedactPayload(JsonElement? payload)
    {
        if (payload is null)
            return null;
        return RedactElement(payload.Value, parentKey: null);
    }

    private static object? RedactElement(JsonElement element, string? parentKey)
    {
        if (parentKey is not null && SensitiveKeys.Contains(parentKey))
            return "[REDACTED]";

        return element.ValueKind switch
        {
            JsonValueKind.Object => RedactObject(element),
            JsonValueKind.Array => RedactArray(element),
            JsonValueKind.String => RedactString(element.GetString() ?? "", parentKey),
            JsonValueKind.Number => element.GetRawText(),
            JsonValueKind.True => true,
            JsonValueKind.False => false,
            JsonValueKind.Null => null,
            _ => element.GetRawText()
        };
    }

    private static Dictionary<string, object?> RedactObject(JsonElement element)
    {
        var output = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase);
        foreach (var property in element.EnumerateObject())
            output[property.Name] = RedactElement(property.Value, property.Name);
        return output;
    }

    private static List<object?> RedactArray(JsonElement element)
    {
        var output = new List<object?>();
        foreach (var item in element.EnumerateArray())
            output.Add(RedactElement(item, parentKey: null));
        return output;
    }

    private static object RedactString(string value, string? key)
    {
        if (key is not null && SensitiveKeys.Contains(key))
            return "[REDACTED]";
        if (value.Length <= 128)
            return value;
        return $"{value[..128]}...(truncated)";
    }
}
