using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Time.Testing;
using StellaOps.Feedser.Merge.Options;
using StellaOps.Feedser.Merge.Services;
using StellaOps.Feedser.Models;

namespace StellaOps.Feedser.Merge.Tests;

public sealed class AdvisoryPrecedenceMergerTests
{
    [Fact]
    public void Merge_PrefersVendorPrecedenceOverNvd()
    {
        var timeProvider = new FakeTimeProvider(new DateTimeOffset(2025, 1, 1, 0, 0, 0, TimeSpan.Zero));
        var merger = new AdvisoryPrecedenceMerger(new AffectedPackagePrecedenceResolver(), timeProvider);
        using var metrics = new MetricCollector("StellaOps.Feedser.Merge");

        var (redHat, nvd) = CreateVendorAndRegistryAdvisories();
        var expectedMergeTimestamp = timeProvider.GetUtcNow();

        var merged = merger.Merge(new[] { nvd, redHat });

        Assert.Equal("CVE-2025-1000", merged.AdvisoryKey);
        Assert.Equal("Red Hat Security Advisory", merged.Title);
        Assert.Equal("Vendor-confirmed impact on RHEL 9.", merged.Summary);
        Assert.Equal("high", merged.Severity);
        Assert.Equal(redHat.Published, merged.Published);
        Assert.Equal(redHat.Modified, merged.Modified);
        Assert.Contains("RHSA-2025:0001", merged.Aliases);
        Assert.Contains("CVE-2025-1000", merged.Aliases);

        var package = Assert.Single(merged.AffectedPackages);
        Assert.Equal("cpe:2.3:o:redhat:enterprise_linux:9:*:*:*:*:*:*:*", package.Identifier);
        Assert.Empty(package.VersionRanges); // NVD range suppressed by vendor precedence
        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");

        Assert.Contains(merged.CvssMetrics, metric => metric.Provenance.Source == "redhat");
        Assert.Contains(merged.CvssMetrics, metric => metric.Provenance.Source == "nvd");

        var mergeProvenance = merged.Provenance.Single(p => p.Source == "merge");
        Assert.Equal("precedence", mergeProvenance.Kind);
        Assert.Equal(expectedMergeTimestamp, mergeProvenance.RecordedAt);
        Assert.Contains("redhat", mergeProvenance.Value, StringComparison.OrdinalIgnoreCase);
        Assert.Contains("nvd", mergeProvenance.Value, StringComparison.OrdinalIgnoreCase);

        var rangeMeasurement = Assert.Single(metrics.Measurements, measurement => measurement.Name == "feedser.merge.range_overrides");
        Assert.Equal(1, rangeMeasurement.Value);
        Assert.Contains(rangeMeasurement.Tags, tag => string.Equals(tag.Key, "suppressed_source", StringComparison.Ordinal) && tag.Value?.ToString()?.Contains("nvd", StringComparison.OrdinalIgnoreCase) == true);

        var severityConflict = Assert.Single(metrics.Measurements, measurement => measurement.Name == "feedser.merge.conflicts");
        Assert.Equal(1, severityConflict.Value);
        Assert.Contains(severityConflict.Tags, tag => string.Equals(tag.Key, "type", StringComparison.Ordinal) && string.Equals(tag.Value?.ToString(), "severity", StringComparison.OrdinalIgnoreCase));
    }

