using ClaudeDo.Data;
using ClaudeDo.Data.Models;
using ClaudeDo.Ui.Services;
using ClaudeDo.Ui.ViewModels.Islands;
using Microsoft.EntityFrameworkCore;
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;

namespace ClaudeDo.Ui.Tests.ViewModels;

// A status change on the task bound in the detail pane can come from outside the UI (worker run,
// MCP tool, another session) and must refresh the pane's Task without waiting for a list reload.
public class DetailsIslandTaskUpdatedTests : IDisposable
{
    private readonly string _dbPath;

    public DetailsIslandTaskUpdatedTests()
    {
        _dbPath = Path.Combine(Path.GetTempPath(), $"claudedo_details_taskupdated_{Guid.NewGuid():N}.db");
        using var ctx = NewContext();
        ctx.Database.EnsureCreated();
    }

    public void Dispose()
    {
        try { File.Delete(_dbPath); } catch { }
        try { File.Delete(_dbPath + "-wal"); } catch { }
        try { File.Delete(_dbPath + "-shm"); } catch { }
    }

    private ClaudeDoDbContext NewContext()
    {
        var opts = new DbContextOptionsBuilder<ClaudeDoDbContext>()
            .UseSqlite($"Data Source={_dbPath}")
            .Options;
        return new ClaudeDoDbContext(opts);
    }




    private sealed class FakeWorker : StubWorkerClient
    {
        public override bool IsConnected => true;
    }

    private async Task SeedTaskAsync(string id, TaskStatus status)
    {
        await using var db = NewContext();
        if (!await db.Lists.AnyAsync(l => l.Id == "L1"))
            db.Lists.Add(new ListEntity { Id = "L1", Name = "Work", CreatedAt = DateTime.UtcNow });
        db.Tasks.Add(new TaskEntity
        {
            Number = TestTaskNumbers.Next(),
            Id = id, ListId = "L1", Title = "Task", Status = status, CreatedAt = DateTime.UtcNow,
        });
        await db.SaveChangesAsync();
    }

    private DetailsIslandViewModel BuildVm(StubWorkerClient worker) =>
        new(new TestDbFactory(NewContext), worker, new NullServiceProvider(),
            new ClaudeDo.Ui.Services.MergeCoordinator());

    [Fact]
    public async Task TaskUpdated_ForBoundTask_RefreshesStatus()
    {
        await SeedTaskAsync("t-bound-1", TaskStatus.Running);

        var worker = new FakeWorker();
        var vm = BuildVm(worker);
        vm.Bind(new TaskRowViewModel { Id = "t-bound-1", Status = TaskStatus.Running });
        await Task.Delay(50);

        Assert.Equal(TaskStatus.Running, vm.Task?.Status);

        await using (var db = NewContext())
        {
            (await db.Tasks.FirstAsync(t => t.Id == "t-bound-1")).Status = TaskStatus.WaitingForReview;
            await db.SaveChangesAsync();
        }
        worker.RaiseTaskUpdated("t-bound-1");

        var deadline = DateTime.UtcNow.AddSeconds(3);
        while (DateTime.UtcNow < deadline && vm.Task?.Status != TaskStatus.WaitingForReview)
            await Task.Delay(25);

        Assert.Equal(TaskStatus.WaitingForReview, vm.Task?.Status);
    }

    [Fact]
    public async Task TaskUpdated_ForADifferentTask_DoesNotTouchBoundTask()
    {
        await SeedTaskAsync("t-bound-2", TaskStatus.Running);
        await SeedTaskAsync("t-other-2", TaskStatus.Idle);

        var worker = new FakeWorker();
        var vm = BuildVm(worker);
        vm.Bind(new TaskRowViewModel { Id = "t-bound-2", Status = TaskStatus.Running });

        await using (var db = NewContext())
        {
            (await db.Tasks.FirstAsync(t => t.Id == "t-other-2")).Status = TaskStatus.Done;
            await db.SaveChangesAsync();
        }
        worker.RaiseTaskUpdated("t-other-2");
        await Task.Delay(150);

        Assert.Equal(TaskStatus.Running, vm.Task?.Status);
    }
}
