using Moq;
using PersonalToolBox.Models;
using PersonalToolBox.Services;
using PersonalToolBox.ViewModels;

namespace PersonalToolBox.Tests.ViewModels;

public class GroupEditViewModelTests
{
    private readonly Mock<IDataService> _dataServiceMock = new();
    private readonly Mock<ILogService> _logServiceMock = new();
    private readonly AppConfig _config;

    public GroupEditViewModelTests()
    {
        _config = new AppConfig
        {
            Tools =
            [
                new() { Id = "t1", Name = "VS Code", IsValid = true },
                new() { Id = "t2", Name = "Postman", IsValid = true },
                new() { Id = "t3", Name = "SomeGroup", IsGroup = true, SubToolIds = new List<string> { "t1" }, IsValid = true }
            ]
        };
        _dataServiceMock.Setup(d => d.Config).Returns(_config);
    }

    [Fact]
    public void Constructor_AddMode_LoadsAllToolsIncludingGroups()
    {
        var vm = new GroupEditViewModel(_dataServiceMock.Object, _logServiceMock.Object);

        Assert.Equal("添加组合", vm.WindowTitle);
        Assert.Equal(3, vm.AvailableTools.Count); // t1, t2, t3 (new groups can include other groups)
    }

    [Fact]
    public void Constructor_EditMode_ExcludesSelfAndAncestors()
    {
        // Group A (t3) contains t1 -> Group B being edited should exclude itself
        var group = new ToolItem
        {
            Id = "t4",
            Name = "GroupB",
            IsGroup = true,
            SubToolIds = new List<string>()
        };
        // t3 contains t4, so t3 is an ancestor of t4
        _config.Tools.First(t => t.Id == "t3").SubToolIds.Add("t4");

        var vm = new GroupEditViewModel(_dataServiceMock.Object, _logServiceMock.Object, group);

        // t4 (self) and t3 (ancestor) should be excluded
        Assert.Equal(2, vm.AvailableTools.Count); // t1, t2 only
    }

    [Fact]
    public void Constructor_EditMode_PreselectedTools()
    {
        var group = new ToolItem
        {
            Name = "ExistingGroup",
            IsGroup = true,
            SubToolIds = new List<string> { "t1" }
        };

        var vm = new GroupEditViewModel(_dataServiceMock.Object, _logServiceMock.Object, group);

        Assert.Equal("编辑组合", vm.WindowTitle);
        Assert.Equal("ExistingGroup", vm.Name);
        Assert.True(vm.AvailableTools.First(t => t.Tool.Id == "t1").IsSelected);
        Assert.False(vm.AvailableTools.First(t => t.Tool.Id == "t2").IsSelected);
    }

    [Fact]
    public void Save_EmptyName_LogsWarning()
    {
        var vm = new GroupEditViewModel(_dataServiceMock.Object, _logServiceMock.Object);

        vm.SaveCommand.Execute(null);

        _logServiceMock.Verify(x => x.Warning(It.Is<string>(s => s.Contains("名称不能为空"))), Times.Once);
    }

    [Fact]
    public void Save_AddMode_CreatesGroupAndCloses()
    {
        var vm = new GroupEditViewModel(_dataServiceMock.Object, _logServiceMock.Object)
        {
            Name = "NewGroup"
        };
        vm.AvailableTools.First(t => t.Tool.Id == "t1").IsSelected = true;
        bool? closeResult = null;
        vm.CloseAction = (r) => closeResult = r;

        vm.SaveCommand.Execute(null);

        Assert.True(vm.Saved);
        Assert.True(closeResult);
        Assert.Single(_config.Tools.Where(t => t.IsGroup && t.Name == "NewGroup"));
        Assert.Contains("t1", _config.Tools.First(t => t.Name == "NewGroup").SubToolIds);
        _dataServiceMock.Verify(x => x.Save(), Times.Once);
    }

    [Fact]
    public void Cancel_ClosesWithoutSaving()
    {
        var vm = new GroupEditViewModel(_dataServiceMock.Object, _logServiceMock.Object);
        bool? closeResult = null;
        vm.CloseAction = (r) => closeResult = r;

        vm.CancelCommand.Execute(null);

        Assert.False(vm.Saved);
        Assert.False(closeResult);
    }
}
