using System.Collections.Immutable;
using System.Linq;
using System.Text;
using FluentAssertions;
using Microsoft.Extensions.Logging.Abstractions;
using StellaOps.Excititor.Core;
using StellaOps.Excititor.Formats.CycloneDX;

namespace StellaOps.Excititor.Formats.CycloneDX.Tests;

public sealed class CycloneDxNormalizerTests
{
    [Fact]
    public async Task NormalizeAsync_MapsAnalysisStateAndJustification()
    {
        var json = """
        {
          "bomFormat": "CycloneDX",
          "specVersion": "1.4",
          "serialNumber": "urn:uuid:1234",
          "version": "7",
          "metadata": {
            "timestamp": "2025-10-15T12:00:00Z"
          },
          "components": [
            {
              "bom-ref": "pkg:npm/acme/lib@1.0.0",
              "name": "acme-lib",
              "version": "1.0.0",
              "purl": "pkg:npm/acme/lib@1.0.0"
            }
          ],
          "vulnerabilities": [
            {
              "id": "CVE-2025-1000",
              "detail": "Library issue",
              "analysis": {
                "state": "not_affected",
                "justification": "code_not_present",
                "response": [ "can_not_fix", "will_not_fix" ]
              },
              "affects": [
                { "ref": "pkg:npm/acme/lib@1.0.0" }
              ]
            },
            {
              "id": "CVE-2025-1001",
              "description": "Investigating impact",
              "analysis": {
                "state": "in_triage"
              },
              "affects": [
                { "ref": "pkg:npm/missing/component@2.0.0" }
              ]
            }
          ]
        }
        """;

        var rawDocument = new VexRawDocument(
            "excititor:cyclonedx",
            VexDocumentFormat.CycloneDx,
            new Uri("https://example.org/vex.json"),
            new DateTimeOffset(2025, 10, 16, 0, 0, 0, TimeSpan.Zero),
            "sha256:dummydigest",
            Encoding.UTF8.GetBytes(json),
            ImmutableDictionary<string, string>.Empty);

        var provider = new VexProvider("excititor:cyclonedx", "CycloneDX Provider", VexProviderKind.Vendor);
        var normalizer = new CycloneDxNormalizer(NullLogger<CycloneDxNormalizer>.Instance);

        var batch = await normalizer.NormalizeAsync(rawDocument, provider, CancellationToken.None);

        batch.Claims.Should().HaveCount(2);

        var notAffected = batch.Claims.Single(c => c.VulnerabilityId == "CVE-2025-1000");
        notAffected.Status.Should().Be(VexClaimStatus.NotAffected);
        notAffected.Justification.Should().Be(VexJustification.CodeNotPresent);
        notAffected.Product.Key.Should().Be("pkg:npm/acme/lib@1.0.0");
        notAffected.Product.Purl.Should().Be("pkg:npm/acme/lib@1.0.0");
        notAffected.Document.Revision.Should().Be("7");
        notAffected.AdditionalMetadata["cyclonedx.specVersion"].Should().Be("1.4");
        notAffected.AdditionalMetadata["cyclonedx.analysis.state"].Should().Be("not_affected");
        notAffected.AdditionalMetadata["cyclonedx.analysis.response"].Should().Be("can_not_fix,will_not_fix");

        var investigating = batch.Claims.Single(c => c.VulnerabilityId == "CVE-2025-1001");
        investigating.Status.Should().Be(VexClaimStatus.UnderInvestigation);
        investigating.Justification.Should().BeNull();
        investigating.Product.Key.Should().Be("pkg:npm/missing/component@2.0.0");
        investigating.Product.Name.Should().Be("pkg:npm/missing/component@2.0.0");
        investigating.AdditionalMetadata.Should().ContainKey("cyclonedx.specVersion");
    }
}
