using System.Collections.Generic;
using System.Globalization;
using System.IO;
using StellaOps.Feedser.Exporter.Json;
using StellaOps.Feedser.Models;

namespace StellaOps.Feedser.Exporter.Json.Tests;

public sealed class VulnListJsonExportPathResolverTests
{
    private static readonly DateTimeOffset DefaultPublished = DateTimeOffset.Parse("2024-01-01T00:00:00Z", CultureInfo.InvariantCulture);

    [Fact]
    public void ResolvesCvePath()
    {
        var advisory = CreateAdvisory("CVE-2024-1234");
        var resolver = new VulnListJsonExportPathResolver();

        var path = resolver.GetRelativePath(advisory);

        Assert.Equal(Path.Combine("nvd", "2024", "CVE-2024-1234.json"), path);
    }

    [Fact]
    public void ResolvesGhsaWithPackage()
    {
        var package = new AffectedPackage(
            AffectedPackageTypes.SemVer,
            "pkg:go/github.com/acme/widget@1.0.0",
            platform: null,
            versionRanges: Array.Empty<AffectedVersionRange>(),
            statuses: Array.Empty<AffectedPackageStatus>(),
            provenance: Array.Empty<AdvisoryProvenance>());

        var advisory = CreateAdvisory(
            "GHSA-aaaa-bbbb-cccc",
            aliases: new[] { "CVE-2024-2000" },
            packages: new[] { package });
        var resolver = new VulnListJsonExportPathResolver();

        var path = resolver.GetRelativePath(advisory);

        Assert.Equal(Path.Combine("ghsa", "go", "github.com%2Facme%2Fwidget", "GHSA-AAAA-BBBB-CCCC.json"), path);
    }

    [Fact]
    public void ResolvesUbuntuUsn()
    {
        var advisory = CreateAdvisory("USN-6620-1");
        var resolver = new VulnListJsonExportPathResolver();
        var path = resolver.GetRelativePath(advisory);

        Assert.Equal(Path.Combine("ubuntu", "USN-6620-1.json"), path);
    }

    [Fact]
    public void ResolvesDebianDla()
    {
        var advisory = CreateAdvisory("DLA-1234-1");
        var resolver = new VulnListJsonExportPathResolver();
        var path = resolver.GetRelativePath(advisory);

        Assert.Equal(Path.Combine("debian", "DLA-1234-1.json"), path);
    }

    [Fact]
    public void ResolvesRedHatRhsa()
    {
        var advisory = CreateAdvisory("RHSA-2024:0252");
        var resolver = new VulnListJsonExportPathResolver();
        var path = resolver.GetRelativePath(advisory);

        Assert.Equal(Path.Combine("redhat", "oval", "RHSA-2024_0252.json"), path);
    }

    [Fact]
    public void ResolvesAmazonAlas()
    {
        var advisory = CreateAdvisory("ALAS2-2024-1234");
        var resolver = new VulnListJsonExportPathResolver();
        var path = resolver.GetRelativePath(advisory);

        Assert.Equal(Path.Combine("amazon", "2", "ALAS2-2024-1234.json"), path);
    }

    [Fact]
    public void ResolvesOracleElsa()
    {
        var advisory = CreateAdvisory("ELSA-2024-12345");
        var resolver = new VulnListJsonExportPathResolver();
        var path = resolver.GetRelativePath(advisory);

        Assert.Equal(Path.Combine("oracle", "linux", "ELSA-2024-12345.json"), path);
    }

    [Fact]
    public void ResolvesRockyRlsa()
    {
        var advisory = CreateAdvisory("RLSA-2024:0417");
        var resolver = new VulnListJsonExportPathResolver();
        var path = resolver.GetRelativePath(advisory);

        Assert.Equal(Path.Combine("rocky", "RLSA-2024_0417.json"), path);
    }

    [Fact]
    public void ResolvesByProvenanceFallback()
    {
        var provenance = new[] { new AdvisoryProvenance("wolfi", "map", "", DefaultPublished) };
        var advisory = CreateAdvisory("WOLFI-2024-0001", provenance: provenance);
        var resolver = new VulnListJsonExportPathResolver();
        var path = resolver.GetRelativePath(advisory);

        Assert.Equal(Path.Combine("wolfi", "WOLFI-2024-0001.json"), path);
    }

    [Fact]
    public void ResolvesAcscByProvenance()
    {
        var provenance = new[] { new AdvisoryProvenance("acsc", "mapping", "acsc-2025-010", DefaultPublished) };
        var advisory = CreateAdvisory("acsc-2025-010", provenance: provenance);
        var resolver = new VulnListJsonExportPathResolver();
        var path = resolver.GetRelativePath(advisory);

        Assert.Equal(Path.Combine("cert", "au", "acsc-2025-010.json"), path);
    }

    [Fact]
    public void DefaultsToMiscWhenUnmapped()
    {
        var advisory = CreateAdvisory("CUSTOM-2024-99");
        var resolver = new VulnListJsonExportPathResolver();
        var path = resolver.GetRelativePath(advisory);

        Assert.Equal(Path.Combine("misc", "CUSTOM-2024-99.json"), path);
    }

    private static Advisory CreateAdvisory(
        string advisoryKey,
        IEnumerable<string>? aliases = null,
        IEnumerable<AffectedPackage>? packages = null,
        IEnumerable<AdvisoryProvenance>? provenance = null)
    {
        return new Advisory(
            advisoryKey: advisoryKey,
            title: $"Advisory {advisoryKey}",
            summary: null,
            language: "en",
            published: DefaultPublished,
            modified: DefaultPublished,
            severity: "medium",
            exploitKnown: false,
            aliases: aliases ?? Array.Empty<string>(),
            references: Array.Empty<AdvisoryReference>(),
            affectedPackages: packages ?? Array.Empty<AffectedPackage>(),
            cvssMetrics: Array.Empty<CvssMetric>(),
            provenance: provenance ?? Array.Empty<AdvisoryProvenance>());
    }
}
