using System.Text.Json;
using System.Text.Json.Nodes;

namespace SIGCM2.Infrastructure.Audit;

/// UDT-010 (#REQ-AUD-5): serializes a metadata object to JSON and strips keys in the blacklist
/// at every nesting level. Case-insensitive match. Null input → null output (never throws).
/// Produces valid JSON consumable by AuditEvent.Metadata (ISJSON=1 constraint).
public static class JsonSanitizer
{
    /// Serializes <paramref name="obj"/> to JSON and removes any property whose key matches
    /// (case-insensitively) an entry in <paramref name="blacklist"/>. Recursive into nested
    /// objects and arrays. Returns null if the input is null.
    public static string? Sanitize(object? obj, IReadOnlyCollection<string> blacklist)
    {
        if (obj is null) return null;

        var node = JsonSerializer.SerializeToNode(obj);
        if (node is null) return null;

        if (blacklist.Count > 0)
        {
            var blacklistLower = blacklist
                .Select(k => k.ToLowerInvariant())
                .ToHashSet();
            Strip(node, blacklistLower);
        }

        return node.ToJsonString();
    }

    private static void Strip(JsonNode node, HashSet<string> blacklistLower)
    {
        switch (node)
        {
            case JsonObject obj:
                var keysToRemove = obj
                    .Where(kvp => blacklistLower.Contains(kvp.Key.ToLowerInvariant()))
                    .Select(kvp => kvp.Key)
                    .ToList();

                foreach (var key in keysToRemove)
                    obj.Remove(key);

                foreach (var kvp in obj.ToList())
                {
                    if (kvp.Value is not null)
                        Strip(kvp.Value, blacklistLower);
                }
                break;

            case JsonArray arr:
                foreach (var item in arr)
                {
                    if (item is not null)
                        Strip(item, blacklistLower);
                }
                break;

                // JsonValue: scalar, nothing to strip
        }
    }
}
