using ClaudeDo.Data.Environment;

namespace ClaudeDo.Data.Tests;

public sealed class ExecutableResolverTests : IDisposable
{
    private readonly string _root;

    public ExecutableResolverTests()
    {
        _root = Path.Combine(Path.GetTempPath(), $"claudedo_exeresolve_{Guid.NewGuid():N}");
        Directory.CreateDirectory(_root);
    }

    public void Dispose()
    {
        try { Directory.Delete(_root, recursive: true); } catch { }
    }

    private string MakeDir(string name)
    {
        var dir = Path.Combine(_root, name);
        Directory.CreateDirectory(dir);
        return dir;
    }

    private static void Touch(string path) => File.WriteAllText(path, "");

    [Fact]
    public void Command_with_directory_separator_is_treated_as_path()
    {
        var dir = MakeDir("bin1");
        var exePath = Path.Combine(dir, "myclaude.exe");
        Touch(exePath);

        var result = ExecutableResolver.Resolve(exePath, pathOverride: "C:\\does\\not\\matter");

        Assert.NotNull(result);
        Assert.Equal(exePath, result!.Path);
        Assert.False(result.IsShim);
    }

    [Fact]
    public void Path_like_command_without_extension_resolves_via_pathext()
    {
        var dir = MakeDir("bin2");
        var cmdPath = Path.Combine(dir, "myclaude.cmd");
        Touch(cmdPath);
        var commandWithoutExt = Path.Combine(dir, "myclaude");

        var result = ExecutableResolver.Resolve(commandWithoutExt, pathExtOverride: ".com;.exe;.bat;.cmd");

        Assert.NotNull(result);
        Assert.Equal(cmdPath, result!.Path);
        Assert.True(result.IsShim);
    }

    [Fact]
    public void Cmd_file_in_path_is_found_when_no_exe_exists()
    {
        var dir = MakeDir("bin3");
        Touch(Path.Combine(dir, "claude.cmd"));

        var result = ExecutableResolver.Resolve("claude", pathOverride: dir, pathExtOverride: ".com;.exe;.bat;.cmd");

        Assert.NotNull(result);
        Assert.Equal(Path.Combine(dir, "claude.cmd"), result!.Path);
        Assert.True(result.IsShim);
    }

    [Fact]
    public void Exe_wins_against_cmd_in_same_directory()
    {
        var dir = MakeDir("bin4");
        Touch(Path.Combine(dir, "claude.cmd"));
        Touch(Path.Combine(dir, "claude.exe"));

        var result = ExecutableResolver.Resolve("claude", pathOverride: dir, pathExtOverride: ".com;.exe;.bat;.cmd");

        Assert.NotNull(result);
        Assert.Equal(Path.Combine(dir, "claude.exe"), result!.Path);
        Assert.False(result.IsShim);
    }

    [Fact]
    public void Directory_order_in_path_is_respected()
    {
        var dir1 = MakeDir("bin5a");
        var dir2 = MakeDir("bin5b");
        Touch(Path.Combine(dir1, "claude.cmd"));
        Touch(Path.Combine(dir2, "claude.exe"));

        var pathOverride = $"{dir1}{Path.PathSeparator}{dir2}";
        var result = ExecutableResolver.Resolve("claude", pathOverride: pathOverride, pathExtOverride: ".com;.exe;.bat;.cmd");

        Assert.NotNull(result);
        Assert.Equal(Path.Combine(dir1, "claude.cmd"), result!.Path);
        Assert.True(result.IsShim);
    }

    [Fact]
    public void Unresolvable_command_returns_null()
    {
        var dir = MakeDir("bin6");

        var result = ExecutableResolver.Resolve(
            "claudedo_totally_missing_cmd_9f3a1",
            pathOverride: dir,
            pathExtOverride: ".com;.exe;.bat;.cmd");

        Assert.Null(result);
    }

    [Fact]
    public void BuildShimStartInfo_uses_cmd_exe_and_quotes_paths_with_spaces()
    {
        const string shimPath = @"C:\Program Files\claude\claude.cmd";

        var startInfo = ExecutableResolver.BuildShimStartInfo(shimPath, new[] { "--version", "arg2" });

        Assert.Equal("cmd.exe", startInfo.FileName);
        Assert.Contains("/c", startInfo.Arguments);
        Assert.Contains($"\"{shimPath}\"", startInfo.Arguments);
        Assert.Contains("--version", startInfo.Arguments);
        Assert.Contains("arg2", startInfo.Arguments);
    }
}
