using System;
using System.Collections.Generic;
using System.Linq;

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

internal sealed record AdobeBulletinDto(
    string AdvisoryId,
    string Title,
    DateTimeOffset Published,
    IReadOnlyList<AdobeProductEntry> Products,
    IReadOnlyList<string> Cves,
    string DetailUrl,
    string? Summary)
{
    public static AdobeBulletinDto Create(
        string advisoryId,
        string title,
        DateTimeOffset published,
        IEnumerable<AdobeProductEntry>? products,
        IEnumerable<string>? cves,
        Uri detailUri,
        string? summary)
    {
        ArgumentException.ThrowIfNullOrEmpty(advisoryId);
        ArgumentException.ThrowIfNullOrEmpty(title);
        ArgumentNullException.ThrowIfNull(detailUri);

        var productList = products?
            .Where(static p => !string.IsNullOrWhiteSpace(p.Product))
            .Select(static p => p with { Product = p.Product.Trim() })
            .Distinct(AdobeProductEntryComparer.Instance)
            .OrderBy(static p => p.Product, StringComparer.OrdinalIgnoreCase)
            .ThenBy(static p => p.Platform, StringComparer.OrdinalIgnoreCase)
            .ThenBy(static p => p.Track, StringComparer.OrdinalIgnoreCase)
            .ToList()
            ?? new List<AdobeProductEntry>();

        var cveList = cves?.Where(static c => !string.IsNullOrWhiteSpace(c))
            .Select(static c => c.Trim().ToUpperInvariant())
            .Distinct(StringComparer.Ordinal)
            .OrderBy(static c => c, StringComparer.Ordinal)
            .ToList() ?? new List<string>();

        return new AdobeBulletinDto(
            advisoryId.ToUpperInvariant(),
            title.Trim(),
            published.ToUniversalTime(),
            productList,
            cveList,
            detailUri.ToString(),
            string.IsNullOrWhiteSpace(summary) ? null : summary.Trim());
    }
}

internal sealed record AdobeProductEntry(
    string Product,
    string Track,
    string Platform,
    string? AffectedVersion,
    string? UpdatedVersion,
    string? Priority,
    string? Availability);

internal sealed class AdobeProductEntryComparer : IEqualityComparer<AdobeProductEntry>
{
    public static AdobeProductEntryComparer Instance { get; } = new();

    public bool Equals(AdobeProductEntry? x, AdobeProductEntry? y)
    {
        if (ReferenceEquals(x, y))
        {
            return true;
        }

        if (x is null || y is null)
        {
            return false;
        }

        return string.Equals(x.Product, y.Product, StringComparison.OrdinalIgnoreCase)
            && string.Equals(x.Track, y.Track, StringComparison.OrdinalIgnoreCase)
            && string.Equals(x.Platform, y.Platform, StringComparison.OrdinalIgnoreCase)
            && string.Equals(x.AffectedVersion, y.AffectedVersion, StringComparison.OrdinalIgnoreCase)
            && string.Equals(x.UpdatedVersion, y.UpdatedVersion, StringComparison.OrdinalIgnoreCase)
            && string.Equals(x.Priority, y.Priority, StringComparison.OrdinalIgnoreCase)
            && string.Equals(x.Availability, y.Availability, StringComparison.OrdinalIgnoreCase);
    }

    public int GetHashCode(AdobeProductEntry obj)
    {
        var hash = new HashCode();
        hash.Add(obj.Product, StringComparer.OrdinalIgnoreCase);
        hash.Add(obj.Track, StringComparer.OrdinalIgnoreCase);
        hash.Add(obj.Platform, StringComparer.OrdinalIgnoreCase);
        hash.Add(obj.AffectedVersion, StringComparer.OrdinalIgnoreCase);
        hash.Add(obj.UpdatedVersion, StringComparer.OrdinalIgnoreCase);
        hash.Add(obj.Priority, StringComparer.OrdinalIgnoreCase);
        hash.Add(obj.Availability, StringComparer.OrdinalIgnoreCase);
        return hash.ToHashCode();
    }
}
