using ClaudeDo.Data.Models;
using ClaudeDo.Ui.Services;
using ClaudeDo.Ui.ViewModels.Modals;
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
using Xunit;

namespace ClaudeDo.Ui.Tests.ViewModels;

public class WorktreesOverviewBatchMergeTests
{
    private static WorktreeOverviewRowViewModel ActiveRow(string id) => new()
    {
        TaskId = id,
        TaskTitle = $"Task {id}",
        TaskStatus = TaskStatus.WaitingForReview,
        State = WorktreeState.Active,
    };

    [Fact]
    public void Row_outcome_helpers_reflect_state()
    {
        var row = ActiveRow("a");
        Assert.Equal(BatchMergeOutcome.None, row.MergeOutcome);
        Assert.False(row.IsConflict);

        row.MergeOutcome = BatchMergeOutcome.Conflict;
        Assert.True(row.IsConflict);

        row.MergeOutcome = BatchMergeOutcome.Merged;
        Assert.False(row.IsConflict);
    }

    private static WorktreesOverviewModalViewModel NewVm(MergeCoordinator? merge = null) =>
        new(new ClaudeDo.Ui.Services.WorkerClient("http://127.0.0.1:1/hub"), () => null!,
            merge ?? new ClaudeDo.Ui.Services.MergeCoordinator());

    private static MergeResultDto Merged() => new("merged", System.Array.Empty<string>(), null);
    private static MergeResultDto Conflict() => new("conflict", new[] { "f.cs" }, null);
    private static MergeResultDto Blocked() => new("blocked", System.Array.Empty<string>(), "blocked");

    [Fact]
    public async System.Threading.Tasks.Task MergeSelected_only_processes_checked_active_rows()
    {
        var vm = NewVm();
        var a = ActiveRow("a"); a.IsChecked = true;
        var b = ActiveRow("b"); b.IsChecked = false;
        var c = ActiveRow("c"); c.IsChecked = true; c.State = WorktreeState.Merged;
        vm.Rows.Add(a); vm.Rows.Add(b); vm.Rows.Add(c);
        vm.SelectedTarget = "main";

        var seen = new System.Collections.Generic.List<string>();
        await vm.MergeSelectedAsync((id, target, remove, msg) =>
        {
            seen.Add(id);
            Assert.Equal("main", target);
            Assert.False(remove);
            return System.Threading.Tasks.Task.FromResult(Merged());
        });

        Assert.Equal(new[] { "a" }, seen);
        Assert.Equal(BatchMergeOutcome.Merged, a.MergeOutcome);
        Assert.False(a.IsChecked);
    }

    [Fact]
    public async System.Threading.Tasks.Task MergeSelected_continues_past_conflict_and_collects_it()
    {
        var vm = NewVm();
        var a = ActiveRow("a"); a.IsChecked = true;
        var b = ActiveRow("b"); b.IsChecked = true;
        var c = ActiveRow("c"); c.IsChecked = true;
        vm.Rows.Add(a); vm.Rows.Add(b); vm.Rows.Add(c);
        vm.SelectedTarget = "main";

        await vm.MergeSelectedAsync((id, target, remove, msg) =>
            System.Threading.Tasks.Task.FromResult(id == "b" ? Conflict() : Merged()));

        Assert.Equal(BatchMergeOutcome.Merged, a.MergeOutcome);
        Assert.Equal(BatchMergeOutcome.Conflict, b.MergeOutcome);
        Assert.Equal(BatchMergeOutcome.Merged, c.MergeOutcome);
        Assert.Contains(b, vm.ConflictRows);
        Assert.Single(vm.ConflictRows);
    }

