using ClaudeDo.Data.Models;
using ClaudeDo.Data.Repositories;
using ClaudeDo.Worker.Hub;
using ClaudeDo.Worker.Tests.Infrastructure;
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore;
using Xunit;
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;

namespace ClaudeDo.Worker.Tests.Hub;

public sealed class SetTaskStatusHubTests : IDisposable
{
    private readonly DbFixture _db = new();

    public void Dispose() => _db.Dispose();

    private WorkerHub CreateHub()
    {
        var factory = _db.CreateFactory();
        var built = TaskStateServiceBuilder.Build(factory);
        var broadcaster = new HubBroadcaster(new CapturingHubContext());
        var hub = new WorkerHub(
            null!, null!, null!, null!, broadcaster, factory,
            null!, null!, null!, null!, null!, null!, null!, null!, null!, null!,
            built.State,
            null!, null!,
            null!, new ClaudeDo.Worker.Online.OnlineInboxConfig(), new ClaudeDo.Worker.OnlineRefreshTokenStore(new ClaudeDo.Worker.DpapiTokenStore("unused.token")),
            new ClaudeDo.Worker.Runner.PendingQuestionRegistry(), null!);
        hub.Clients = new FakeHubCallerClients(new RecordingClientProxy());
        hub.Context = new FakeHubCallerContext();
        return hub;
    }

    private async Task<string> SeedTaskAsync(TaskStatus status)
    {
        using var ctx = _db.CreateContext();
        var listId = Guid.NewGuid().ToString();
        await new ListRepository(ctx).AddAsync(new ListEntity
        {
            Id = listId, Name = "L", CreatedAt = DateTime.UtcNow,
        });

        var taskId = Guid.NewGuid().ToString();
        await new TaskRepository(ctx).AddAsync(new TaskEntity
        {
            Id = taskId, ListId = listId, Title = "T",
            Status = status, CreatedAt = DateTime.UtcNow, CommitType = "feat",
        });
        return taskId;
    }

    [Fact]
    public async Task SetTaskStatus_RunningTask_Throws_AndLeavesStatusUnchanged()
    {
        var taskId = await SeedTaskAsync(TaskStatus.Running);
        var hub = CreateHub();

        await Assert.ThrowsAsync<HubException>(() => hub.SetTaskStatus(taskId, "Done"));

        await using var ctx = _db.CreateContext();
        var task = await ctx.Tasks.FindAsync(taskId);
        Assert.Equal(TaskStatus.Running, task!.Status);
    }

    [Fact]
    public async Task SetTaskStatus_IdleTask_StillSucceeds()
    {
        var taskId = await SeedTaskAsync(TaskStatus.Idle);
        var hub = CreateHub();

        await hub.SetTaskStatus(taskId, "Cancelled");

        await using var ctx = _db.CreateContext();
        var task = await ctx.Tasks.FindAsync(taskId);
        Assert.Equal(TaskStatus.Cancelled, task!.Status);
    }

    [Fact]
    public async Task SetTaskStatus_Missing_Throws()
    {
        var hub = CreateHub();

        await Assert.ThrowsAsync<HubException>(() => hub.SetTaskStatus("does-not-exist", "Done"));
    }
}
