using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using StellaOps.Feedser.Models;

namespace StellaOps.Feedser.Merge.Identity;

/// <summary>
/// Represents a connected component of advisories that refer to the same vulnerability.
/// </summary>
public sealed class AdvisoryIdentityCluster
{
    public AdvisoryIdentityCluster(string advisoryKey, IEnumerable<Advisory> advisories, IEnumerable<AliasIdentity> aliases)
    {
        AdvisoryKey = !string.IsNullOrWhiteSpace(advisoryKey)
            ? advisoryKey.Trim()
            : throw new ArgumentException("Canonical advisory key must be provided.", nameof(advisoryKey));

        var advisoriesArray = (advisories ?? throw new ArgumentNullException(nameof(advisories)))
            .Where(static advisory => advisory is not null)
            .OrderBy(static advisory => advisory.AdvisoryKey, StringComparer.OrdinalIgnoreCase)
            .ThenBy(static advisory => advisory.Provenance.Length)
            .ThenBy(static advisory => advisory.Title, StringComparer.OrdinalIgnoreCase)
            .ToImmutableArray();

        if (advisoriesArray.IsDefaultOrEmpty)
        {
            throw new ArgumentException("At least one advisory is required for a cluster.", nameof(advisories));
        }

        var aliasArray = (aliases ?? throw new ArgumentNullException(nameof(aliases)))
            .Where(static alias => alias is not null && !string.IsNullOrWhiteSpace(alias.Value))
            .GroupBy(static alias => alias.Value, StringComparer.OrdinalIgnoreCase)
            .Select(static group =>
            {
                var representative = group
                    .OrderBy(static entry => entry.Scheme ?? string.Empty, StringComparer.OrdinalIgnoreCase)
                    .ThenBy(static entry => entry.Value, StringComparer.OrdinalIgnoreCase)
                    .First();
                return representative;
            })
            .OrderBy(static alias => alias.Scheme ?? string.Empty, StringComparer.OrdinalIgnoreCase)
            .ThenBy(static alias => alias.Value, StringComparer.OrdinalIgnoreCase)
            .ToImmutableArray();

        Advisories = advisoriesArray;
        Aliases = aliasArray;
    }

    public string AdvisoryKey { get; }

    public ImmutableArray<Advisory> Advisories { get; }

    public ImmutableArray<AliasIdentity> Aliases { get; }
}