    [Fact]
    public async System.Threading.Tasks.Task MergeSelected_maps_blocked_and_exception_to_failure_outcomes()
    {
        var vm = NewVm();
        var a = ActiveRow("a"); a.IsChecked = true;
        var b = ActiveRow("b"); b.IsChecked = true;
        vm.Rows.Add(a); vm.Rows.Add(b);
        vm.SelectedTarget = "main";

        await vm.MergeSelectedAsync((id, target, remove, msg) => id == "a"
            ? System.Threading.Tasks.Task.FromResult(Blocked())
            : throw new System.InvalidOperationException("boom"));

        Assert.Equal(BatchMergeOutcome.Blocked, a.MergeOutcome);
        Assert.Equal(BatchMergeOutcome.Failed, b.MergeOutcome);
        Assert.Empty(vm.ConflictRows);
        Assert.False(vm.BatchMergeStatus.IsRunning);
    }

    [Fact]
    public async System.Threading.Tasks.Task MergeSelected_noop_when_no_target()
    {
        var vm = NewVm();
        var a = ActiveRow("a"); a.IsChecked = true;
        vm.Rows.Add(a);
        vm.SelectedTarget = null;

        var called = false;
        await vm.MergeSelectedAsync((id, t, r, m) => { called = true; return System.Threading.Tasks.Task.FromResult(Merged()); });

        Assert.False(called);
        Assert.Equal(BatchMergeOutcome.None, a.MergeOutcome);
    }

    [Fact]
    public void SelectedCount_tracks_checked_active_rows()
    {
        var vm = NewVm();
        var a = ActiveRow("a");
        var b = ActiveRow("b");
        var merged = ActiveRow("c"); merged.State = WorktreeState.Merged;
        vm.AddRowForTest(a); vm.AddRowForTest(b); vm.AddRowForTest(merged);

        Assert.Equal(0, vm.SelectedCount);
        a.IsChecked = true;
        Assert.Equal(1, vm.SelectedCount);
        b.IsChecked = true;
        merged.IsChecked = true;
        Assert.Equal(2, vm.SelectedCount);
        a.IsChecked = false;
        Assert.Equal(1, vm.SelectedCount);
    }

    [Fact]
    public void ResolveConflict_invokes_seam_with_task_and_target()
    {
        (string Task, string Target)? captured = null;
        var coordinator = new ClaudeDo.Ui.Services.MergeCoordinator
        {
            Handler = (taskId, target) => { captured = (taskId, target); return System.Threading.Tasks.Task.CompletedTask; },
        };
        var vm = NewVm(coordinator);
        vm.SelectedTarget = "release";
        var row = ActiveRow("x"); row.MergeOutcome = BatchMergeOutcome.Conflict;

        vm.ResolveConflictCommand.Execute(row);

        Assert.Equal(("x", "release"), captured);
    }

    [Fact]
    public void MergeAll_canExecute_requires_target_selection_and_idle()
    {
        var vm = NewVm();
        var a = ActiveRow("a");
        vm.AddRowForTest(a);

        Assert.False(vm.MergeAllCommand.CanExecute(null));
        a.IsChecked = true;
        Assert.False(vm.MergeAllCommand.CanExecute(null));
        vm.SelectedTarget = "main";
        Assert.True(vm.MergeAllCommand.CanExecute(null));
        using (vm.BatchMergeStatus.Begin("merging"))
        {
            Assert.False(vm.MergeAllCommand.CanExecute(null));
        }
        Assert.True(vm.MergeAllCommand.CanExecute(null));
    }

    [Fact]
    public void MergeAll_CanExecuteChanged_fires_when_batch_status_changes()
    {
        var vm = NewVm();
        var a = ActiveRow("a"); a.IsChecked = true;
        vm.AddRowForTest(a);
        vm.SelectedTarget = "main";

        var raised = 0;
        vm.MergeAllCommand.CanExecuteChanged += (_, _) => raised++;

        using (vm.BatchMergeStatus.Begin("merging")) { }

        Assert.Equal(2, raised);
    }

    [Fact]
    public void ForceRemove_canExecute_is_false_while_a_removal_is_in_flight()
    {
        var vm = NewVm();
        var row = ActiveRow("a");

        Assert.True(vm.ForceRemoveCommand.CanExecute(row));
        using (vm.ForceRemoveStatus.Begin("removing"))
        {
            Assert.False(vm.ForceRemoveCommand.CanExecute(row));
        }
        Assert.True(vm.ForceRemoveCommand.CanExecute(row));
    }
}
