using ClaudeDo.Data;
using ClaudeDo.Data.Models;
using ClaudeDo.Data.Repositories;
using ClaudeDo.Worker.External;
using ClaudeDo.Worker.Tests.Infrastructure;
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;

namespace ClaudeDo.Worker.Tests.External;

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

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

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

    private async Task<TaskEntity> SeedTaskAsync()
    {
        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 = TaskStatus.Idle, CreatedAt = DateTime.UtcNow, CommitType = "chore",
        };
        await _tasks.AddAsync(task);
        return task;
    }

    [Fact]
    public async Task ResolveAsync_HashNumber_ResolvesToGuid()
    {
        var task = await SeedTaskAsync();

        var resolved = await TaskIdResolver.ResolveAsync(_tasks, $"#{task.Number}", CancellationToken.None);

        Assert.Equal(task.Id, resolved);
    }

    [Fact]
    public async Task ResolveAsync_BareNumber_ResolvesToGuid()
    {
        var task = await SeedTaskAsync();

        var resolved = await TaskIdResolver.ResolveAsync(_tasks, task.Number.ToString(), CancellationToken.None);

        Assert.Equal(task.Id, resolved);
    }

    [Fact]
    public async Task ResolveAsync_Guid_PassesThroughUnchanged()
    {
        var task = await SeedTaskAsync();

        var resolved = await TaskIdResolver.ResolveAsync(_tasks, task.Id, CancellationToken.None);

        Assert.Equal(task.Id, resolved);
    }

    [Fact]
    public async Task ResolveAsync_UnknownNumber_ThrowsWithNumberInMessage()
    {
        var ex = await Assert.ThrowsAsync<InvalidOperationException>(() =>
            TaskIdResolver.ResolveAsync(_tasks, "#999999", CancellationToken.None));

        Assert.Contains("999999", ex.Message);
    }

    [Fact]
    public async Task ResolveOptionalAsync_EmptyString_PassesThroughUnchanged()
    {
        var resolved = await TaskIdResolver.ResolveOptionalAsync(_tasks, "", CancellationToken.None);

        Assert.Equal("", resolved);
    }

    [Fact]
    public async Task ResolveOptionalAsync_Null_ReturnsNull()
    {
        var resolved = await TaskIdResolver.ResolveOptionalAsync(_tasks, null, CancellationToken.None);

        Assert.Null(resolved);
    }

    [Fact]
    public async Task ResolveManyAsync_MixedArray_ResolvesBoth()
    {
        var a = await SeedTaskAsync();
        var b = await SeedTaskAsync();

        var resolved = await TaskIdResolver.ResolveManyAsync(_tasks, [$"#{a.Number}", b.Id], CancellationToken.None);

        Assert.Equal(new[] { a.Id, b.Id }, resolved);
    }
}
