using FrameProcessor.Configuration;
using FrameProcessor.Domain;
using FrameProcessor.Storage;
using Microsoft.Extensions.Options;

namespace FrameProcessor.Tests;

public class ImageStoreTests : IDisposable
{
    private readonly string _directory;

    public ImageStoreTests()
    {
        _directory = Path.Combine(Path.GetTempPath(), "frame-processor-tests", Guid.NewGuid().ToString("N"));
    }

    public void Dispose()
    {
        if (Directory.Exists(_directory))
        {
            Directory.Delete(_directory, recursive: true);
        }
    }

    [Fact]
    public void Constructor_CreatesImageDirectoryIfMissing()
    {
        Assert.False(Directory.Exists(_directory));

        _ = CreateStore();

        Assert.True(Directory.Exists(_directory));
    }

    [Fact]
    public async Task WriteAsync_PersistsBytesAtMacKeyedPath()
    {
        var store = CreateStore();
        var mac = MacAddress.Parse("AA:BB:CC:DD:EE:FF");
        var payload = new byte[] { 0xDE, 0xAD, 0xBE, 0xEF };

        await store.WriteAsync(mac, payload, CancellationToken.None);

        var expectedPath = Path.Combine(_directory, "aabbccddeeff.png");
        Assert.True(File.Exists(expectedPath));
        Assert.Equal(payload, await File.ReadAllBytesAsync(expectedPath));
    }

    [Fact]
    public async Task WriteAsync_RemovesTempFileAfterRename()
    {
        var store = CreateStore();
        var mac = MacAddress.Parse("aabbccddeeff");

        await store.WriteAsync(mac, new byte[] { 0x01 }, CancellationToken.None);

        Assert.False(File.Exists(Path.Combine(_directory, "aabbccddeeff.png.tmp")));
    }

    [Fact]
    public async Task WriteAsync_OverwritesPreviousImage()
    {
        var store = CreateStore();
        var mac = MacAddress.Parse("aabbccddeeff");
        await store.WriteAsync(mac, new byte[] { 0x01, 0x02 }, CancellationToken.None);

        await store.WriteAsync(mac, new byte[] { 0x09, 0x08, 0x07 }, CancellationToken.None);

        var path = Path.Combine(_directory, "aabbccddeeff.png");
        Assert.Equal(new byte[] { 0x09, 0x08, 0x07 }, await File.ReadAllBytesAsync(path));
    }

    [Fact]
    public async Task WriteAsync_OverwritesStaleTempFileFromPriorCrash()
    {
        Directory.CreateDirectory(_directory);
        var stalePath = Path.Combine(_directory, "aabbccddeeff.png.tmp");
        await File.WriteAllBytesAsync(stalePath, new byte[] { 0xFF, 0xFF });

        var store = CreateStore();
        await store.WriteAsync(MacAddress.Parse("aabbccddeeff"), new byte[] { 0x01 }, CancellationToken.None);

        Assert.False(File.Exists(stalePath));
        Assert.Equal(new byte[] { 0x01 }, await File.ReadAllBytesAsync(Path.Combine(_directory, "aabbccddeeff.png")));
    }

    [Fact]
    public void TryGetPath_ReturnsFalseWhenAbsent()
    {
        var store = CreateStore();

        var found = store.TryGetPath(MacAddress.Parse("aabbccddeeff"), out var path);

        Assert.False(found);
        Assert.Null(path);
    }

    [Fact]
    public async Task TryGetPath_ReturnsPathAfterWrite()
    {
        var store = CreateStore();
        var mac = MacAddress.Parse("aabbccddeeff");
        await store.WriteAsync(mac, new byte[] { 0x01 }, CancellationToken.None);

        var found = store.TryGetPath(mac, out var path);

        Assert.True(found);
        Assert.Equal(Path.Combine(_directory, "aabbccddeeff.png"), path);
    }

    private ImageStore CreateStore() =>
        new(Options.Create(new StorageOptions { ImageDirectory = _directory }));
}
