using ClaudeDo.Data;
using ClaudeDo.Data.Models;
using ClaudeDo.Data.Repositories;
using ClaudeDo.Ui.Services;
using ClaudeDo.Ui.ViewModels.Islands;
using CommunityToolkit.Mvvm.Input;
using Microsoft.EntityFrameworkCore;
using Xunit;
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;

namespace ClaudeDo.Worker.Tests.UiVm;

// ── Fake worker client ────────────────────────────────────────────────────────

sealed class FakeWorkerClient : IWorkerClient
{
    public int StartPlanningCalls { get; private set; }
    public int ResumePlanningCalls { get; private set; }
    public int DiscardPlanningCalls { get; private set; }
    public int FinalizePlanningCalls { get; private set; }
    public int WakeQueueCalls { get; private set; }

    public bool IsConnected => false;
    public event System.ComponentModel.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 void RaiseTaskUpdated(string taskId) => TaskUpdatedEvent?.Invoke(taskId);
    public void RaiseWorktreeUpdated(string taskId) => WorktreeUpdatedEvent?.Invoke(taskId);
    public void RaiseTaskMessage(string taskId, string line) => TaskMessageEvent?.Invoke(taskId, line);

    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, TaskStatus status) => Task.CompletedTask;
    public Task WakeQueueAsync() { WakeQueueCalls++; return Task.CompletedTask; }
    public Task StartPlanningSessionAsync(string taskId, CancellationToken ct = default) { StartPlanningCalls++; return Task.CompletedTask; }
    public Task OpenInteractiveTerminalAsync(string taskId, CancellationToken ct = default) => Task.CompletedTask;
    public Task QueuePlanningSubtasksAsync(string parentTaskId, CancellationToken ct = default) => Task.CompletedTask;
    public Task ResumePlanningSessionAsync(string taskId, CancellationToken ct = default) { ResumePlanningCalls++; return Task.CompletedTask; }
    public Task<DiscardPlanningOutcome> DiscardPlanningSessionAsync(string taskId, bool dequeueQueuedChildren = false, CancellationToken ct = default)
    {
        DiscardPlanningCalls++;
        return Task.FromResult(new DiscardPlanningOutcome(DiscardPlanningResult.Discarded, 0, 0));
    }
    public Task FinalizePlanningSessionAsync(string taskId, bool queueAgentTasks = true, CancellationToken ct = default) { FinalizePlanningCalls++; return Task.CompletedTask; }
    public Task<int> GetPendingDraftCountAsync(string taskId, CancellationToken ct = default) => Task.FromResult(0);

    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<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 ContinuePlanningMergeAsync(string planningTaskId) => Task.CompletedTask;
    public Task AbortPlanningMergeAsync(string planningTaskId) => Task.CompletedTask;
}

// ── Helper to build VM with pre-seeded Items ──────────────────────────────────

file static class VmFactory
{
    // Minimal SQLite :memory: factory — never actually called in these tests
    // (we seed Items directly), but required by the VM constructor.
    private static IDbContextFactory<ClaudeDoDbContext> NullDbFactory()
    {
        var opts = new DbContextOptionsBuilder<ClaudeDoDbContext>()
            .UseSqlite("DataSource=:memory:")
            .Options;
        return new NullDbContextFactory(opts);
    }

    private sealed class NullDbContextFactory(DbContextOptions<ClaudeDoDbContext> opts)
        : IDbContextFactory<ClaudeDoDbContext>
    {
        public ClaudeDoDbContext CreateDbContext() => new(opts);
    }

    public static (TasksIslandViewModel vm, FakeWorkerClient worker) Create(
        IEnumerable<TaskRowViewModel> rows)
    {
        var worker = new FakeWorkerClient();
        var vm = new TasksIslandViewModel(NullDbFactory(), worker);
        foreach (var r in rows)
            vm.Items.Add(r);
        vm.Regroup();
        return (vm, worker);
    }
}

// ── Tests ─────────────────────────────────────────────────────────────────────

public class TasksIslandViewModelPlanningTests
{
    private static TaskRowViewModel MakeRow(
        string id,
        TaskStatus status,
        string? parentId = null,
        int sortOrder = 0,
        PlanningPhase phase = PlanningPhase.None)
        => new TaskRowViewModel { Id = id, Status = status, ParentTaskId = parentId, PlanningPhase = phase };

    [Fact]
    public void ToggleExpand_CollapsesChildrenOfPlanningParent()
    {
        var parent = MakeRow("p1", TaskStatus.Idle, phase: PlanningPhase.Active);
        var child1 = MakeRow("c1", TaskStatus.Idle, "p1");
        var child2 = MakeRow("c2", TaskStatus.Idle, "p1");

        var (vm, _) = VmFactory.Create([parent, child1, child2]);

        // Initially expanded — children visible in OpenItems
        Assert.Contains(child1, vm.OpenItems);
        Assert.Contains(child2, vm.OpenItems);

        // Collapse the parent
        vm.ToggleExpandCommand.Execute(parent);

        // Children should no longer appear
        Assert.DoesNotContain(child1, vm.OpenItems);
        Assert.DoesNotContain(child2, vm.OpenItems);
        // Parent still present
        Assert.Contains(parent, vm.OpenItems);
    }

    [Fact]
    public async Task OpenPlanningSession_IgnoresNonIdleRow()
    {
        var row = MakeRow("t1", TaskStatus.Queued);
        var (vm, worker) = VmFactory.Create([row]);

        await ((IAsyncRelayCommand<TaskRowViewModel?>)vm.OpenPlanningSessionCommand).ExecuteAsync(row);

        Assert.Equal(0, worker.StartPlanningCalls);
    }

    [Fact]
    public async Task OpenPlanningSession_CallsWorkerForIdleRow()
    {
        var row = MakeRow("t1", TaskStatus.Idle);
        var (vm, worker) = VmFactory.Create([row]);

        await ((IAsyncRelayCommand<TaskRowViewModel?>)vm.OpenPlanningSessionCommand).ExecuteAsync(row);

        Assert.Equal(1, worker.StartPlanningCalls);
    }

    [Fact]
    public void ToggleExpand_ExpandsCollapsedParentAgain()
    {
        var parent = MakeRow("p1", TaskStatus.Idle, phase: PlanningPhase.Finalized);
        var child = MakeRow("c1", TaskStatus.Idle, "p1");

        var (vm, _) = VmFactory.Create([parent, child]);

        // Collapse
        vm.ToggleExpandCommand.Execute(parent);
        Assert.DoesNotContain(child, vm.OpenItems);

        // Re-expand
        vm.ToggleExpandCommand.Execute(parent);
        Assert.Contains(child, vm.OpenItems);
    }
}
