using System.Collections.Immutable;
using System.IO.Abstractions.TestingHelpers;
using System.Linq;
using System.Text.Json;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using StellaOps.Vexer.Core;
using StellaOps.Vexer.Export;

namespace StellaOps.Vexer.Export.Tests;

public sealed class OfflineBundleArtifactStoreTests
{
    [Fact]
    public async Task SaveAsync_WritesArtifactAndManifest()
    {
        var fs = new MockFileSystem();
        var options = Options.Create(new OfflineBundleArtifactStoreOptions { RootPath = "/offline" });
        var store = new OfflineBundleArtifactStore(options, NullLogger<OfflineBundleArtifactStore>.Instance, fs);

        var content = new byte[] { 1, 2, 3 };
        var digest = "sha256:" + Convert.ToHexString(System.Security.Cryptography.SHA256.HashData(content)).ToLowerInvariant();
        var artifact = new VexExportArtifact(
            new VexContentAddress("sha256", digest.Split(':')[1]),
            VexExportFormat.Json,
            content,
            ImmutableDictionary<string, string>.Empty);

        var stored = await store.SaveAsync(artifact, CancellationToken.None);

        var artifactPath = fs.Path.Combine(options.Value.RootPath, stored.Location);
        Assert.True(fs.FileExists(artifactPath));

        var manifestPath = fs.Path.Combine(options.Value.RootPath, options.Value.ManifestFileName);
        Assert.True(fs.FileExists(manifestPath));
        await using var manifestStream = fs.File.OpenRead(manifestPath);
        using var document = await JsonDocument.ParseAsync(manifestStream);
        var artifacts = document.RootElement.GetProperty("artifacts");
        Assert.True(artifacts.GetArrayLength() >= 1);
        var first = artifacts.EnumerateArray().First();
        Assert.Equal(digest, first.GetProperty("digest").GetString());
    }

    [Fact]
    public async Task SaveAsync_ThrowsOnDigestMismatch()
    {
        var fs = new MockFileSystem();
        var options = Options.Create(new OfflineBundleArtifactStoreOptions { RootPath = "/offline" });
        var store = new OfflineBundleArtifactStore(options, NullLogger<OfflineBundleArtifactStore>.Instance, fs);

        var artifact = new VexExportArtifact(
            new VexContentAddress("sha256", "deadbeef"),
            VexExportFormat.Json,
            new byte[] { 0x01, 0x02 },
            ImmutableDictionary<string, string>.Empty);

        await Assert.ThrowsAsync<InvalidOperationException>(() => store.SaveAsync(artifact, CancellationToken.None).AsTask());
    }
}
