using ClaudeDo.Installer.Checks;
using ClaudeDo.Installer.Core;

namespace ClaudeDo.Installer.Tests.Checks;

public sealed class ClaudeCliLookupTests : IDisposable
{
    private readonly string _dir;

    public ClaudeCliLookupTests()
    {
        _dir = Path.Combine(Path.GetTempPath(), $"cdclaudelookup_{Guid.NewGuid():N}");
        Directory.CreateDirectory(_dir);
        File.WriteAllText(Path.Combine(_dir, "claude.exe"), "");
    }

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

    [Fact]
    public async Task Version_is_executed_only_once_across_repeated_calls()
    {
        var runner = new FakeProcessRunner((0, "2.1.230 (Claude Code)"));
        var lookup = new ClaudeCliLookup(runner, pathOverride: _dir, pathExtOverride: ".exe");

        await lookup.ResolveAsync(new InstallContext(), CancellationToken.None);
        await lookup.ResolveAsync(new InstallContext(), CancellationToken.None);
        await lookup.ResolveAsync(new InstallContext(), CancellationToken.None);

        Assert.Single(runner.Calls);
    }

    [Fact]
    public async Task Concurrent_calls_still_run_version_only_once()
    {
        var runner = new FakeProcessRunner((0, "2.1.230 (Claude Code)"));
        var lookup = new ClaudeCliLookup(runner, pathOverride: _dir, pathExtOverride: ".exe");

        await Task.WhenAll(Enumerable.Range(0, 8)
            .Select(_ => lookup.ResolveAsync(new InstallContext(), CancellationToken.None)));

        Assert.Single(runner.Calls);
    }

    [Fact]
    public async Task Version_is_executed_only_once_when_shared_across_all_four_checks()
    {
        var runner = new FakeProcessRunner((0, "2.1.230 (Claude Code)"));
        var lookup = new ClaudeCliLookup(runner, pathOverride: _dir, pathExtOverride: ".exe");
        var ctx = new InstallContext();

        await new ClaudeCliCheck(lookup).RunAsync(ctx, CancellationToken.None);
        await new ClaudeVersionCheck(lookup).RunAsync(ctx, CancellationToken.None);
        await new ClaudeAuthCheck(lookup).RunAsync(ctx, CancellationToken.None);
        await new PermissionModeAutoCheck(lookup).RunAsync(ctx, CancellationToken.None);

        var versionCalls = runner.Calls.Count(c => c.Arguments.Contains("--version"));
        Assert.Equal(1, versionCalls);
    }

    [Fact]
    public async Task Cli_not_found_returns_null_resolved_without_running_a_process()
    {
        var emptyDir = Path.Combine(Path.GetTempPath(), $"cdclaudelookup_empty_{Guid.NewGuid():N}");
        Directory.CreateDirectory(emptyDir);
        try
        {
            var runner = new FakeProcessRunner();
            var lookup = new ClaudeCliLookup(runner, pathOverride: emptyDir, pathExtOverride: ".exe");
            var ctx = new InstallContext { ClaudeBin = "claudedo_totally_missing_cmd_9f3a1" };

            var result = await lookup.ResolveAsync(ctx, CancellationToken.None);

            Assert.Null(result.Resolved);
            Assert.Empty(runner.Calls);
        }
        finally
        {
            Directory.Delete(emptyDir, recursive: true);
        }
    }
}
