using ClaudeDo.Data;
using ClaudeDo.Ui.ViewModels.Islands;
using Microsoft.EntityFrameworkCore;
using Xunit;

namespace ClaudeDo.Ui.Tests.ViewModels;

public class TaskMonitorViewModelTests : IDisposable
{
    private readonly string _dbPath;

    public TaskMonitorViewModelTests()
    {
        _dbPath = Path.Combine(Path.GetTempPath(), $"claudedo_monitor_{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 { }

    private TaskMonitorViewModel Build(FakeWorker worker)
        => new TaskMonitorViewModel(new TestDbFactory(NewContext), worker);

    [Fact]
    public void Feeds_AccumulateLogLines_WithKinds()
    {
        var worker = new FakeWorker();
        using var vm = Build(worker);
        vm.SetTaskId("t1");

        worker.RaiseTaskMessage("t1", "[sys] boot");
        worker.RaiseTaskMessage("t1", "[tool] read file");
        worker.RaiseTaskMessage("t1", "[claude] hello");

        Assert.Equal(3, vm.Log.Count);
        Assert.Equal(LogKind.Sys, vm.Log[0].Kind);
        Assert.Equal(LogKind.Tool, vm.Log[1].Kind);
        Assert.Equal(LogKind.Claude, vm.Log[2].Kind);
    }

    [Fact]
    public void Feeds_ForOtherTask_AreIgnored()
    {
        var worker = new FakeWorker();
        using var vm = Build(worker);
        vm.SetTaskId("t1");

        worker.RaiseTaskMessage("other", "[sys] not mine");

        Assert.Empty(vm.Log);
    }

    [Fact]
    public void TaskFinished_FlipsState_AndAppendsDoneLine()
    {
        var worker = new FakeWorker();
        using var vm = Build(worker);
        vm.SetTaskId("t1");

        worker.RaiseTaskFinished("slot-1", "t1", "done", DateTime.UtcNow);

        Assert.True(vm.IsDone);
        Assert.Equal("done", vm.AgentState);
        Assert.Equal(LogKind.Done, vm.Log[^1].Kind);
    }

    [Fact]
    public void ApplyOutcome_SplitsRoadblockMarker()
    {
        var worker = new FakeWorker();
        using var vm = Build(worker);

        vm.ApplyOutcome(
            "Summary text\n\nRoadblocks reported during the run:\n- something broke",
            errorFallback: null);

        Assert.Equal("Summary text", vm.SessionOutcome);
        Assert.Equal("- something broke", vm.Roadblocks);
    }

    [Fact]
    public void HasRoadblock_TrueAfterRoadblockOutcome()
    {
        var worker = new FakeWorker();
        using var vm = Build(worker);
        vm.ApplyOutcome("Summary\n\nRoadblocks reported during the run:\n- broke", errorFallback: null);
        Assert.True(vm.HasRoadblock);
    }

    [Fact]
    public void TaskQuestionAsked_SurfacesQuestion()
    {
        var worker = new FakeWorker();
        using var vm = Build(worker);
        vm.SetTaskId("t1");

        worker.RaiseTaskQuestionAsked("t1", "q1", "DPAPI or plaintext?");

        Assert.True(vm.HasPendingQuestion);
        Assert.Equal("DPAPI or plaintext?", vm.PendingQuestion);
        Assert.True(vm.SubmitAnswerCommand.CanExecute(null) is false); // no draft yet
    }

    [Fact]
    public void TaskQuestionAsked_ForOtherTask_IsIgnored()
    {
        var worker = new FakeWorker();
        using var vm = Build(worker);
        vm.SetTaskId("t1");

        worker.RaiseTaskQuestionAsked("other", "q1", "not mine");

        Assert.False(vm.HasPendingQuestion);
    }

    [Fact]
    public async Task SubmitAnswer_InvokesClient_AndClears()
    {
        var worker = new FakeWorker();
        using var vm = Build(worker);
        vm.SetTaskId("t1");
        worker.RaiseTaskQuestionAsked("t1", "q1", "DPAPI or plaintext?");
        vm.AnswerDraft = "DPAPI please";

        Assert.True(vm.SubmitAnswerCommand.CanExecute(null));
        await vm.SubmitAnswerCommand.ExecuteAsync(null);

        Assert.Equal(("t1", "q1", "DPAPI please"), worker.LastAnswer);
        Assert.False(vm.HasPendingQuestion);
        Assert.Equal(string.Empty, vm.AnswerDraft);
    }

    [Fact]
    public void TaskQuestionResolved_ClearsMatchingQuestion()
    {
        var worker = new FakeWorker();
        using var vm = Build(worker);
        vm.SetTaskId("t1");
        worker.RaiseTaskQuestionAsked("t1", "q1", "?");

        worker.RaiseTaskQuestionResolved("t1", "q1");

        Assert.False(vm.HasPendingQuestion);
    }

    [Fact]
    public void TaskFinished_ClearsPendingQuestion()
    {
        var worker = new FakeWorker();
        using var vm = Build(worker);
        vm.SetTaskId("t1");
        worker.RaiseTaskQuestionAsked("t1", "q1", "?");

        worker.RaiseTaskFinished("slot-1", "t1", "done", DateTime.UtcNow);

        Assert.False(vm.HasPendingQuestion);
    }
}
