// Go: TestFileStoreEncryption server/filestore_test.go
// Reference: golang/nats-server/server/filestore.go:816-907 (genEncryptionKeys, recoverAEK, setupAEK)
// Tests that block files are encrypted at rest and can be recovered with the same key.

using System.Security.Cryptography;
using NATS.Server.JetStream.Storage;

namespace NATS.Server.Tests;

public class FileStoreEncryptionTests
{
    [Fact]
    public async Task Encrypted_block_round_trips_message()
    {
        // Go: TestFileStoreEncryption server/filestore_test.go
        var dir = Directory.CreateTempSubdirectory();
        var key = new byte[32];
        RandomNumberGenerator.Fill(key);

        await using (var store = new FileStore(new FileStoreOptions
        {
            Directory = dir.FullName,
            Cipher = StoreCipher.ChaCha,
            EncryptionKey = key,
        }))
        {
            await store.AppendAsync("test.subj", "hello encrypted"u8.ToArray(), default);
        }

        // Raw block file should NOT contain plaintext
        var blkFiles = Directory.GetFiles(dir.FullName, "*.blk");
        blkFiles.ShouldNotBeEmpty();
        var raw = File.ReadAllBytes(blkFiles[0]);
        System.Text.Encoding.UTF8.GetString(raw).ShouldNotContain("hello encrypted");

        // Recover with same key should return plaintext
        await using var recovered = new FileStore(new FileStoreOptions
        {
            Directory = dir.FullName,
            Cipher = StoreCipher.ChaCha,
            EncryptionKey = key,
        });
        var msg = await recovered.LoadAsync(1, default);
        msg.ShouldNotBeNull();
        System.Text.Encoding.UTF8.GetString(msg.Payload.Span).ShouldBe("hello encrypted");
    }

    [Fact]
    public async Task Encrypted_block_with_aes_round_trips()
    {
        var dir = Directory.CreateTempSubdirectory();
        var key = new byte[32];
        RandomNumberGenerator.Fill(key);

        await using (var store = new FileStore(new FileStoreOptions
        {
            Directory = dir.FullName,
            Cipher = StoreCipher.Aes,
            EncryptionKey = key,
        }))
        {
            await store.AppendAsync("aes.subj", "aes payload"u8.ToArray(), default);
        }

        await using var recovered = new FileStore(new FileStoreOptions
        {
            Directory = dir.FullName,
            Cipher = StoreCipher.Aes,
            EncryptionKey = key,
        });
        var msg = await recovered.LoadAsync(1, default);
        msg.ShouldNotBeNull();
        System.Text.Encoding.UTF8.GetString(msg.Payload.Span).ShouldBe("aes payload");
    }

    [Fact]
    public async Task Wrong_key_fails_to_decrypt()
    {
        var dir = Directory.CreateTempSubdirectory();
        var key1 = new byte[32];
        var key2 = new byte[32];
        RandomNumberGenerator.Fill(key1);
        RandomNumberGenerator.Fill(key2);

        await using (var store = new FileStore(new FileStoreOptions
        {
            Directory = dir.FullName,
            Cipher = StoreCipher.ChaCha,
            EncryptionKey = key1,
        }))
        {
            await store.AppendAsync("secret", "data"u8.ToArray(), default);
        }

        // Recovery with wrong key should throw InvalidDataException (from CryptographicException)
        var act = () => new FileStore(new FileStoreOptions
        {
            Directory = dir.FullName,
            Cipher = StoreCipher.ChaCha,
            EncryptionKey = key2,
        });
        Should.Throw<InvalidDataException>(act);
    }
}