    [Fact]
    public void Merge_KevOnlyTogglesExploitKnown()
    {
        var timeProvider = new FakeTimeProvider(new DateTimeOffset(2025, 2, 1, 0, 0, 0, TimeSpan.Zero));
        var merger = new AdvisoryPrecedenceMerger(new AffectedPackagePrecedenceResolver(), timeProvider);

        var nvdProvenance = new AdvisoryProvenance("nvd", "document", "https://nvd", timeProvider.GetUtcNow());
        var baseAdvisory = new Advisory(
            "CVE-2025-2000",
                "CVE-2025-2000",
                "Base registry summary",
                "en",
                new DateTimeOffset(2025, 1, 5, 0, 0, 0, TimeSpan.Zero),
                new DateTimeOffset(2025, 1, 6, 0, 0, 0, TimeSpan.Zero),
                "medium",
                exploitKnown: false,
                aliases: new[] { "CVE-2025-2000" },
                credits: Array.Empty<AdvisoryCredit>(),
                references: Array.Empty<AdvisoryReference>(),
                affectedPackages: new[]
                {
                    new AffectedPackage(
                        AffectedPackageTypes.Cpe,
                        "cpe:2.3:a:example:product:2.0:*:*:*:*:*:*:*",
                        null,
                        new[]
                        {
                            new AffectedVersionRange(
                                "semver",
                                "2.0.0",
                                "2.0.5",
                                null,
                                "<2.0.5",
                                new AdvisoryProvenance("nvd", "cpe_match", "product", timeProvider.GetUtcNow()))
                        },
                        Array.Empty<AffectedPackageStatus>(),
                        new[] { nvdProvenance })
                },
                cvssMetrics: Array.Empty<CvssMetric>(),
                provenance: new[] { nvdProvenance });

        var kevProvenance = new AdvisoryProvenance("kev", "catalog", "CVE-2025-2000", timeProvider.GetUtcNow());
        var kevAdvisory = new Advisory(
            "CVE-2025-2000",
            "Known Exploited Vulnerability",
            summary: null,
            language: null,
            published: null,
            modified: null,
            severity: null,
            exploitKnown: true,
            aliases: new[] { "KEV-CVE-2025-2000" },
            credits: Array.Empty<AdvisoryCredit>(),
            references: Array.Empty<AdvisoryReference>(),
            affectedPackages: Array.Empty<AffectedPackage>(),
            cvssMetrics: Array.Empty<CvssMetric>(),
            provenance: new[] { kevProvenance });

        var merged = merger.Merge(new[] { baseAdvisory, kevAdvisory });

        Assert.True(merged.ExploitKnown);
        Assert.Equal("medium", merged.Severity); // KEV must not override severity
        Assert.Equal("Base registry summary", merged.Summary);
        Assert.Contains("CVE-2025-2000", merged.Aliases);
        Assert.Contains("KEV-CVE-2025-2000", merged.Aliases);
        Assert.Contains(merged.Provenance, provenance => provenance.Source == "kev");
        Assert.Contains(merged.Provenance, provenance => provenance.Source == "merge");
    }

