using ClaudeDo.Worker.Queue;
using Microsoft.Extensions.Logging.Abstractions;

namespace ClaudeDo.Worker.Tests.Queue;

public sealed class RunCancellationRegistryTests
{
    [Fact]
    public void TryCancel_RegisteredTask_CancelsAndReturnsTrue()
    {
        var sut = new RunCancellationRegistry(NullLogger<RunCancellationRegistry>.Instance);
        using var cts = new CancellationTokenSource();
        Assert.True(sut.Register("t1", cts));

        Assert.True(sut.TryCancel("t1"));
        Assert.True(cts.IsCancellationRequested);
    }

    [Fact]
    public void TryCancel_UnknownTask_ReturnsFalse()
    {
        var sut = new RunCancellationRegistry(NullLogger<RunCancellationRegistry>.Instance);
        Assert.False(sut.TryCancel("nope"));
    }

    [Fact]
    public void Register_SecondCallForSameTask_RefusesAndKeepsFirstRegistration()
    {
        // A double-dispatch (e.g. RunNow racing the queue picker for the same task) must not
        // let the second registration silently clobber the first — that would leave CancelAsync
        // pointing at the wrong (or already-finished) CTS.
        var sut = new RunCancellationRegistry(NullLogger<RunCancellationRegistry>.Instance);
        using var first = new CancellationTokenSource();
        using var second = new CancellationTokenSource();

        Assert.True(sut.Register("t1", first));
        Assert.False(sut.Register("t1", second));

        Assert.True(sut.TryCancel("t1"));
        Assert.True(first.IsCancellationRequested);
        Assert.False(second.IsCancellationRequested);
    }

    [Fact]
    public void Unregister_RemovesOnlyTheGivenRegistration()
    {
        var sut = new RunCancellationRegistry(NullLogger<RunCancellationRegistry>.Instance);
        using var first = new CancellationTokenSource();

        sut.Register("t1", first);
        sut.Unregister("t1", first);

        // Slot is free again once the run that held it cleans up properly, so a genuine
        // sequential re-run can register a fresh CTS under the same task id.
        using var second = new CancellationTokenSource();
        Assert.True(sut.Register("t1", second));

        Assert.True(sut.TryCancel("t1"));
        Assert.True(second.IsCancellationRequested);
    }

    [Fact]
    public void TryCancel_DisposedCts_ReturnsFalse()
    {
        var sut = new RunCancellationRegistry(NullLogger<RunCancellationRegistry>.Instance);
        var cts = new CancellationTokenSource();
        sut.Register("t1", cts);
        cts.Dispose();

        Assert.False(sut.TryCancel("t1"));
    }
}
