using ClaudeDo.Data;
using ClaudeDo.Data.Models;
using ClaudeDo.Data.Repositories;
using ClaudeDo.Worker.Refine;
using ClaudeDo.Worker.Runner;
using ClaudeDo.Worker.Tests.Infrastructure;
using Microsoft.Extensions.Logging.Abstractions;
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;

namespace ClaudeDo.Worker.Tests.Refine;

public sealed class RefineRunnerTests : IDisposable
{
    private readonly DbFixture _db = new();
    private readonly ClaudeDoDbContext _ctx;

    public RefineRunnerTests()
    {
        _ctx = _db.CreateContext();
    }

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

    private async Task<string> SeedListAsync()
    {
        var listId = Guid.NewGuid().ToString();
        await new ListRepository(_ctx).AddAsync(new ListEntity
        {
            Id = listId,
            Name = "Test",
            CreatedAt = DateTime.UtcNow,
            WorkingDir = null,
        });
        return listId;
    }

    private async Task<TaskEntity> SeedTaskAsync(string listId, TaskStatus status)
    {
        var task = new TaskEntity
        {
            Id = Guid.NewGuid().ToString(),
            ListId = listId,
            Title = "Test task",
            Status = status,
            CreatedAt = DateTime.UtcNow,
        };
        await new TaskRepository(_ctx).AddAsync(task);
        return task;
    }

    private RefineRunner BuildRunner(RecordingClaudeProcess claude, RecordingRefineBroadcaster broadcaster)
    {
        return new RefineRunner(
            claude,
            _db.CreateFactory(),
            NullLogger<RefineRunner>.Instance,
            broadcaster);
    }

    [Fact]
    public async Task Refuses_when_task_not_idle()
    {
        var listId = await SeedListAsync();
        var task = await SeedTaskAsync(listId, TaskStatus.Queued);

        var claude = new RecordingClaudeProcess(success: true);
        var broadcaster = new RecordingRefineBroadcaster();
        var runner = BuildRunner(claude, broadcaster);

        var outcome = await runner.RefineAsync(task.Id, CancellationToken.None);

        Assert.False(outcome.Success);
        Assert.Equal(0, claude.CallCount);
    }

    [Fact]
    public async Task Idle_task_invokes_claude_once_and_brackets_with_events()
    {
        var listId = await SeedListAsync();
        var task = await SeedTaskAsync(listId, TaskStatus.Idle);

        var claude = new RecordingClaudeProcess(success: true);
        var broadcaster = new RecordingRefineBroadcaster();
        var runner = BuildRunner(claude, broadcaster);

        var outcome = await runner.RefineAsync(task.Id, CancellationToken.None);

        Assert.True(outcome.Success);
        Assert.Equal(1, claude.CallCount);
        Assert.Equal(1, broadcaster.StartedCount);
        Assert.Equal(1, broadcaster.FinishedCount);
    }
}

internal sealed class RecordingClaudeProcess : IClaudeProcess
{
    private readonly bool _success;
    private int _callCount;

    public int CallCount => _callCount;

    public RecordingClaudeProcess(bool success) => _success = success;

    public Task<RunResult> RunAsync(IReadOnlyList<string> arguments, string prompt, string workingDirectory,
        Func<string, Task> onStdoutLine, CancellationToken ct)
    {
        Interlocked.Increment(ref _callCount);
        var result = _success
            ? new RunResult { ExitCode = 0, ResultMarkdown = "ok" }
            : new RunResult { ExitCode = 1, ResultMarkdown = null };
        return Task.FromResult(result);
    }
}

internal sealed class RecordingRefineBroadcaster : IRefineBroadcaster
{
    private int _startedCount;
    private int _finishedCount;

    public int StartedCount => _startedCount;
    public int FinishedCount => _finishedCount;

    public Task RefineStartedAsync(string taskId)
    {
        Interlocked.Increment(ref _startedCount);
        return Task.CompletedTask;
    }

    public Task RefineFinishedAsync(string taskId, bool success, string? error)
    {
        Interlocked.Increment(ref _finishedCount);
        return Task.CompletedTask;
    }
}