    [Fact]
    public void Merge_UnionsCreditsFromSources()
    {
        var timeProvider = new FakeTimeProvider();
        var merger = new AdvisoryPrecedenceMerger(new AffectedPackagePrecedenceResolver(), timeProvider);

        var ghsaCredits = new[]
        {
            new AdvisoryCredit(
                displayName: "maintainer-team",
                role: "remediation_developer",
                contacts: new[] { "https://github.com/maintainer-team" },
                provenance: new AdvisoryProvenance(
                    "ghsa",
                    "credit",
                    "mantainer-team",
                    timeProvider.GetUtcNow(),
                    new[] { ProvenanceFieldMasks.Credits })),
            new AdvisoryCredit(
                displayName: "security-reporter",
                role: "reporter",
                contacts: new[] { "https://github.com/security-reporter" },
                provenance: new AdvisoryProvenance(
                    "ghsa",
                    "credit",
                    "security-reporter",
                    timeProvider.GetUtcNow(),
                    new[] { ProvenanceFieldMasks.Credits })),
        };

        var ghsa = new Advisory(
            "CVE-2025-9000",
            "GHSA advisory",
            "Reported in GHSA",
            "en",
            timeProvider.GetUtcNow(),
            timeProvider.GetUtcNow(),
            "high",
            exploitKnown: false,
            aliases: new[] { "GHSA-aaaa-bbbb-cccc", "CVE-2025-9000" },
            credits: ghsaCredits,
            references: Array.Empty<AdvisoryReference>(),
            affectedPackages: Array.Empty<AffectedPackage>(),
            cvssMetrics: Array.Empty<CvssMetric>(),
            provenance: new[] { new AdvisoryProvenance("ghsa", "document", "https://github.com/advisories/GHSA-aaaa-bbbb-cccc", timeProvider.GetUtcNow(), new[] { ProvenanceFieldMasks.Advisory }) });

        var osvCredits = new[]
        {
            new AdvisoryCredit(
                displayName: "osv-researcher",
                role: "reporter",
                contacts: new[] { "mailto:osv-researcher@example.com" },
                provenance: new AdvisoryProvenance(
                    "osv",
                    "credit",
                    "osv-researcher",
                    timeProvider.GetUtcNow(),
                    new[] { ProvenanceFieldMasks.Credits })),
            new AdvisoryCredit(
                displayName: "maintainer-team",
                role: "remediation_developer",
                contacts: new[] { "https://github.com/maintainer-team" },
                provenance: new AdvisoryProvenance(
                    "osv",
                    "credit",
                    "maintainer-team",
                    timeProvider.GetUtcNow(),
                    new[] { ProvenanceFieldMasks.Credits })),
        };

        var osv = new Advisory(
            "CVE-2025-9000",
            "OSV advisory",
            "Reported in OSV.dev",
            "en",
            timeProvider.GetUtcNow().AddDays(-1),
            timeProvider.GetUtcNow().AddHours(-1),
            "medium",
            exploitKnown: false,
            aliases: new[] { "CVE-2025-9000" },
            credits: osvCredits,
            references: Array.Empty<AdvisoryReference>(),
            affectedPackages: Array.Empty<AffectedPackage>(),
            cvssMetrics: Array.Empty<CvssMetric>(),
            provenance: new[] { new AdvisoryProvenance("osv", "document", "https://osv.dev/vulnerability/CVE-2025-9000", timeProvider.GetUtcNow(), new[] { ProvenanceFieldMasks.Advisory }) });

        var merged = merger.Merge(new[] { ghsa, osv });

        Assert.Equal("CVE-2025-9000", merged.AdvisoryKey);
        Assert.Contains(merged.Credits, credit =>
            string.Equals(credit.DisplayName, "maintainer-team", StringComparison.OrdinalIgnoreCase) &&
            string.Equals(credit.Role, "remediation_developer", StringComparison.OrdinalIgnoreCase));
        Assert.Contains(merged.Credits, credit =>
            string.Equals(credit.DisplayName, "osv-researcher", StringComparison.OrdinalIgnoreCase) &&
            string.Equals(credit.Role, "reporter", StringComparison.OrdinalIgnoreCase));
        Assert.Contains(merged.Credits, credit =>
            string.Equals(credit.DisplayName, "security-reporter", StringComparison.OrdinalIgnoreCase) &&
            string.Equals(credit.Role, "reporter", StringComparison.OrdinalIgnoreCase));

        Assert.Contains(merged.Credits, credit => credit.Provenance.Source == "ghsa");
        Assert.Contains(merged.Credits, credit => credit.Provenance.Source == "osv");
    }

