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

namespace ClaudeDo.Ui.Tests.ViewModels;

public class PlanningDiffViewModelTests
{
    private sealed class FakePlanningWorker : IWorkerClient
    {
        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 bool IsConnected => false;

        public IReadOnlyList<SubtaskDiffDto> AggregateResult { get; set; } = Array.Empty<SubtaskDiffDto>();
        public CombinedDiffResultDto? CombinedResult { get; set; }

        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(AggregateResult);
        public Task<CombinedDiffResultDto?> BuildPlanningIntegrationBranchAsync(string planningTaskId, string targetBranch) =>
            Task.FromResult(CombinedResult);
        public Task MergeAllPlanningAsync(string planningTaskId, string targetBranch) => Task.CompletedTask;
        public Task ContinuePlanningMergeAsync(string planningTaskId) => Task.CompletedTask;
        public Task AbortPlanningMergeAsync(string planningTaskId) => Task.CompletedTask;
        public Task OpenInteractiveTerminalAsync(string taskId, CancellationToken ct = default) => Task.CompletedTask;
        public Task QueuePlanningSubtasksAsync(string parentTaskId, CancellationToken ct = default) => Task.CompletedTask;
    }

    [Fact]
    public async Task InitializeAsync_PopulatesSubtasks()
    {
        var fake = new FakePlanningWorker
        {
            AggregateResult = new[]
            {
                new SubtaskDiffDto("s1", "First",  "branch-1", "base1", "head1", "+1 -0", "diff1"),
                new SubtaskDiffDto("s2", "Second", "branch-2", "base2", "head2", "+2 -1", "diff2"),
            }
        };
        var vm = new PlanningDiffViewModel(fake, "plan-1", "main");

        await vm.InitializeAsync();

        Assert.Equal(2, vm.Subtasks.Count);
        Assert.Equal(vm.Subtasks[0], vm.SelectedSubtask);
    }

    [Fact]
    public async Task SelectingSubtask_InGroupedMode_SetsDisplayedDiff()
    {
        var fake = new FakePlanningWorker
        {
            AggregateResult = new[]
            {
                new SubtaskDiffDto("s1", "First",  "b1", "base1", "head1", null, "DIFF-A"),
                new SubtaskDiffDto("s2", "Second", "b2", "base2", "head2", null, "DIFF-B"),
            }
        };
        var vm = new PlanningDiffViewModel(fake, "plan-1", "main");
        await vm.InitializeAsync();

        vm.SelectedSubtask = vm.Subtasks[1];

        Assert.Equal("DIFF-B", vm.DisplayedDiff);
    }

    [Fact]
    public async Task ToggleCombined_Success_DisplaysUnifiedDiff()
    {
        var fake = new FakePlanningWorker
        {
            AggregateResult = new[]
            {
                new SubtaskDiffDto("s1", "First", "b1", "base1", "head1", null, "DIFF-A"),
            },
            CombinedResult = new CombinedDiffResultDto(true, "integration-branch", "COMBINED-DIFF", null, null),
        };
        var vm = new PlanningDiffViewModel(fake, "plan-1", "main");
        await vm.InitializeAsync();

        vm.IsCombinedMode = true;

        // Wait for the async toggle command to complete
        var deadline = DateTime.UtcNow.AddSeconds(5);
        while (DateTime.UtcNow < deadline && vm.IsLoadingCombined)
            await Task.Delay(10);

        Assert.Equal("COMBINED-DIFF", vm.DisplayedDiff);
        Assert.Null(vm.CombinedWarning);
    }

    [Fact]
    public async Task ToggleCombined_Conflict_ShowsWarning()
    {
        var fake = new FakePlanningWorker
        {
            AggregateResult = new[]
            {
                new SubtaskDiffDto("s1", "First", "b1", "base1", "head1", null, "DIFF-A"),
            },
            CombinedResult = new CombinedDiffResultDto(false, null, null, "subtask-42", new[] { "a.cs", "b.cs" }),
        };
        var vm = new PlanningDiffViewModel(fake, "plan-1", "main");
        await vm.InitializeAsync();

        vm.IsCombinedMode = true;

        var deadline = DateTime.UtcNow.AddSeconds(5);
        while (DateTime.UtcNow < deadline && vm.IsLoadingCombined)
            await Task.Delay(10);

        Assert.NotNull(vm.CombinedWarning);
        Assert.Contains("subtask-42", vm.CombinedWarning);
        Assert.Contains("2 files", vm.CombinedWarning);
    }

    [Fact]
    public async Task ToggleCombined_HubReturnsNull_ShowsError()
    {
        var fake = new FakePlanningWorker
        {
            AggregateResult = new[]
            {
                new SubtaskDiffDto("s1", "First", "b1", "base1", "head1", null, "DIFF-A"),
            },
            CombinedResult = null,
        };
        var vm = new PlanningDiffViewModel(fake, "plan-1", "main");
        await vm.InitializeAsync();

        vm.IsCombinedMode = true;

        var deadline = DateTime.UtcNow.AddSeconds(5);
        while (DateTime.UtcNow < deadline && vm.IsLoadingCombined)
            await Task.Delay(10);

        Assert.NotNull(vm.CombinedWarning);
        Assert.NotEmpty(vm.CombinedWarning!);
    }
}
