using System.Diagnostics;

namespace AxCopilot.Services;

/// <summary>
/// 런타임 환경(Python, Node.js 등) 설치 여부를 감지하는 정적 헬퍼.
/// 스킬 시스템에서 런타임 의존 스킬의 가용성을 판단하는 데 사용합니다.
/// 결과는 5분간 캐시되어 반복 호출 시 프로세스 생성을 최소화합니다.
/// </summary>
public static class RuntimeDetector
{
    private static Dictionary<string, bool>? _cache;
    private static DateTime _cacheTime;
    private static readonly TimeSpan CacheTtl = TimeSpan.FromMinutes(5);

    /// <summary>지정된 런타임이 설치되어 PATH에서 접근 가능한지 확인합니다.</summary>
    /// <param name="runtime">런타임 이름 (python, node, dotnet 등)</param>
    public static bool IsAvailable(string runtime)
    {
        if (string.IsNullOrWhiteSpace(runtime)) return true;
        runtime = runtime.Trim().ToLowerInvariant();

        // 캐시 확인
        if (_cache != null && (DateTime.UtcNow - _cacheTime) < CacheTtl)
        {
            if (_cache.TryGetValue(runtime, out var cached))
                return cached;
        }
        else
        {
            _cache = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
            _cacheTime = DateTime.UtcNow;
        }

        var available = DetectRuntime(runtime);
        _cache[runtime] = available;
        return available;
    }

    /// <summary>캐시를 강제로 초기화합니다.</summary>
    public static void ClearCache() => _cache = null;

    private static bool DetectRuntime(string runtime)
    {
        // 1단계: where.exe로 PATH 존재 확인
        var wherePath = RunQuick("where.exe", runtime);
        if (string.IsNullOrEmpty(wherePath)) return false;

        // 2단계: --version으로 실행 가능 확인
        var versionArg = runtime switch
        {
            "java" => "-version",
            _ => "--version",
        };
        var version = RunQuick(runtime, versionArg);
        return !string.IsNullOrEmpty(version);
    }

    private static string? RunQuick(string exe, string args)
    {
        try
        {
            var psi = new ProcessStartInfo(exe, args)
            {
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                UseShellExecute = false,
                CreateNoWindow = true,
            };
            using var proc = Process.Start(psi);
            if (proc == null) return null;
            var output = proc.StandardOutput.ReadToEnd();
            var error = proc.StandardError.ReadToEnd();
            proc.WaitForExit(5000);
            var result = string.IsNullOrWhiteSpace(output) ? error : output;
            return string.IsNullOrWhiteSpace(result) ? null : result.Trim();
        }
        catch (Exception) { return null; }
    }
}