    [Fact]
    public void Merge_AcscActsAsEnrichmentSource()
    {
        var timeProvider = new FakeTimeProvider(new DateTimeOffset(2025, 10, 12, 0, 0, 0, TimeSpan.Zero));
        var merger = new AdvisoryPrecedenceMerger(new AffectedPackagePrecedenceResolver(), timeProvider);

        var vendorDocumentProvenance = new AdvisoryProvenance(
            source: "vndr-cisco",
            kind: "document",
            value: "https://vendor.example/advisories/router-critical",
            recordedAt: timeProvider.GetUtcNow(),
            fieldMask: new[] { ProvenanceFieldMasks.Advisory });

        var vendorReference = new AdvisoryReference(
            "https://vendor.example/advisories/router-critical",
            kind: "advisory",
            sourceTag: "vendor",
            summary: "Vendor advisory",
            provenance: new AdvisoryProvenance("vndr-cisco", "reference", "https://vendor.example/advisories/router-critical", timeProvider.GetUtcNow()));

        var vendorPackage = new AffectedPackage(
            AffectedPackageTypes.Vendor,
            "ExampleCo Router X",
            platform: null,
            versionRanges: Array.Empty<AffectedVersionRange>(),
            statuses: Array.Empty<AffectedPackageStatus>(),
            normalizedVersions: Array.Empty<NormalizedVersionRule>(),
            provenance: new[] { vendorDocumentProvenance });

        var vendor = new Advisory(
            advisoryKey: "acsc-2025-010",
            title: "Vendor Critical Router Advisory",
            summary: "Vendor-confirmed exploit.",
            language: "en",
            published: new DateTimeOffset(2025, 10, 11, 23, 0, 0, TimeSpan.Zero),
            modified: new DateTimeOffset(2025, 10, 11, 23, 30, 0, TimeSpan.Zero),
            severity: "critical",
            exploitKnown: false,
            aliases: new[] { "VENDOR-2025-010" },
            references: new[] { vendorReference },
            affectedPackages: new[] { vendorPackage },
            cvssMetrics: Array.Empty<CvssMetric>(),
            provenance: new[] { vendorDocumentProvenance });

        var acscDocumentProvenance = new AdvisoryProvenance(
            source: "acsc",
            kind: "document",
            value: "https://origin.example/feeds/alerts/rss",
            recordedAt: timeProvider.GetUtcNow(),
            fieldMask: new[] { ProvenanceFieldMasks.Advisory });

        var acscReference = new AdvisoryReference(
            "https://origin.example/advisories/router-critical",
            kind: "advisory",
            sourceTag: "acsc",
            summary: "ACSC alert",
            provenance: new AdvisoryProvenance("acsc", "reference", "https://origin.example/advisories/router-critical", timeProvider.GetUtcNow()));

        var acscPackage = new AffectedPackage(
            AffectedPackageTypes.Vendor,
            "ExampleCo Router X",
            platform: null,
            versionRanges: Array.Empty<AffectedVersionRange>(),
            statuses: Array.Empty<AffectedPackageStatus>(),
            normalizedVersions: Array.Empty<NormalizedVersionRule>(),
            provenance: new[] { acscDocumentProvenance });

        var acsc = new Advisory(
            advisoryKey: "acsc-2025-010",
            title: "ACSC Router Alert",
            summary: "ACSC recommends installing vendor update.",
            language: "en",
            published: new DateTimeOffset(2025, 10, 12, 0, 0, 0, TimeSpan.Zero),
            modified: null,
            severity: "medium",
            exploitKnown: false,
            aliases: new[] { "ACSC-2025-010" },
            references: new[] { acscReference },
            affectedPackages: new[] { acscPackage },
            cvssMetrics: Array.Empty<CvssMetric>(),
            provenance: new[] { acscDocumentProvenance });

        var merged = merger.Merge(new[] { acsc, vendor });

        Assert.Equal("critical", merged.Severity); // ACSC must not override vendor severity
        Assert.Equal("Vendor-confirmed exploit.", merged.Summary);

        Assert.Contains("ACSC-2025-010", merged.Aliases);
        Assert.Contains("VENDOR-2025-010", merged.Aliases);

        Assert.Contains(merged.References, reference => reference.SourceTag == "vendor" && reference.Url == vendorReference.Url);
        Assert.Contains(merged.References, reference => reference.SourceTag == "acsc" && reference.Url == acscReference.Url);

        var enrichedPackage = Assert.Single(merged.AffectedPackages, package => package.Identifier == "ExampleCo Router X");
        Assert.Contains(enrichedPackage.Provenance, provenance => provenance.Source == "vndr-cisco");
        Assert.Contains(enrichedPackage.Provenance, provenance => provenance.Source == "acsc");

        Assert.Contains(merged.Provenance, provenance => provenance.Source == "acsc");
        Assert.Contains(merged.Provenance, provenance => provenance.Source == "vndr-cisco");
        Assert.Contains(merged.Provenance, provenance => provenance.Source == "merge" && (provenance.Value?.Contains("acsc", StringComparison.OrdinalIgnoreCase) ?? false));
    }

