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

namespace StellaOps.Feedser.Source.CertBund.Internal;

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

    private readonly HtmlContentSanitizer _sanitizer;

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

    public CertBundAdvisoryDto Parse(Uri detailUri, Uri portalUri, byte[] payload)
    {
        var detail = JsonSerializer.Deserialize<CertBundDetailResponse>(payload, SerializerOptions)
            ?? throw new InvalidOperationException("CERT-Bund detail payload deserialized to null.");

        var advisoryId = detail.Name ?? throw new InvalidOperationException("CERT-Bund detail missing advisory name.");
        var contentHtml = _sanitizer.Sanitize(detail.Description ?? string.Empty, portalUri);

        return new CertBundAdvisoryDto
        {
            AdvisoryId = advisoryId,
            Title = detail.Title ?? advisoryId,
            Summary = detail.Summary,
            ContentHtml = contentHtml,
            Severity = detail.Severity,
            Language = string.IsNullOrWhiteSpace(detail.Language) ? "de" : detail.Language!,
            Published = detail.Published,
            Modified = detail.Updated ?? detail.Published,
            PortalUri = portalUri,
            DetailUri = detailUri,
            CveIds = detail.CveIds?.Where(static id => !string.IsNullOrWhiteSpace(id))
                .Select(static id => id!.Trim())
                .Distinct(StringComparer.OrdinalIgnoreCase)
                .ToArray() ?? Array.Empty<string>(),
            References = MapReferences(detail.References),
            Products = MapProducts(detail.Products),
        };
    }

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

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

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

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