using System.Text.RegularExpressions;
using ClaudeDo.Data.Environment;
using ClaudeDo.Installer.Core;
using ClaudeDo.Installer.Core.Interfaces;

namespace ClaudeDo.Installer.Checks;

public sealed record ClaudeCliLookupResult(
    ResolvedExecutable? Resolved,
    int ExitCode,
    string Output,
    Version? ParsedVersion);

/// <summary>
/// Resolves the claude CLI and runs `--version` at most once per install run, shared across
/// ClaudeCliCheck, ClaudeVersionCheck, ClaudeAuthCheck, and PermissionModeAutoCheck.
/// </summary>
public sealed class ClaudeCliLookup
{
    private static readonly Regex VersionPattern = new(@"\d+(\.\d+){1,3}", RegexOptions.Compiled);

    private readonly SemaphoreSlim _gate = new(1, 1);
    private ClaudeCliLookupResult? _cached;

    public ClaudeCliLookup(IProcessRunner processRunner, string? pathOverride = null, string? pathExtOverride = null)
    {
        ProcessRunner = processRunner;
        PathOverride = pathOverride;
        PathExtOverride = pathExtOverride;
    }

    public IProcessRunner ProcessRunner { get; }
    public string? PathOverride { get; }
    public string? PathExtOverride { get; }

    public async Task<ClaudeCliLookupResult> ResolveAsync(InstallContext ctx, CancellationToken ct)
    {
        if (_cached is not null) return _cached;

        await _gate.WaitAsync(ct);
        try
        {
            _cached ??= await ResolveCoreAsync(ctx, ct);
            return _cached;
        }
        finally
        {
            _gate.Release();
        }
    }

    private async Task<ClaudeCliLookupResult> ResolveCoreAsync(InstallContext ctx, CancellationToken ct)
    {
        var resolved = ExecutableResolver.Resolve(ctx.ClaudeBin, PathOverride, PathExtOverride);
        if (resolved is null)
        {
            return new ClaudeCliLookupResult(null, ExitCode: -1, Output: string.Empty, ParsedVersion: null);
        }

        var (fileName, arguments) = ProcessCommand.For(resolved, "--version");
        var (exitCode, output) = await ProcessRunner.RunAsync(fileName, arguments, null, ct);
        var parsedVersion = exitCode == 0 ? ParseVersion(output) : null;

        return new ClaudeCliLookupResult(resolved, exitCode, output, parsedVersion);
    }

    private static Version? ParseVersion(string output) =>
        VersionPattern.Match(output) is { Success: true } match && Version.TryParse(match.Value, out var version)
            ? version
            : null;
}
