using Tiku.Worker;

namespace Tiku.UnitTests.Worker;

public sealed class PeriodicWorkerExecutionModeTests
{
    [Fact]
    public async Task Multi_instance_mode_does_not_request_global_lock()
    {
        var processorLock = new RecordingProcessorLock(null);

        await using var lease = await PeriodicWorker.TryAcquireExecutionLeaseAsync(
            PeriodicWorkerExecutionMode.MultiInstance,
            processorLock,
            "background-jobs",
            CancellationToken.None);

        Assert.NotNull(lease);
        Assert.Equal(0, processorLock.Attempts);
    }

    [Fact]
    public async Task Singleton_mode_skips_iteration_when_global_lock_is_unavailable()
    {
        var processorLock = new RecordingProcessorLock(null);

        var lease = await PeriodicWorker.TryAcquireExecutionLeaseAsync(
            PeriodicWorkerExecutionMode.Singleton,
            processorLock,
            "commercial-billing",
            CancellationToken.None);

        Assert.Null(lease);
        Assert.Equal(1, processorLock.Attempts);
    }

    [Fact]
    public async Task Singleton_mode_returns_acquired_global_lock_lease()
    {
        var expected = new RecordingLease();
        var processorLock = new RecordingProcessorLock(expected);

        var lease = await PeriodicWorker.TryAcquireExecutionLeaseAsync(
            PeriodicWorkerExecutionMode.Singleton,
            processorLock,
            "platform-approvals",
            CancellationToken.None);

        Assert.Same(expected, lease);
        Assert.Equal(1, processorLock.Attempts);
        await lease!.DisposeAsync();
        Assert.True(expected.Disposed);
    }

    private sealed class RecordingProcessorLock(IAsyncDisposable? lease) : IPeriodicProcessorLock
    {
        public int Attempts { get; private set; }

        public Task<IAsyncDisposable?> TryAcquireAsync(string processor, CancellationToken cancellationToken)
        {
            Attempts++;
            return Task.FromResult(lease);
        }
    }

    private sealed class RecordingLease : IAsyncDisposable
    {
        public bool Disposed { get; private set; }

        public ValueTask DisposeAsync()
        {
            Disposed = true;
            return ValueTask.CompletedTask;
        }
    }
}