    [Fact]
    public void Merge_RecordsNormalizedRuleMetrics()
    {
        var now = new DateTimeOffset(2025, 3, 1, 0, 0, 0, TimeSpan.Zero);
        var timeProvider = new FakeTimeProvider(now);
        var merger = new AdvisoryPrecedenceMerger(new AffectedPackagePrecedenceResolver(), timeProvider);
        using var metrics = new MetricCollector("StellaOps.Feedser.Merge");

        var normalizedRule = new NormalizedVersionRule(
            NormalizedVersionSchemes.SemVer,
            NormalizedVersionRuleTypes.Range,
            min: "1.0.0",
            minInclusive: true,
            max: "2.0.0",
            maxInclusive: false,
            notes: "ghsa:GHSA-xxxx-yyyy");

        var ghsaProvenance = new AdvisoryProvenance("ghsa", "package", "pkg:npm/example", now);
        var ghsaPackage = new AffectedPackage(
            AffectedPackageTypes.SemVer,
            "pkg:npm/example",
            platform: null,
            versionRanges: new[]
            {
                new AffectedVersionRange(
                    NormalizedVersionSchemes.SemVer,
                    "1.0.0",
                    "2.0.0",
                    null,
                    ">= 1.0.0 < 2.0.0",
                    ghsaProvenance)
            },
            statuses: Array.Empty<AffectedPackageStatus>(),
            provenance: new[]
            {
                ghsaProvenance,
            },
            normalizedVersions: new[] { normalizedRule });

        var nvdPackage = new AffectedPackage(
            AffectedPackageTypes.SemVer,
            "pkg:npm/example",
            platform: null,
            versionRanges: new[]
            {
                new AffectedVersionRange(
                    NormalizedVersionSchemes.SemVer,
                    "1.0.0",
                    "2.0.0",
                    null,
                    ">= 1.0.0 < 2.0.0",
                    new AdvisoryProvenance("nvd", "cpe_match", "pkg:npm/example", now))
            },
            statuses: Array.Empty<AffectedPackageStatus>(),
            provenance: new[]
            {
                new AdvisoryProvenance("nvd", "document", "https://nvd.nist.gov/vuln/detail/CVE-2025-7000", now),
            },
            normalizedVersions: Array.Empty<NormalizedVersionRule>());

        var nvdExclusivePackage = new AffectedPackage(
            AffectedPackageTypes.SemVer,
            "pkg:npm/another",
            platform: null,
            versionRanges: new[]
            {
                new AffectedVersionRange(
                    NormalizedVersionSchemes.SemVer,
                    "3.0.0",
                    null,
                    null,
                    ">= 3.0.0",
                    new AdvisoryProvenance("nvd", "cpe_match", "pkg:npm/another", now))
            },
            statuses: Array.Empty<AffectedPackageStatus>(),
            provenance: new[]
            {
                new AdvisoryProvenance("nvd", "document", "https://nvd.nist.gov/vuln/detail/CVE-2025-7000", now),
            },
            normalizedVersions: Array.Empty<NormalizedVersionRule>());

        var ghsaAdvisory = new Advisory(
            "CVE-2025-7000",
            "GHSA advisory",
            "GHSA summary",
            "en",
            now,
            now,
            "high",
            exploitKnown: false,
            aliases: new[] { "CVE-2025-7000", "GHSA-xxxx-yyyy" },
            credits: Array.Empty<AdvisoryCredit>(),
            references: Array.Empty<AdvisoryReference>(),
            affectedPackages: new[] { ghsaPackage },
            cvssMetrics: Array.Empty<CvssMetric>(),
            provenance: new[]
            {
                new AdvisoryProvenance("ghsa", "document", "https://github.com/advisories/GHSA-xxxx-yyyy", now),
            });

        var nvdAdvisory = new Advisory(
            "CVE-2025-7000",
            "NVD entry",
            "NVD summary",
            "en",
            now,
            now,
            "high",
            exploitKnown: false,
            aliases: new[] { "CVE-2025-7000" },
            credits: Array.Empty<AdvisoryCredit>(),
            references: Array.Empty<AdvisoryReference>(),
            affectedPackages: new[] { nvdPackage, nvdExclusivePackage },
            cvssMetrics: Array.Empty<CvssMetric>(),
            provenance: new[]
            {
                new AdvisoryProvenance("nvd", "document", "https://nvd.nist.gov/vuln/detail/CVE-2025-7000", now),
            });

        var merged = merger.Merge(new[] { nvdAdvisory, ghsaAdvisory });
        Assert.Equal(2, merged.AffectedPackages.Length);

        var normalizedPackage = Assert.Single(merged.AffectedPackages, pkg => pkg.Identifier == "pkg:npm/example");
        Assert.Single(normalizedPackage.NormalizedVersions);

        var missingPackage = Assert.Single(merged.AffectedPackages, pkg => pkg.Identifier == "pkg:npm/another");
        Assert.Empty(missingPackage.NormalizedVersions);
        Assert.NotEmpty(missingPackage.VersionRanges);

        var normalizedMeasurements = metrics.Measurements.Where(m => m.Name == "feedser.merge.normalized_rules").ToList();
        Assert.Contains(normalizedMeasurements, measurement =>
            measurement.Value == 1
                && measurement.Tags.Any(tag => string.Equals(tag.Key, "scheme", StringComparison.Ordinal) && string.Equals(tag.Value?.ToString(), "semver", StringComparison.Ordinal))
                && measurement.Tags.Any(tag => string.Equals(tag.Key, "package_type", StringComparison.Ordinal) && string.Equals(tag.Value?.ToString(), "semver", StringComparison.Ordinal)));

        var missingMeasurements = metrics.Measurements.Where(m => m.Name == "feedser.merge.normalized_rules_missing").ToList();
        var missingMeasurement = Assert.Single(missingMeasurements);
        Assert.Equal(1, missingMeasurement.Value);
        Assert.Contains(missingMeasurement.Tags, tag => string.Equals(tag.Key, "package_type", StringComparison.Ordinal) && string.Equals(tag.Value?.ToString(), "semver", StringComparison.Ordinal));
    }

