using System.ComponentModel;
using ClaudeDo.Data.Models;
using ClaudeDo.Ui.Services;
using ClaudeDo.Ui.ViewModels.Planning;

namespace ClaudeDo.Ui.Tests.ViewModels;

public class ConflictResolutionViewModelTests
{
    // ------------------------------------------------------------------ fake
    private sealed class FakeWorker : IWorkerClient
    {
        public bool IsConnected => false;

        public string? ContinueCalledWith { get; private set; }
        public string? AbortCalledWith { get; private set; }
        public Exception? ContinueThrows { get; set; }
        public Exception? AbortThrows { get; set; }

        public event PropertyChangedEventHandler? PropertyChanged;
        public event Action<string, string, DateTime>? TaskStartedEvent;
        public event Action<string, string, string, DateTime>? TaskFinishedEvent;
        public event Action<string>? TaskUpdatedEvent;
        public event Action? ConnectionRestoredEvent;
        public event Action<string>? WorktreeUpdatedEvent;
        public event Action<string>? ListUpdatedEvent;
        public event Action<string, string>? TaskMessageEvent;
        public event Action<string, string>? PlanningMergeStartedEvent;
        public event Action<string, string>? PlanningSubtaskMergedEvent;
        public event Action<string, string, IReadOnlyList<string>>? PlanningMergeConflictEvent;
        public event Action<string>? PlanningMergeAbortedEvent;
        public event Action<string>? PlanningCompletedEvent;

        public Task WakeQueueAsync() => Task.CompletedTask;
        public Task RunNowAsync(string taskId) => Task.CompletedTask;
        public Task ContinueTaskAsync(string taskId, string followUpPrompt) => Task.CompletedTask;
        public Task ResetTaskAsync(string taskId) => Task.CompletedTask;
        public Task CancelTaskAsync(string taskId) => Task.CompletedTask;
        public Task<List<AgentInfo>> GetAgentsAsync() => Task.FromResult(new List<AgentInfo>());
        public Task<ListConfigDto?> GetListConfigAsync(string listId) => Task.FromResult<ListConfigDto?>(null);
        public Task UpdateTaskAgentSettingsAsync(UpdateTaskAgentSettingsDto dto) => Task.CompletedTask;
        public Task SetTaskStatusAsync(string taskId, ClaudeDo.Data.Models.TaskStatus status) => Task.CompletedTask;
        public Task SetTaskTagsAsync(string taskId, IEnumerable<string> tagNames) => Task.CompletedTask;
        public Task<List<string>> GetAllTagsAsync() => Task.FromResult(new List<string>());
        public Task StartPlanningSessionAsync(string taskId, CancellationToken ct = default) => Task.CompletedTask;
        public Task ResumePlanningSessionAsync(string taskId, CancellationToken ct = default) => Task.CompletedTask;
        public Task<ClaudeDo.Data.Repositories.DiscardPlanningOutcome> DiscardPlanningSessionAsync(string taskId, bool dequeueQueuedChildren = false, CancellationToken ct = default)
            => Task.FromResult(new ClaudeDo.Data.Repositories.DiscardPlanningOutcome(ClaudeDo.Data.Repositories.DiscardPlanningResult.Discarded, 0, 0));
        public Task FinalizePlanningSessionAsync(string taskId, bool queueAgentTasks = true, CancellationToken ct = default) => Task.CompletedTask;
        public Task<int> GetPendingDraftCountAsync(string taskId, CancellationToken ct = default) => Task.FromResult(0);
        public Task<MergeTargetsDto?> GetMergeTargetsAsync(string taskId) => Task.FromResult<MergeTargetsDto?>(null);
        public Task<IReadOnlyList<SubtaskDiffDto>> GetPlanningAggregateAsync(string planningTaskId) =>
            Task.FromResult<IReadOnlyList<SubtaskDiffDto>>(Array.Empty<SubtaskDiffDto>());
        public Task<CombinedDiffResultDto?> BuildPlanningIntegrationBranchAsync(string planningTaskId, string targetBranch) =>
            Task.FromResult<CombinedDiffResultDto?>(null);
        public Task MergeAllPlanningAsync(string planningTaskId, string targetBranch) => Task.CompletedTask;
        public Task OpenInteractiveTerminalAsync(string taskId, CancellationToken ct = default) => Task.CompletedTask;
        public Task QueuePlanningSubtasksAsync(string parentTaskId, CancellationToken ct = default) => Task.CompletedTask;

        public Task ContinuePlanningMergeAsync(string planningTaskId)
        {
            ContinueCalledWith = planningTaskId;
            if (ContinueThrows is not null) throw ContinueThrows;
            return Task.CompletedTask;
        }

        public Task AbortPlanningMergeAsync(string planningTaskId)
        {
            AbortCalledWith = planningTaskId;
            if (AbortThrows is not null) throw AbortThrows;
            return Task.CompletedTask;
        }
    }

    private static ConflictResolutionViewModel BuildVm(FakeWorker worker, string planningTaskId = "plan-1") =>
        new ConflictResolutionViewModel(
            worker,
            planningTaskId,
            subtaskTitle: "My subtask",
            targetBranch: "main",
            conflictedFiles: new[] { "src/Foo.cs", "src/Bar.cs" },
            worktreePath: "C:/worktrees/plan-1");

    // ------------------------------------------------------------------ tests

    [Fact]
    public async Task ContinueAsync_CallsHub_AndClosesOnSuccess()
    {
        var worker = new FakeWorker();
        var vm = BuildVm(worker, "plan-42");
        bool closeCalled = false;
        vm.CloseRequested = () => closeCalled = true;

        await vm.ContinueCommand.ExecuteAsync(null);

        Assert.Equal("plan-42", worker.ContinueCalledWith);
        Assert.True(closeCalled);
        Assert.Null(vm.ActionError);
    }

    [Fact]
    public async Task ContinueAsync_HubThrows_ShowsActionErrorAndStaysOpen()
    {
        var worker = new FakeWorker { ContinueThrows = new InvalidOperationException("hub down") };
        var vm = BuildVm(worker);
        bool closeCalled = false;
        vm.CloseRequested = () => closeCalled = true;

        await vm.ContinueCommand.ExecuteAsync(null);

        Assert.False(closeCalled);
        Assert.Equal("hub down", vm.ActionError);
    }

    [Fact]
    public async Task AbortAsync_CallsHub_AndClosesOnSuccess()
    {
        var worker = new FakeWorker();
        var vm = BuildVm(worker, "plan-99");
        bool closeCalled = false;
        vm.CloseRequested = () => closeCalled = true;

        await vm.AbortCommand.ExecuteAsync(null);

        Assert.Equal("plan-99", worker.AbortCalledWith);
        Assert.True(closeCalled);
        Assert.Null(vm.ActionError);
    }

    [Fact]
    public async Task AbortAsync_HubThrows_ShowsActionError()
    {
        var worker = new FakeWorker { AbortThrows = new InvalidOperationException("abort failed") };
        var vm = BuildVm(worker);
        bool closeCalled = false;
        vm.CloseRequested = () => closeCalled = true;

        await vm.AbortCommand.ExecuteAsync(null);

        Assert.False(closeCalled);
        Assert.Equal("abort failed", vm.ActionError);
    }

    // OpenInVsCode is not unit-tested here because abstracting Process.Start
    // would require an indirection layer that isn't part of the approved design.
    // The error path is covered by the VsCodeError property being set on catch.
}
