using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using StellaOps.Feedser.Source.Common.Html;

namespace StellaOps.Feedser.Source.Kisa.Internal;

public sealed class KisaDetailParser
{
    private static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.Web)
    {
        PropertyNameCaseInsensitive = true,
        DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
    };

    private readonly HtmlContentSanitizer _sanitizer;

    public KisaDetailParser(HtmlContentSanitizer sanitizer)
        => _sanitizer = sanitizer ?? throw new ArgumentNullException(nameof(sanitizer));

    public KisaParsedAdvisory Parse(Uri detailApiUri, Uri detailPageUri, byte[] payload)
    {
        var response = JsonSerializer.Deserialize<KisaDetailResponse>(payload, SerializerOptions)
            ?? throw new InvalidOperationException("KISA detail payload deserialized to null");

        var idx = response.Idx ?? throw new InvalidOperationException("KISA detail missing IDX");
        var contentHtml = _sanitizer.Sanitize(response.ContentHtml ?? string.Empty, detailPageUri);

        return new KisaParsedAdvisory(
            idx,
            Normalize(response.Title) ?? idx,
            Normalize(response.Summary),
            contentHtml,
            Normalize(response.Severity),
            response.Published,
            response.Updated ?? response.Published,
            detailApiUri,
            detailPageUri,
            NormalizeArray(response.CveIds),
            MapReferences(response.References),
            MapProducts(response.Products));
    }

    private static IReadOnlyList<string> NormalizeArray(string[]? values)
    {
        if (values is null || values.Length == 0)
        {
            return Array.Empty<string>();
        }

        return values
            .Select(Normalize)
            .Where(static value => !string.IsNullOrWhiteSpace(value))
            .Distinct(StringComparer.OrdinalIgnoreCase)
            .OrderBy(static value => value, StringComparer.OrdinalIgnoreCase)
            .ToArray()!;
    }

    private static IReadOnlyList<KisaParsedReference> MapReferences(KisaReferenceDto[]? references)
    {
        if (references is null || references.Length == 0)
        {
            return Array.Empty<KisaParsedReference>();
        }

        return references
            .Where(static reference => !string.IsNullOrWhiteSpace(reference.Url))
            .Select(reference => new KisaParsedReference(reference.Url!, Normalize(reference.Label)))
            .DistinctBy(static reference => reference.Url, StringComparer.OrdinalIgnoreCase)
            .ToArray();
    }

    private static IReadOnlyList<KisaParsedProduct> MapProducts(KisaProductDto[]? products)
    {
        if (products is null || products.Length == 0)
        {
            return Array.Empty<KisaParsedProduct>();
        }

        return products
            .Where(static product => !string.IsNullOrWhiteSpace(product.Vendor) || !string.IsNullOrWhiteSpace(product.Name))
            .Select(product => new KisaParsedProduct(
                Normalize(product.Vendor),
                Normalize(product.Name),
                Normalize(product.Versions)))
            .ToArray();
    }

    private static string? Normalize(string? value)
        => string.IsNullOrWhiteSpace(value)
            ? null
            : value.Normalize(NormalizationForm.FormC).Trim();
}

public sealed record KisaParsedAdvisory(
    string AdvisoryId,
    string Title,
    string? Summary,
    string ContentHtml,
    string? Severity,
    DateTimeOffset? Published,
    DateTimeOffset? Modified,
    Uri DetailApiUri,
    Uri DetailPageUri,
    IReadOnlyList<string> CveIds,
    IReadOnlyList<KisaParsedReference> References,
    IReadOnlyList<KisaParsedProduct> Products);

public sealed record KisaParsedReference(string Url, string? Label);

public sealed record KisaParsedProduct(string? Vendor, string? Name, string? Versions);