    [Fact]
    public void Merge_RespectsConfiguredPrecedenceOverrides()
    {
        var timeProvider = new FakeTimeProvider(new DateTimeOffset(2025, 3, 1, 0, 0, 0, TimeSpan.Zero));
        var options = new AdvisoryPrecedenceOptions
        {
            Ranks = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
            {
                ["nvd"] = 0,
                ["redhat"] = 5,
            }
        };

        var logger = new TestLogger<AdvisoryPrecedenceMerger>();
        using var metrics = new MetricCollector("StellaOps.Feedser.Merge");

        var merger = new AdvisoryPrecedenceMerger(
            new AffectedPackagePrecedenceResolver(),
            options,
            timeProvider,
            logger);

        var (redHat, nvd) = CreateVendorAndRegistryAdvisories();
        var merged = merger.Merge(new[] { redHat, nvd });

        Assert.Equal("CVE-2025-1000", merged.AdvisoryKey);
        Assert.Equal("CVE-2025-1000", merged.Title); // NVD preferred
        Assert.Equal("NVD summary", merged.Summary);
        Assert.Equal("medium", merged.Severity);

        var package = Assert.Single(merged.AffectedPackages);
        Assert.NotEmpty(package.VersionRanges); // Vendor range no longer overrides
        Assert.Contains(package.Provenance, provenance => provenance.Source == "nvd");
        Assert.Contains(package.Provenance, provenance => provenance.Source == "redhat");

        var overrideMeasurement = Assert.Single(metrics.Measurements, m => m.Name == "feedser.merge.overrides");
        Assert.Equal(1, overrideMeasurement.Value);
        Assert.Contains(overrideMeasurement.Tags, tag => tag.Key == "primary_source" && string.Equals(tag.Value?.ToString(), "nvd", StringComparison.OrdinalIgnoreCase));
        Assert.Contains(overrideMeasurement.Tags, tag => tag.Key == "suppressed_source" && tag.Value?.ToString()?.Contains("redhat", StringComparison.OrdinalIgnoreCase) == true);

        Assert.DoesNotContain(metrics.Measurements, measurement => measurement.Name == "feedser.merge.range_overrides");

        var conflictMeasurement = Assert.Single(metrics.Measurements, measurement => measurement.Name == "feedser.merge.conflicts");
        Assert.Equal(1, conflictMeasurement.Value);
        Assert.Contains(conflictMeasurement.Tags, tag => tag.Key == "type" && string.Equals(tag.Value?.ToString(), "severity", StringComparison.OrdinalIgnoreCase));
        Assert.Contains(conflictMeasurement.Tags, tag => tag.Key == "reason" && string.Equals(tag.Value?.ToString(), "mismatch", StringComparison.OrdinalIgnoreCase));

        var logEntry = Assert.Single(logger.Entries, entry => entry.EventId.Name == "AdvisoryOverride");
        Assert.Equal(LogLevel.Information, logEntry.Level);
        Assert.NotNull(logEntry.StructuredState);
        Assert.Contains(logEntry.StructuredState!, kvp =>
            (string.Equals(kvp.Key, "Override", StringComparison.Ordinal) ||
             string.Equals(kvp.Key, "@Override", StringComparison.Ordinal)) &&
            kvp.Value is not null);
    }

