using System.Collections.Generic;
using System.Linq;
using System.Text.Json;

namespace StellaOps.Feedser.Source.Vndr.Cisco.Internal;

internal static class CiscoCsafParser
{
    public static CiscoCsafData Parse(string content)
    {
        if (string.IsNullOrWhiteSpace(content))
        {
            return new CiscoCsafData(
                Products: new Dictionary<string, CiscoCsafProduct>(0, StringComparer.OrdinalIgnoreCase),
                ProductStatuses: new Dictionary<string, IReadOnlyCollection<string>>(0, StringComparer.OrdinalIgnoreCase));
        }

        using var document = JsonDocument.Parse(content);
        var root = document.RootElement;

        var products = ParseProducts(root);
        var statuses = ParseStatuses(root);

        return new CiscoCsafData(products, statuses);
    }

    private static IReadOnlyDictionary<string, CiscoCsafProduct> ParseProducts(JsonElement root)
    {
        var dictionary = new Dictionary<string, CiscoCsafProduct>(StringComparer.OrdinalIgnoreCase);
        if (!root.TryGetProperty("product_tree", out var productTree))
        {
            return dictionary;
        }

        if (productTree.TryGetProperty("full_product_names", out var fullProductNames)
            && fullProductNames.ValueKind == JsonValueKind.Array)
        {
            foreach (var entry in fullProductNames.EnumerateArray())
            {
                var productId = entry.TryGetProperty("product_id", out var idElement) && idElement.ValueKind == JsonValueKind.String
                    ? idElement.GetString()
                    : null;

                if (string.IsNullOrWhiteSpace(productId))
                {
                    continue;
                }

                var name = entry.TryGetProperty("name", out var nameElement) && nameElement.ValueKind == JsonValueKind.String
                    ? nameElement.GetString()
                    : null;

                if (string.IsNullOrWhiteSpace(name))
                {
                    name = productId;
                }

                dictionary[productId] = new CiscoCsafProduct(productId, name);
            }
        }

        return dictionary;
    }

    private static IReadOnlyDictionary<string, IReadOnlyCollection<string>> ParseStatuses(JsonElement root)
    {
        var map = new Dictionary<string, HashSet<string>>(StringComparer.OrdinalIgnoreCase);

        if (!root.TryGetProperty("vulnerabilities", out var vulnerabilities)
            || vulnerabilities.ValueKind != JsonValueKind.Array)
        {
            return map.ToDictionary(
                static kvp => kvp.Key,
                static kvp => (IReadOnlyCollection<string>)kvp.Value.ToArray(),
                StringComparer.OrdinalIgnoreCase);
        }

        foreach (var vulnerability in vulnerabilities.EnumerateArray())
        {
            if (!vulnerability.TryGetProperty("product_status", out var productStatus)
                || productStatus.ValueKind != JsonValueKind.Object)
            {
                continue;
            }

            foreach (var property in productStatus.EnumerateObject())
            {
                var statusLabel = property.Name;
                if (property.Value.ValueKind != JsonValueKind.Array)
                {
                    continue;
                }

                foreach (var productIdElement in property.Value.EnumerateArray())
                {
                    if (productIdElement.ValueKind != JsonValueKind.String)
                    {
                        continue;
                    }

                    var productId = productIdElement.GetString();
                    if (string.IsNullOrWhiteSpace(productId))
                    {
                        continue;
                    }

                    if (!map.TryGetValue(productId, out var set))
                    {
                        set = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
                        map[productId] = set;
                    }

                    set.Add(statusLabel);
                }
            }
        }

        return map.ToDictionary(
            static kvp => kvp.Key,
            static kvp => (IReadOnlyCollection<string>)kvp.Value.ToArray(),
            StringComparer.OrdinalIgnoreCase);
    }
}
