using System.Collections.Generic;
using System.Threading.Tasks;
using ClaudeDo.Ui.Services;
using ClaudeDo.Ui.ViewModels.Modals;
using Xunit;

namespace ClaudeDo.Ui.Tests.ViewModels;

/// <summary>
/// Covers the two things that made a working merge look broken: the prefilled commit message
/// (must be the worker's conventional default, not a hand-rolled "Merge task: …") and the
/// progress feedback while the post-merge verify gate holds the call open for minutes.
/// </summary>
public class MergeModalViewModelTests
{
    private sealed class Worker : StubWorkerClient
    {
        public MergeTargetsDto? Targets { get; set; } =
            new("main", new[] { "main", "dev" }, "fix(my-list): merge do the thing");

        public TaskCompletionSource<MergeResultDto> MergeGate { get; } = new();
        public bool BlockMerge { get; set; }
        public string? CapturedCommitMessage { get; private set; }

        public override Task<MergeTargetsDto?> GetMergeTargetsAsync(string taskId) =>
            Task.FromResult(Targets);

        public override Task<MergeResultDto> MergeTaskAsync(
            string taskId, string targetBranch, bool removeWorktree, string commitMessage)
        {
            CapturedCommitMessage = commitMessage;
            return BlockMerge
                ? MergeGate.Task
                : Task.FromResult(new MergeResultDto("merged", new List<string>(), null));
        }
    }

    private static (MergeModalViewModel vm, Worker worker) Build()
    {
        var worker = new Worker();
        return (new MergeModalViewModel(worker, new MergeCoordinator()), worker);
    }

    [Fact]
    public async Task Initialize_prefills_the_workers_default_commit_message()
    {
        var (vm, _) = Build();

        await vm.InitializeAsync("task-1", "do the thing");

        Assert.Equal("fix(my-list): merge do the thing", vm.CommitMessage);
        Assert.Equal("main", vm.SelectedBranch);
    }

    [Fact]
    public async Task Initialize_keeps_local_fallback_when_worker_sends_no_default()
    {
        var (vm, worker) = Build();
        worker.Targets = new MergeTargetsDto("main", new[] { "main" }, "");

        await vm.InitializeAsync("task-1", "do the thing");

        Assert.False(string.IsNullOrWhiteSpace(vm.CommitMessage));
        Assert.NotEqual("", vm.CommitMessage);
    }

    [Fact]
    public async Task Submit_shows_a_phase_while_the_merge_is_pending_and_clears_it_after()
    {
        var (vm, worker) = Build();
        await vm.InitializeAsync("task-1", "do the thing");
        worker.BlockMerge = true;

        var submit = vm.SubmitCommand.ExecuteAsync(null);

        Assert.True(vm.IsBusy);
        var merging = vm.ProgressMessage;
        Assert.False(string.IsNullOrWhiteSpace(merging));

        // The verify gate starts — a distinct phase, so the user sees why this is taking minutes.
        // (The elapsed time rides in a format argument, which the test localizer drops.)
        worker.RaiseMergeProgress("task-1", "verifying", 90);
        Assert.NotEqual(merging, vm.ProgressMessage);

        worker.MergeGate.SetResult(new MergeResultDto("merged", new List<string>(), null));
        await submit;

        Assert.Null(vm.ProgressMessage);
        Assert.False(vm.IsBusy);
        Assert.True(vm.Merged);
    }

    [Fact]
    public async Task Progress_for_another_task_is_ignored()
    {
        var (vm, worker) = Build();
        await vm.InitializeAsync("task-1", "do the thing");
        worker.BlockMerge = true;

        var submit = vm.SubmitCommand.ExecuteAsync(null);
        var merging = vm.ProgressMessage;

        worker.RaiseMergeProgress("some-other-task", "verifying", 42);

        Assert.Equal(merging, vm.ProgressMessage);

        worker.MergeGate.SetResult(new MergeResultDto("merged", new List<string>(), null));
        await submit;
    }

    [Fact]
    public async Task Submit_unsubscribes_so_a_later_broadcast_cannot_revive_the_progress_line()
    {
        var (vm, worker) = Build();
        await vm.InitializeAsync("task-1", "do the thing");

        await vm.SubmitCommand.ExecuteAsync(null);
        worker.RaiseMergeProgress("task-1", "verifying", 10);

        Assert.Null(vm.ProgressMessage);
    }

    [Fact]
    public async Task Submit_shows_the_rebase_phase_with_branch_count_instead_of_stalled_merging()
    {
        var (vm, worker) = Build();
        await vm.InitializeAsync("task-1", "do the thing");
        worker.BlockMerge = true;

        var submit = vm.SubmitCommand.ExecuteAsync(null);
        var merging = vm.ProgressMessage;

        // Needs the branch total, which only rides OperationProgress (MergeProgressEvent
        // forwards elapsed-seconds only) -- and must not be clobbered by that other forwarder.
        worker.RaiseOperationProgress("task-1", "rebasing", 1, 2);
        Assert.NotEqual(merging, vm.ProgressMessage);
        Assert.Contains("(1/2)", vm.ProgressMessage);

        worker.MergeGate.SetResult(new MergeResultDto("merged", new List<string>(), null));
        await submit;

        Assert.Null(vm.ProgressMessage);
    }
}
