using ClaudeDo.Data;
using ClaudeDo.Data.Models;
using ClaudeDo.Data.Repositories;
using ClaudeDo.Worker.External;
using ClaudeDo.Worker.Hub;
using ClaudeDo.Worker.Lifecycle;
using ClaudeDo.Worker.Runner;
using ClaudeDo.Worker.State;
using ClaudeDo.Worker.Tests.Infrastructure;
using ClaudeDo.Data.Git;
using ClaudeDo.Worker.Config;
using Microsoft.Extensions.Logging.Abstractions;
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;

namespace ClaudeDo.Worker.Tests.External;

public sealed class LifecycleMcpToolsTests : IDisposable
{
    private readonly DbFixture _db = new();
    private readonly ClaudeDoDbContext _ctx;
    private readonly TaskRepository _tasks;
    private readonly ListRepository _lists;

    public LifecycleMcpToolsTests()
    {
        _ctx = _db.CreateContext();
        _tasks = new TaskRepository(_ctx);
        _lists = new ListRepository(_ctx);
    }

    public void Dispose() { _ctx.Dispose(); _db.Dispose(); }

    private LifecycleMcpTools BuildSut()
    {
        var cfg = new WorkerConfig
        {
            SandboxRoot = Path.Combine(Path.GetTempPath(), $"cd_{Guid.NewGuid():N}"),
            LogRoot = Path.Combine(Path.GetTempPath(), $"cdl_{Guid.NewGuid():N}"),
        };
        var dbFactory = _db.CreateFactory();
        var broadcaster = new HubBroadcaster(new CapturingHubContext());
        var wtManager = new WorktreeManager(new GitService(), dbFactory, cfg, NullLogger<WorktreeManager>.Instance);
        var state = TaskStateServiceBuilder.Build(dbFactory).State;
        var reset = new TaskResetService(dbFactory, wtManager, broadcaster, state, NullLogger<TaskResetService>.Instance);
        return new LifecycleMcpTools(_tasks, reset);
    }

    private async Task<TaskEntity> SeedTaskAsync(TaskStatus status)
    {
        var listId = Guid.NewGuid().ToString();
        await _lists.AddAsync(new ListEntity { Id = listId, Name = "L", CreatedAt = DateTime.UtcNow });
        var task = new TaskEntity
        {
            Id = Guid.NewGuid().ToString(), ListId = listId, Title = "t",
            Status = status, CreatedAt = DateTime.UtcNow, CommitType = "chore",
        };
        await _tasks.AddAsync(task);
        return task;
    }

    [Fact]
    public async Task ResetFailedTask_OnFailed_ResetsToIdle()
    {
        var task = await SeedTaskAsync(TaskStatus.Failed);
        var sut = BuildSut();

        await sut.ResetFailedTask(task.Id, CancellationToken.None);

        var loaded = await _tasks.GetByIdAsync(task.Id);
        Assert.Equal(TaskStatus.Idle, loaded!.Status);
    }

    [Fact]
    public async Task ResetFailedTask_OnNonFailed_Throws()
    {
        var task = await SeedTaskAsync(TaskStatus.Done);
        var sut = BuildSut();

        await Assert.ThrowsAsync<InvalidOperationException>(() =>
            sut.ResetFailedTask(task.Id, CancellationToken.None));
    }

    [Fact]
    public async Task ResetFailedTask_NotFound_Throws()
    {
        var sut = BuildSut();
        await Assert.ThrowsAsync<InvalidOperationException>(() =>
            sut.ResetFailedTask("missing", CancellationToken.None));
    }
}
