using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using StellaOps.Feedser.Exporter.TrivyDb;

namespace StellaOps.Feedser.Exporter.TrivyDb.Tests;

public sealed class TrivyDbPackageBuilderTests
{
    [Fact]
    public void BuildsOciManifestWithExpectedMediaTypes()
    {
        var metadata = Encoding.UTF8.GetBytes("{\"generatedAt\":\"2024-07-15T12:00:00Z\"}");
        var archive = Enumerable.Range(0, 256).Select(static b => (byte)b).ToArray();
        var archivePath = Path.GetTempFileName();
        File.WriteAllBytes(archivePath, archive);
        var archiveDigest = ComputeDigest(archive);

        try
        {
            var request = new TrivyDbPackageRequest(
                metadata,
                archivePath,
                archiveDigest,
                archive.LongLength,
                DateTimeOffset.Parse("2024-07-15T12:00:00Z"),
                "2024.07.15");

            var builder = new TrivyDbPackageBuilder();
            var package = builder.BuildPackage(request);

            Assert.Equal(TrivyDbMediaTypes.OciManifest, package.Manifest.MediaType);
            Assert.Equal(TrivyDbMediaTypes.TrivyConfig, package.Manifest.Config.MediaType);
            var layer = Assert.Single(package.Manifest.Layers);
            Assert.Equal(TrivyDbMediaTypes.TrivyLayer, layer.MediaType);

            var configBytes = JsonSerializer.SerializeToUtf8Bytes(package.Config, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
            var expectedConfigDigest = ComputeDigest(configBytes);
            Assert.Equal(expectedConfigDigest, package.Manifest.Config.Digest);

            Assert.Equal(archiveDigest, layer.Digest);
            Assert.True(package.Blobs.ContainsKey(archiveDigest));
            Assert.Equal(archive.LongLength, package.Blobs[archiveDigest].Length);
            Assert.True(package.Blobs.ContainsKey(expectedConfigDigest));
            Assert.Equal(metadata, package.MetadataJson.ToArray());
        }
        finally
        {
            if (File.Exists(archivePath))
            {
                File.Delete(archivePath);
            }
        }
    }

    [Fact]
    public void ThrowsWhenMetadataMissing()
    {
        var builder = new TrivyDbPackageBuilder();
        var archivePath = Path.GetTempFileName();
        var archiveBytes = new byte[] { 1, 2, 3 };
        File.WriteAllBytes(archivePath, archiveBytes);
        var digest = ComputeDigest(archiveBytes);

        try
        {
            Assert.Throws<ArgumentException>(() => builder.BuildPackage(new TrivyDbPackageRequest(
                ReadOnlyMemory<byte>.Empty,
                archivePath,
                digest,
                archiveBytes.LongLength,
                DateTimeOffset.UtcNow,
                "1")));
        }
        finally
        {
            if (File.Exists(archivePath))
            {
                File.Delete(archivePath);
            }
        }
    }

    private static string ComputeDigest(ReadOnlySpan<byte> payload)
    {
        var hash = SHA256.HashData(payload);
        var hex = Convert.ToHexString(hash);
        return "sha256:" + hex.ToLowerInvariant();
    }
}
