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

namespace PersonalToolBox.Tests.Services;

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

    public ProcessExecutionServiceTests()
    {
        _dataServiceMock.Setup(d => d.Config).Returns(_config);
    }

    [Fact]
    public async Task Execute_NullTool_LogsError()
    {
        var service = new ProcessExecutionService(_logServiceMock.Object, _dataServiceMock.Object);

        await service.ExecuteAsync(null!);

        _logServiceMock.Verify(x => x.Error(It.Is<string>(s => s.Contains("空工具项"))), Times.Once);
    }

    [Fact]
    public async Task Execute_InvalidTool_LogsWarning()
    {
        var tool = new ToolItem
        {
            Name = "BadTool",
            ExecutablePath = @"C:\nonexistent.exe",
            IsValid = false
        };
        var service = new ProcessExecutionService(_logServiceMock.Object, _dataServiceMock.Object);

        await service.ExecuteAsync(tool);

        _logServiceMock.Verify(x => x.Warning(It.Is<string>(s => s.Contains("无法运行") && s.Contains("BadTool"))), Times.Once);
    }

    [Fact]
    public async Task Execute_ProcessFails_LogsError()
    {
        var tool = new ToolItem
        {
            Name = "FailTool",
            ExecutablePath = @"Z:\impossible_path\not_really.exe",
            IsValid = true
        };
        var service = new ProcessExecutionService(_logServiceMock.Object, _dataServiceMock.Object);

        await service.ExecuteAsync(tool);

        _logServiceMock.Verify(x => x.Error(It.Is<string>(s => s.Contains("FailTool") && s.Contains("失败"))), Times.Once);
    }

    [Fact]
    public async Task Execute_Success_LogsInfo()
    {
        var tool = new ToolItem
        {
            Name = "记事本",
            ExecutablePath = "notepad.exe",
            IsValid = true
        };
        var service = new ProcessExecutionService(_logServiceMock.Object, _dataServiceMock.Object);

        await service.ExecuteAsync(tool);

        _logServiceMock.Verify(x => x.Info(It.Is<string>(s => s.Contains("成功启动") && s.Contains("记事本"))), Times.Once);
    }

    [Fact]
    public async Task Execute_Group_BatchExecutesSubTools()
    {
        var sub1 = new ToolItem { Id = "s1", Name = "Sub1", ExecutablePath = "cmd.exe", IsValid = true };
        var sub2 = new ToolItem { Id = "s2", Name = "Sub2", ExecutablePath = "cmd.exe", IsValid = true };
        _config.Tools.Add(sub1);
        _config.Tools.Add(sub2);

        var group = new ToolItem
        {
            Name = "MyGroup",
            IsGroup = true,
            SubToolIds = new List<string> { "s1", "s2" },
            IsValid = true
        };
        var service = new ProcessExecutionService(_logServiceMock.Object, _dataServiceMock.Object);

        await service.ExecuteAsync(group);

        _logServiceMock.Verify(x => x.Info(It.Is<string>(s => s.Contains("开始启动组合"))), Times.Once);
        _logServiceMock.Verify(x => x.Info(It.Is<string>(s => s.Contains("组合") && s.Contains("启动完成"))), Times.Once);
        _logServiceMock.Verify(x => x.Info(It.Is<string>(s => s.Contains("成功启动") && s.Contains("Sub1"))), Times.Once);
    }

    [Fact]
    public async Task Execute_Group_OrphanSubIds_Skipped()
    {
        var sub1 = new ToolItem { Id = "s1", Name = "Sub1", ExecutablePath = "cmd.exe", IsValid = true };
        _config.Tools.Add(sub1);

        var group = new ToolItem
        {
            Name = "Group",
            IsGroup = true,
            SubToolIds = new List<string> { "s1", "missing_id" },
            IsValid = true
        };
        var service = new ProcessExecutionService(_logServiceMock.Object, _dataServiceMock.Object);

        await service.ExecuteAsync(group);

        _logServiceMock.Verify(x => x.Warning(It.Is<string>(s => s.Contains("找不到 ID") && s.Contains("missing_id"))), Times.Once);
        _logServiceMock.Verify(x => x.Info(It.Is<string>(s => s.Contains("成功启动") && s.Contains("Sub1"))), Times.Once);
    }
}
