using LibHac.Fs;
using LibHac.FsSystem;
using LibHac.Mem;
using Xunit;

using CacheHandle = System.UInt64;

namespace LibHac.Tests.FsSystem;

public class FileSystemBufferManagerTests
{
    private FileSystemBufferManager CreateManager(int size, int blockSize = 0x4000, int maxCacheCount = 16)
    {
        int orderMax = FileSystemBuddyHeap.QueryOrderMax((nuint)size, (nuint)blockSize);
        nuint workBufferSize = FileSystemBuddyHeap.QueryWorkBufferSize(orderMax);
        byte[] workBuffer = new byte[workBufferSize];
        byte[] heapBuffer = new byte[size];

        var bufferManager = new FileSystemBufferManager();
        Assert.Success(bufferManager.Initialize(maxCacheCount, heapBuffer, blockSize, workBuffer));
        return bufferManager;
    }

    [Fact]
    public void AllocateBuffer_NoFreeSpace_ReturnsNull()
    {
        FileSystemBufferManager manager = CreateManager(0x20000);
        Buffer buffer1 = manager.AllocateBuffer(0x10000);
        Buffer buffer2 = manager.AllocateBuffer(0x10000);
        Buffer buffer3 = manager.AllocateBuffer(0x4000);

        Assert.True(!buffer1.IsNull);
        Assert.True(!buffer2.IsNull);
        Assert.True(buffer3.IsNull);
    }

    [Fact]
    public void AcquireCache_EntryNotEvicted_ReturnsEntry()
    {
        FileSystemBufferManager manager = CreateManager(0x20000);
        Buffer buffer1 = manager.AllocateBuffer(0x10000);

        CacheHandle handle = manager.RegisterCache(buffer1, new IBufferManager.BufferAttribute());

        manager.AllocateBuffer(0x10000);
        Buffer buffer3 = manager.AcquireCache(handle);

        Assert.Equal(buffer1, buffer3);
    }

    [Fact]
    public void AcquireCache_EntryEvicted_ReturnsNull()
    {
        FileSystemBufferManager manager = CreateManager(0x20000);
        Buffer buffer1 = manager.AllocateBuffer(0x10000);

        CacheHandle handle = manager.RegisterCache(buffer1, new IBufferManager.BufferAttribute());

        manager.AllocateBuffer(0x20000);
        Buffer buffer3 = manager.AcquireCache(handle);

        Assert.True(buffer3.IsNull);
    }

    [Fact]
    public void AcquireCache_MultipleEntriesEvicted_OldestAreEvicted()
    {
        FileSystemBufferManager manager = CreateManager(0x20000);
        Buffer buffer1 = manager.AllocateBuffer(0x8000);
        Buffer buffer2 = manager.AllocateBuffer(0x8000);
        Buffer buffer3 = manager.AllocateBuffer(0x8000);
        Buffer buffer4 = manager.AllocateBuffer(0x8000);

        CacheHandle handle1 = manager.RegisterCache(buffer1, new IBufferManager.BufferAttribute());
        CacheHandle handle2 = manager.RegisterCache(buffer2, new IBufferManager.BufferAttribute());
        CacheHandle handle3 = manager.RegisterCache(buffer3, new IBufferManager.BufferAttribute());
        CacheHandle handle4 = manager.RegisterCache(buffer4, new IBufferManager.BufferAttribute());

        manager.AllocateBuffer(0x10000);

        Buffer buffer1B = manager.AcquireCache(handle1);
        Buffer buffer2B = manager.AcquireCache(handle2);
        Buffer buffer3B = manager.AcquireCache(handle3);
        Buffer buffer4B = manager.AcquireCache(handle4);

        Assert.True(buffer1B.IsNull);
        Assert.True(buffer2B.IsNull);
        Assert.Equal(buffer3, buffer3B);
        Assert.Equal(buffer4, buffer4B);
    }
}