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

namespace StellaOps.Feedser.Merge.Tests;

public sealed class AffectedPackagePrecedenceResolverTests
{
    [Fact]
    public void Merge_PrefersRedHatOverNvdForSameCpe()
    {
        var redHat = new AffectedPackage(
            type: AffectedPackageTypes.Cpe,
            identifier: "cpe:2.3:o:redhat:enterprise_linux:9:*:*:*:*:*:*:*",
            platform: "RHEL 9",
            versionRanges: Array.Empty<AffectedVersionRange>(),
            statuses: new[]
            {
                new AffectedPackageStatus(
                    status: "known_affected",
                    provenance: new AdvisoryProvenance("redhat", "oval", "RHEL-9", DateTimeOffset.Parse("2025-10-01T00:00:00Z")))
            },
            provenance: new[]
            {
                new AdvisoryProvenance("redhat", "oval", "RHEL-9", DateTimeOffset.Parse("2025-10-01T00:00:00Z"))
            });

        var nvd = new AffectedPackage(
            type: AffectedPackageTypes.Cpe,
            identifier: "cpe:2.3:o:redhat:enterprise_linux:9:*:*:*:*:*:*:*",
            platform: "RHEL 9",
            versionRanges: new[]
            {
                new AffectedVersionRange(
                    rangeKind: "cpe",
                    introducedVersion: null,
                    fixedVersion: null,
                    lastAffectedVersion: null,
                    rangeExpression: "<=9.0",
                    provenance: new AdvisoryProvenance("nvd", "cpe_match", "RHEL-9", DateTimeOffset.Parse("2025-09-30T00:00:00Z")))
            },
            provenance: new[]
            {
                new AdvisoryProvenance("nvd", "cpe_match", "RHEL-9", DateTimeOffset.Parse("2025-09-30T00:00:00Z"))
            });

        var resolver = new AffectedPackagePrecedenceResolver();
        var result = resolver.Merge(new[] { nvd, redHat });

        var package = Assert.Single(result.Packages);
        Assert.Equal("cpe:2.3:o:redhat:enterprise_linux:9:*:*:*:*:*:*:*", package.Identifier);
        Assert.Empty(package.VersionRanges); // NVD range overridden
        Assert.Contains(package.Statuses, status => status.Status == "known_affected");
        Assert.Contains(package.Provenance, provenance => provenance.Source == "redhat");
        Assert.Contains(package.Provenance, provenance => provenance.Source == "nvd");

        var rangeOverride = Assert.Single(result.Overrides);
        Assert.Equal("cpe:2.3:o:redhat:enterprise_linux:9:*:*:*:*:*:*:*", rangeOverride.Identifier);
        Assert.Equal(0, rangeOverride.PrimaryRank);
        Assert.True(rangeOverride.SuppressedRank >= rangeOverride.PrimaryRank);
        Assert.Equal(0, rangeOverride.PrimaryRangeCount);
        Assert.Equal(1, rangeOverride.SuppressedRangeCount);
    }

    [Fact]
    public void Merge_KeepsNvdWhenNoHigherPrecedence()
    {
        var nvd = new AffectedPackage(
            type: AffectedPackageTypes.Cpe,
            identifier: "cpe:2.3:a:example:product:1.0:*:*:*:*:*:*:*",
            platform: null,
            versionRanges: new[]
            {
                new AffectedVersionRange(
                    rangeKind: "semver",
                    introducedVersion: null,
                    fixedVersion: "1.0.1",
                    lastAffectedVersion: null,
                    rangeExpression: "<1.0.1",
                    provenance: new AdvisoryProvenance("nvd", "cpe_match", "product", DateTimeOffset.Parse("2025-09-01T00:00:00Z")))
            },
            provenance: new[]
            {
                new AdvisoryProvenance("nvd", "cpe_match", "product", DateTimeOffset.Parse("2025-09-01T00:00:00Z"))
            });

        var resolver = new AffectedPackagePrecedenceResolver();
        var result = resolver.Merge(new[] { nvd });

        var package = Assert.Single(result.Packages);
        Assert.Equal(nvd.Identifier, package.Identifier);
        Assert.Equal(nvd.VersionRanges.Single().RangeExpression, package.VersionRanges.Single().RangeExpression);
        Assert.Equal("nvd", package.Provenance.Single().Source);
        Assert.Empty(result.Overrides);
    }
}
