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 DeleteTaskHubTests : 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 ParentId, string ChildId)> SeedParentWithChildAsync(TaskStatus childStatus)
    {
        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 repo = new TaskRepository(ctx);
        var parentId = Guid.NewGuid().ToString();
        await repo.AddAsync(new TaskEntity
        {
            Id = parentId, ListId = listId, Title = "Parent",
            Status = TaskStatus.WaitingForChildren, PlanningPhase = PlanningPhase.Finalized,
            CreatedAt = DateTime.UtcNow, CommitType = "feat",
        });

        var childId = Guid.NewGuid().ToString();
        await repo.AddAsync(new TaskEntity
        {
            Id = childId, ListId = listId, Title = "Child", ParentTaskId = parentId,
            Status = childStatus, CreatedAt = DateTime.UtcNow, CommitType = "feat",
        });

        return (parentId, childId);
    }

    [Fact]
    public async Task DeleteTask_LastChildOfWaitingForChildrenParent_AdvancesParentToWaitingForReview()
    {
        var (parentId, childId) = await SeedParentWithChildAsync(TaskStatus.Idle);
        var hub = CreateHub();

        await hub.DeleteTask(childId, default);

        await using var ctx = _db.CreateContext();
        Assert.Null(await ctx.Tasks.FindAsync(childId));
        var parent = await ctx.Tasks.FindAsync(parentId);
        Assert.Equal(TaskStatus.WaitingForReview, parent!.Status);
    }

    [Fact]
    public async Task DeleteTask_RunningTask_Throws_AndDoesNotDelete()
    {
        var (_, childId) = await SeedParentWithChildAsync(TaskStatus.Running);
        var hub = CreateHub();

        await Assert.ThrowsAsync<HubException>(() => hub.DeleteTask(childId, default));

        await using var ctx = _db.CreateContext();
        Assert.NotNull(await ctx.Tasks.FindAsync(childId));
    }

    [Fact]
    public async Task DeleteTask_TaskWithChildren_ThrowsFriendlyForeignKeyMessage()
    {
        var (parentId, _) = await SeedParentWithChildAsync(TaskStatus.Idle);
        var hub = CreateHub();

        var ex = await Assert.ThrowsAsync<HubException>(() => hub.DeleteTask(parentId, default));

        Assert.Contains("child tasks", ex.Message, StringComparison.OrdinalIgnoreCase);
    }

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

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