using ClaudeDo.Data;
using ClaudeDo.Data.Models;
using ClaudeDo.Worker.Hub;
using ClaudeDo.Worker.Lifecycle;
using ClaudeDo.Worker.Tests.Infrastructure;
using Microsoft.Extensions.Logging.Abstractions;
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;

namespace ClaudeDo.Worker.Tests.Lifecycle;

public sealed class AttachmentOrphanRecoveryTests : IDisposable
{
    private readonly DbFixture _db = new();
    private readonly string _attachRoot;

    public AttachmentOrphanRecoveryTests()
    {
        _attachRoot = Path.Combine(Path.GetTempPath(), $"claudedo_orph_{Guid.NewGuid():N}");
        Directory.CreateDirectory(_attachRoot);
    }

    public void Dispose()
    {
        _db.Dispose();
        try { Directory.Delete(_attachRoot, recursive: true); } catch { }
    }

    [Fact]
    public async Task StartAsync_DeletesOrphanDir_KeepsLiveTaskDir()
    {
        // Seed one real task.
        string listId = Guid.NewGuid().ToString();
        string liveTaskId = Guid.NewGuid().ToString();
        using (var ctx = _db.CreateContext())
        {
            ctx.Lists.Add(new ListEntity { Id = listId, Name = "L", CreatedAt = DateTime.UtcNow });
            ctx.Tasks.Add(new TaskEntity
            {
                Id = liveTaskId, ListId = listId, Title = "T",
                Status = TaskStatus.Idle, CreatedAt = DateTime.UtcNow,
            });
            await ctx.SaveChangesAsync();
        }

        // Create two attachment directories: one for the live task, one orphan.
        var store = new AttachmentStore(_attachRoot);
        var liveDir = store.TaskDir(liveTaskId);
        var orphanId = Guid.NewGuid().ToString();
        var orphanDir = store.TaskDir(orphanId);
        Directory.CreateDirectory(liveDir);
        Directory.CreateDirectory(orphanDir);

        var hubContext = new CapturingHubContext();
        var sut = new AttachmentOrphanRecovery(
            _db.CreateFactory(), store,
            new HubBroadcaster(hubContext),
            NullLogger<AttachmentOrphanRecovery>.Instance);

        await sut.StartAsync(CancellationToken.None);

        Assert.True(Directory.Exists(liveDir), "Live task dir should be kept");
        Assert.False(Directory.Exists(orphanDir), "Orphan dir should be deleted");
        Assert.Contains(hubContext.Proxy.Calls, c =>
            c.Method == "OperationProgress" && (string)c.Args[0]! == "startup-recovery");
    }

    [Fact]
    public async Task StartAsync_NoAttachmentRoot_IsNoop()
    {
        // Use a root that does not exist — should complete without throwing.
        var missingRoot = Path.Combine(Path.GetTempPath(), $"claudedo_missing_{Guid.NewGuid():N}");
        var store = new AttachmentStore(missingRoot);

        var hubContext = new CapturingHubContext();
        var sut = new AttachmentOrphanRecovery(
            _db.CreateFactory(), store,
            new HubBroadcaster(hubContext),
            NullLogger<AttachmentOrphanRecovery>.Instance);

        await sut.StartAsync(CancellationToken.None); // must not throw

        Assert.Contains(hubContext.Proxy.Calls, c =>
            c.Method == "OperationProgress" && (string)c.Args[0]! == "startup-recovery");
    }
}
