using System.Collections.Generic;
using ClaudeDo.Data.Models;
using ClaudeDo.Ui.ViewModels.Islands;
using Xunit;
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;

namespace ClaudeDo.Worker.Tests.UiVm;

public class TaskRowViewModelTests
{
    [Theory]
    [InlineData(TaskStatus.Running, "running")]
    [InlineData(TaskStatus.WaitingForReview, "review")]
    [InlineData(TaskStatus.Failed,  "error")]
    [InlineData(TaskStatus.Done,    "done")]
    [InlineData(TaskStatus.Queued,  "queued")]
    [InlineData(TaskStatus.Idle,    "idle")]
    public void StatusChipClass_Maps_Correctly(TaskStatus s, string expected)
    {
        var vm = new TaskRowViewModel { Id = "t" };
        vm.Status = s;
        Assert.Equal(expected, vm.StatusChipClass);
    }

    [Fact]
    public void Number_SetFromEntity_SurvivesUpdateFromEntity()
    {
        var entity = new TaskEntity
        {
            Id = "t", ListId = "l1", Title = "Task", Number = 123, CreatedAt = DateTime.UtcNow,
        };
        var vm = TaskRowViewModel.FromEntity(entity);
        Assert.Equal(123, vm.Number);

        // Simulates the TaskUpdated refresh path: the entity is re-fetched and the same
        // row is updated in place rather than replaced.
        entity.Title = "Task (edited)";
        vm.UpdateFromEntity(entity);

        Assert.Equal(123, vm.Number);
    }

    [Theory]
    [InlineData(-1, false)]
    [InlineData(0,  false)]
    [InlineData(1,  true)]
    [InlineData(42, true)]
    public void ShowNumberBadge_Hides_For_Zero_And_Negative(int number, bool expected)
    {
        var vm = new TaskRowViewModel { Id = "t" };
        vm.Number = number;
        Assert.Equal(expected, vm.ShowNumberBadge);
    }

    [Fact]
    public void ShowNumberBadge_Raises_PropertyChanged_On_Number_Change()
    {
        var vm = new TaskRowViewModel { Id = "t" };
        var raised = new List<string?>();
        vm.PropertyChanged += (_, e) => raised.Add(e.PropertyName);

        vm.Number = 5;

        Assert.Contains(nameof(TaskRowViewModel.ShowNumberBadge), raised);
    }

    [Fact]
    public void CanOpenWorktree_True_And_ReasonNull_When_Folder_Exists()
    {
        var dir = Path.Combine(Path.GetTempPath(), $"cd_wt_{Guid.NewGuid():N}");
        Directory.CreateDirectory(dir);
        try
        {
            var vm = new TaskRowViewModel { Id = "t" };
            vm.WorktreePath = dir;

            Assert.True(vm.CanOpenWorktree);
            Assert.Null(vm.OpenWorktreeDisabledReason);
        }
        finally { Directory.Delete(dir, true); }
    }

    [Theory]
    [InlineData(null)]
    [InlineData("")]
    [InlineData("   ")]
    public void CanOpenWorktree_False_And_ReasonSet_When_Path_Missing(string? path)
    {
        var vm = new TaskRowViewModel { Id = "t" };
        vm.WorktreePath = path;

        Assert.False(vm.CanOpenWorktree);
        Assert.NotNull(vm.OpenWorktreeDisabledReason);
    }

    // A merged/discarded worktree leaves its recorded Path on the row long after the folder is
    // gone — the menu entry has to gray out, not throw inside Process.Start.
    [Fact]
    public void CanOpenWorktree_False_When_Recorded_Folder_Is_Gone()
    {
        var vm = new TaskRowViewModel { Id = "t" };
        vm.WorktreePath = Path.Combine(Path.GetTempPath(), $"cd_wt_gone_{Guid.NewGuid():N}");

        Assert.False(vm.CanOpenWorktree);
        Assert.NotNull(vm.OpenWorktreeDisabledReason);
    }

    [Fact]
    public void OpenWorktreeGate_Raises_PropertyChanged_On_WorktreePath_Change()
    {
        var vm = new TaskRowViewModel { Id = "t" };
        var raised = new List<string?>();
        vm.PropertyChanged += (_, e) => raised.Add(e.PropertyName);

        vm.WorktreePath = @"C:\repo\worktrees\t";

        Assert.Contains(nameof(TaskRowViewModel.CanOpenWorktree), raised);
        Assert.Contains(nameof(TaskRowViewModel.OpenWorktreeDisabledReason), raised);
    }

    [Fact]
    public void WorktreePath_SetFromEntity_MapsFromWorktreePath()
    {
        var entity = new TaskEntity
        {
            Id = "t", ListId = "l1", Title = "Task", CreatedAt = DateTime.UtcNow,
            Worktree = new WorktreeEntity
            {
                TaskId = "t", Path = @"C:\repo\worktrees\t", BranchName = "b",
                BaseCommit = "abc123", CreatedAt = DateTime.UtcNow,
            },
        };
        var vm = TaskRowViewModel.FromEntity(entity);

        Assert.Equal(@"C:\repo\worktrees\t", vm.WorktreePath);
    }

    [Fact]
    public void IsDropTarget_Follows_Either_DropHint_And_Raises_PropertyChanged()
    {
        var vm = new TaskRowViewModel { Id = "t" };
        var raised = new List<string?>();
        vm.PropertyChanged += (_, e) => raised.Add(e.PropertyName);

        Assert.False(vm.IsDropTarget);

        vm.DropHintAbove = true;
        Assert.True(vm.IsDropTarget);
        Assert.Contains(nameof(TaskRowViewModel.IsDropTarget), raised);

        vm.DropHintAbove = false;
        vm.DropHintBelow = true;
        Assert.True(vm.IsDropTarget);

        vm.DropHintBelow = false;
        Assert.False(vm.IsDropTarget);
    }
}
