using System.Linq;
using StellaOps.Feedser.Merge.Services;
using StellaOps.Feedser.Models;

namespace StellaOps.Feedser.Merge.Tests;

public sealed class CanonicalHashCalculatorTests
{
    private static readonly Advisory SampleAdvisory = new(
        advisoryKey: "CVE-2024-0001",
        title: "Sample advisory",
        summary: "A sample summary",
        language: "EN",
        published: DateTimeOffset.Parse("2024-01-01T00:00:00Z"),
        modified: DateTimeOffset.Parse("2024-01-02T00:00:00Z"),
        severity: "high",
        exploitKnown: true,
        aliases: new[] { "GHSA-xyz", "CVE-2024-0001" },
        references: new[]
        {
            new AdvisoryReference("https://example.com/advisory", "external", "vendor", summary: null, provenance: AdvisoryProvenance.Empty),
            new AdvisoryReference("https://example.com/blog", "article", "blog", summary: null, provenance: AdvisoryProvenance.Empty),
        },
        affectedPackages: new[]
        {
            new AffectedPackage(
                type: AffectedPackageTypes.SemVer,
                identifier: "pkg:npm/sample@1.0.0",
                platform: null,
                versionRanges: new[]
                {
                    new AffectedVersionRange("semver", "1.0.0", "1.2.0", null, null, AdvisoryProvenance.Empty),
                    new AffectedVersionRange("semver", "1.2.0", null, null, null, AdvisoryProvenance.Empty),
                },
                statuses: Array.Empty<AffectedPackageStatus>(),
                provenance: new[] { AdvisoryProvenance.Empty })
        },
        cvssMetrics: new[]
        {
            new CvssMetric("3.1", "AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", 9.8, "critical", AdvisoryProvenance.Empty)
        },
        provenance: new[] { AdvisoryProvenance.Empty });

    [Fact]
    public void ComputeHash_ReturnsDeterministicValue()
    {
        var calculator = new CanonicalHashCalculator();
        var first = calculator.ComputeHash(SampleAdvisory);
        var second = calculator.ComputeHash(SampleAdvisory);

        Assert.Equal(first, second);
    }

    [Fact]
    public void ComputeHash_IgnoresOrderingDifferences()
    {
        var calculator = new CanonicalHashCalculator();

        var reordered = new Advisory(
            SampleAdvisory.AdvisoryKey,
            SampleAdvisory.Title,
            SampleAdvisory.Summary,
            SampleAdvisory.Language,
            SampleAdvisory.Published,
            SampleAdvisory.Modified,
            SampleAdvisory.Severity,
            SampleAdvisory.ExploitKnown,
            aliases: SampleAdvisory.Aliases.Reverse().ToArray(),
            references: SampleAdvisory.References.Reverse().ToArray(),
            affectedPackages: SampleAdvisory.AffectedPackages.Reverse().ToArray(),
            cvssMetrics: SampleAdvisory.CvssMetrics.Reverse().ToArray(),
            provenance: SampleAdvisory.Provenance.Reverse().ToArray());

        var originalHash = calculator.ComputeHash(SampleAdvisory);
        var reorderedHash = calculator.ComputeHash(reordered);

        Assert.Equal(originalHash, reorderedHash);
    }

    [Fact]
    public void ComputeHash_NullReturnsEmpty()
    {
        var calculator = new CanonicalHashCalculator();
        Assert.Empty(calculator.ComputeHash(null));
    }
}