    private static (Advisory Vendor, Advisory Registry) CreateVendorAndRegistryAdvisories()
    {
        var redHatPublished = new DateTimeOffset(2025, 1, 10, 0, 0, 0, TimeSpan.Zero);
        var redHatModified = redHatPublished.AddDays(1);
        var redHatProvenance = new AdvisoryProvenance("redhat", "advisory", "RHSA-2025:0001", redHatModified);
        var redHatPackage = new AffectedPackage(
            AffectedPackageTypes.Cpe,
            "cpe:2.3:o:redhat:enterprise_linux:9:*:*:*:*:*:*:*",
            "rhel-9",
            Array.Empty<AffectedVersionRange>(),
            new[] { new AffectedPackageStatus("known_affected", redHatProvenance) },
            new[] { redHatProvenance });
        var redHat = new Advisory(
            "CVE-2025-1000",
            "Red Hat Security Advisory",
            "Vendor-confirmed impact on RHEL 9.",
            "en",
            redHatPublished,
            redHatModified,
            "high",
            exploitKnown: false,
            aliases: new[] { "CVE-2025-1000", "RHSA-2025:0001" },
            credits: Array.Empty<AdvisoryCredit>(),
            references: new[]
            {
                new AdvisoryReference(
                    "https://access.redhat.com/errata/RHSA-2025:0001",
                    "advisory",
                    "redhat",
                    "Red Hat errata",
                    redHatProvenance)
            },
            affectedPackages: new[] { redHatPackage },
            cvssMetrics: new[]
            {
                new CvssMetric(
                    "3.1",
                    "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
                    9.8,
                    "critical",
                    new AdvisoryProvenance("redhat", "cvss", "RHSA-2025:0001", redHatModified))
            },
            provenance: new[] { redHatProvenance });

        var nvdPublished = new DateTimeOffset(2025, 1, 5, 0, 0, 0, TimeSpan.Zero);
        var nvdModified = nvdPublished.AddDays(2);
        var nvdProvenance = new AdvisoryProvenance("nvd", "document", "https://nvd.nist.gov/vuln/detail/CVE-2025-1000", nvdModified);
        var nvdPackage = new AffectedPackage(
            AffectedPackageTypes.Cpe,
            "cpe:2.3:o:redhat:enterprise_linux:9:*:*:*:*:*:*:*",
            "rhel-9",
            new[]
            {
                new AffectedVersionRange(
                    "cpe",
                    null,
                    null,
                    null,
                    "<=9.0",
                    new AdvisoryProvenance("nvd", "cpe_match", "RHEL", nvdModified))
            },
            Array.Empty<AffectedPackageStatus>(),
            new[] { nvdProvenance });
        var nvd = new Advisory(
            "CVE-2025-1000",
            "CVE-2025-1000",
            "NVD summary",
            "en",
            nvdPublished,
            nvdModified,
            "medium",
            exploitKnown: false,
            aliases: new[] { "CVE-2025-1000" },
            credits: Array.Empty<AdvisoryCredit>(),
            references: new[]
            {
                new AdvisoryReference(
                    "https://nvd.nist.gov/vuln/detail/CVE-2025-1000",
                    "advisory",
                    "nvd",
                    "NVD advisory",
                    nvdProvenance)
            },
            affectedPackages: new[] { nvdPackage },
            cvssMetrics: new[]
            {
                new CvssMetric(
                    "3.1",
                    "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N",
                    6.8,
                    "medium",
                    new AdvisoryProvenance("nvd", "cvss", "CVE-2025-1000", nvdModified))
            },
            provenance: new[] { nvdProvenance });

        return (redHat, nvd);
    }
}
