using System.IO;
using Moq;
using PersonalToolBox.Models;
using PersonalToolBox.Services;

namespace PersonalToolBox.Tests.Services;

public class JsonDataServiceTests
{
    private readonly Mock<ILogService> _logServiceMock = new();
    private readonly string _testDir;

    public JsonDataServiceTests()
    {
        _testDir = Path.Combine(Path.GetTempPath(), $"toolbox_test_{Guid.NewGuid():N}");
        Directory.CreateDirectory(_testDir);
    }

    [Fact]
    public void Load_FileNotExists_CreatesDefaultConfig()
    {
        // Use a path that doesn't exist
        var configPath = Path.Combine(_testDir, "nonexistent.json");
        var service = CreateService(configPath);

        service.Load();

        Assert.NotNull(service.Config);
        Assert.Equal("Dark", service.Config.Theme);
        Assert.Empty(service.Config.Tools);

        // Verify warning was logged
        VerifyLog(LogLevel.Info, "默认配置");
    }

    [Fact]
    public void Save_ThenLoad_PreservesData()
    {
        var configPath = Path.Combine(_testDir, "config.json");
        var service = CreateService(configPath);

        service.Config.Theme = "Light";
        service.Config.Categories.Add(new Category { Id = "1", Name = "dev" });
        service.Config.Tools.Add(new ToolItem
        {
            Name = "TestTool",
            ExecutablePath = Path.Combine(_testDir, "test.exe")
        });

        service.Save();

        // Load into a new service
        var service2 = CreateService(configPath);
        service2.Load();

        Assert.Equal("Light", service2.Config.Theme);
        Assert.Single(service2.Config.Categories);
        Assert.Equal("dev", service2.Config.Categories[0].Name);
        Assert.Single(service2.Config.Tools);
        Assert.Equal("TestTool", service2.Config.Tools[0].Name);
    }

    [Fact]
    public void Load_InvalidPath_ToolSetToInvalid()
    {
        var configPath = Path.Combine(_testDir, "config.json");
        var service = CreateService(configPath);

        service.Config.Tools.Add(new ToolItem
        {
            Name = "MissingTool",
            ExecutablePath = @"C:\definitely\does\not\exist.exe"
        });

        service.Save();

        var service2 = CreateService(configPath);
        service2.Load();

        var tool = service2.Config.Tools[0];
        Assert.False(tool.IsValid);
        VerifyLog(LogLevel.Warning, "路径失效");
    }

    [Fact]
    public void Load_ValidPath_ToolRemainsValid()
    {
        var configPath = Path.Combine(_testDir, "config.json");
        var service = CreateService(configPath);

        // Create a real temp file
        var tempFile = Path.Combine(_testDir, "real_tool.exe");
        File.WriteAllText(tempFile, "dummy");

        service.Config.Tools.Add(new ToolItem
        {
            Name = "RealTool",
            ExecutablePath = tempFile
        });

        service.Save();

        var service2 = CreateService(configPath);
        service2.Load();

        var tool = service2.Config.Tools[0];
        Assert.True(tool.IsValid);
    }

    [Fact]
    public void Load_UrlPath_AlwaysValid()
    {
        var configPath = Path.Combine(_testDir, "config.json");
        var service = CreateService(configPath);

        service.Config.Tools.Add(new ToolItem
        {
            Name = "WebTool",
            ExecutablePath = "https://example.com/tool"
        });

        service.Save();

        var service2 = CreateService(configPath);
        service2.Load();

        var tool = service2.Config.Tools[0];
        Assert.True(tool.IsValid);
    }

    [Fact]
    public void Load_EmptyExecutablePath_SkipsValidation()
    {
        var configPath = Path.Combine(_testDir, "config.json");
        var service = CreateService(configPath);

        service.Config.Tools.Add(new ToolItem
        {
            Name = "NoPathTool",
            ExecutablePath = ""
        });

        service.Save();

        var service2 = CreateService(configPath);
        service2.Load();

        var tool = service2.Config.Tools[0];
        Assert.True(tool.IsValid);
    }

    [Fact]
    public void Load_CorruptedJson_LogsErrorAndUsesDefaults()
    {
        var configPath = Path.Combine(_testDir, "config.json");
        File.WriteAllText(configPath, "this is not json {{{");

        var service = CreateService(configPath);
        service.Load();

        Assert.NotNull(service.Config);
        Assert.Equal("Dark", service.Config.Theme);
        VerifyLog(LogLevel.Error, "解析失败");
    }

    // ───────────────────────────── helpers ─────────────────────────────

    private JsonDataService CreateService(string filePath)
    {
        var service = new JsonDataService(_logServiceMock.Object);
        // Replace the file path via reflection
        typeof(JsonDataService)
            .GetField("_filePath", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)?
            .SetValue(service, filePath);
        return service;
    }

    private void VerifyLog(LogLevel level, string fragment)
    {
        // 根据日志级别验证对应方法被调用
        switch (level)
        {
            case LogLevel.Info:
                _logServiceMock.Verify(x => x.Info(It.Is<string>(s => s.Contains(fragment))), Times.AtLeastOnce);
                break;
            case LogLevel.Warning:
                _logServiceMock.Verify(x => x.Warning(It.Is<string>(s => s.Contains(fragment))), Times.AtLeastOnce);
                break;
            case LogLevel.Error:
                _logServiceMock.Verify(x => x.Error(It.Is<string>(s => s.Contains(fragment))), Times.AtLeastOnce);
                break;
        }
    }
}
