using System;
using System.Collections.Generic;
using StellaOps.Feedser.Models;
using Xunit;

namespace StellaOps.Feedser.Models.Tests;

public sealed class OsvGhsaParityInspectorTests
{
    [Fact]
    public void Compare_ReturnsNoIssues_WhenDatasetsMatch()
    {
        var ghsaId = "GHSA-1111";
        var osv = CreateOsvAdvisory(ghsaId, severity: "high", includeRanges: true);
        var ghsa = CreateGhsaAdvisory(ghsaId, severity: "high", includeRanges: true);

        var report = OsvGhsaParityInspector.Compare(new[] { osv }, new[] { ghsa });

        Assert.False(report.HasIssues);
        Assert.Equal(1, report.TotalGhsaIds);
        Assert.Empty(report.Issues);
    }

    [Fact]
    public void Compare_FlagsMissingOsvEntry()
    {
        var ghsaId = "GHSA-2222";
        var ghsa = CreateGhsaAdvisory(ghsaId, severity: "medium", includeRanges: true);

        var report = OsvGhsaParityInspector.Compare(Array.Empty<Advisory>(), new[] { ghsa });

        var issue = Assert.Single(report.Issues);
        Assert.Equal("missing_osv", issue.IssueKind);
        Assert.Equal(ghsaId, issue.GhsaId);
        Assert.Contains(ProvenanceFieldMasks.AffectedPackages, issue.FieldMask);
    }

    [Fact]
    public void Compare_FlagsMissingGhsaEntry()
    {
        var ghsaId = "GHSA-2424";
        var osv = CreateOsvAdvisory(ghsaId, severity: "medium", includeRanges: true);

        var report = OsvGhsaParityInspector.Compare(new[] { osv }, Array.Empty<Advisory>());

        var issue = Assert.Single(report.Issues);
        Assert.Equal("missing_ghsa", issue.IssueKind);
        Assert.Equal(ghsaId, issue.GhsaId);
        Assert.Contains(ProvenanceFieldMasks.AffectedPackages, issue.FieldMask);
    }

    [Fact]
    public void Compare_FlagsSeverityMismatch()
    {
        var ghsaId = "GHSA-3333";
        var osv = CreateOsvAdvisory(ghsaId, severity: "low", includeRanges: true);
        var ghsa = CreateGhsaAdvisory(ghsaId, severity: "critical", includeRanges: true);

        var report = OsvGhsaParityInspector.Compare(new[] { osv }, new[] { ghsa });

        var issue = Assert.Single(report.Issues, i => i.IssueKind == "severity_mismatch");
        Assert.Equal(ghsaId, issue.GhsaId);
        Assert.Contains(ProvenanceFieldMasks.Advisory, issue.FieldMask);
    }

    [Fact]
    public void Compare_FlagsRangeMismatch()
    {
        var ghsaId = "GHSA-4444";
        var osv = CreateOsvAdvisory(ghsaId, severity: "high", includeRanges: false);
        var ghsa = CreateGhsaAdvisory(ghsaId, severity: "high", includeRanges: true);

        var report = OsvGhsaParityInspector.Compare(new[] { osv }, new[] { ghsa });

        var issue = Assert.Single(report.Issues, i => i.IssueKind == "range_mismatch");
        Assert.Equal(ghsaId, issue.GhsaId);
        Assert.Contains(ProvenanceFieldMasks.VersionRanges, issue.FieldMask);
    }

    private static Advisory CreateOsvAdvisory(string ghsaId, string? severity, bool includeRanges)
    {
        var timestamp = DateTimeOffset.UtcNow;
        return new Advisory(
            advisoryKey: $"osv-{ghsaId.ToLowerInvariant()}",
            title: $"OSV {ghsaId}",
            summary: null,
            language: null,
            published: timestamp,
            modified: timestamp,
            severity: severity,
            exploitKnown: false,
            aliases: new[] { ghsaId },
            references: Array.Empty<AdvisoryReference>(),
            affectedPackages: includeRanges ? new[] { CreatePackage(timestamp, includeRanges) } : Array.Empty<AffectedPackage>(),
            cvssMetrics: Array.Empty<CvssMetric>(),
            provenance: new[]
            {
                new AdvisoryProvenance("osv", "map", ghsaId, timestamp, new[] { ProvenanceFieldMasks.Advisory })
            });
    }

    private static Advisory CreateGhsaAdvisory(string ghsaId, string? severity, bool includeRanges)
    {
        var timestamp = DateTimeOffset.UtcNow;
        return new Advisory(
            advisoryKey: ghsaId.ToLowerInvariant(),
            title: $"GHSA {ghsaId}",
            summary: null,
            language: null,
            published: timestamp,
            modified: timestamp,
            severity: severity,
            exploitKnown: false,
            aliases: new[] { ghsaId },
            references: Array.Empty<AdvisoryReference>(),
            affectedPackages: includeRanges ? new[] { CreatePackage(timestamp, includeRanges) } : Array.Empty<AffectedPackage>(),
            cvssMetrics: Array.Empty<CvssMetric>(),
            provenance: new[]
            {
                new AdvisoryProvenance("ghsa", "map", ghsaId, timestamp, new[] { ProvenanceFieldMasks.Advisory })
            });
    }

    private static AffectedPackage CreatePackage(DateTimeOffset recordedAt, bool includeRanges)
    {
        var ranges = includeRanges
            ? new[]
            {
                new AffectedVersionRange(
                    rangeKind: "semver",
                    introducedVersion: "1.0.0",
                    fixedVersion: "1.2.0",
                    lastAffectedVersion: null,
                    rangeExpression: null,
                    provenance: new AdvisoryProvenance("mapper", "range", "package@1", recordedAt, new[] { ProvenanceFieldMasks.VersionRanges }),
                    primitives: null)
            }
            : Array.Empty<AffectedVersionRange>();

        return new AffectedPackage(
            type: "semver",
            identifier: "pkg@1",
            platform: null,
            versionRanges: ranges,
            statuses: Array.Empty<AffectedPackageStatus>(),
            provenance: new[] { new AdvisoryProvenance("mapper", "package", "pkg@1", recordedAt, new[] { ProvenanceFieldMasks.AffectedPackages }) });
    }
}
