// Copyright (c) 2025 TerraByte Inc.
//
// A new helper class that abstracts away the boilerplate of running external
// command-line processes, specifically Git and VS Code, using CliWrap.

using System;
using CliWrap;
using System.IO;
using System.Linq;
using UnityEngine;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;

namespace Terra.Arbitrator.Services
{
    /// <summary>
    /// An internal helper class for executing Git commands.
    /// It centralizes the logic for finding executables and running them via CliWrap.
    /// </summary>
    internal static class GitCommand
    {
        private static string _projectRoot;
        private static string ProjectRoot => _projectRoot ??= Directory.GetParent(Application.dataPath)?.FullName;

        /// <summary>
        /// Runs a git command asynchronously.
        /// </summary>
        /// <param name="log">A StringBuilder to capture command output for logging.</param>
        /// <param name="args">The arguments to pass to the git command.</param>
        /// <param name="acceptableExitCodes">A list of exit codes that should not be treated as errors.</param>
        public static async Task RunAsync(StringBuilder log, string[] args, params int[] acceptableExitCodes)
        {
            var stdOutBuffer = new StringBuilder();
            var stdErrBuffer = new StringBuilder();
            var argumentsString = string.Join(" ", args);
            log?.AppendLine($"\n--- Executing: git {argumentsString} ---");

            var command = Cli.Wrap(FindGitExecutable())
                             .WithArguments(args)
                             .WithWorkingDirectory(ProjectRoot)
                             .WithValidation(CommandResultValidation.None) // We handle validation manually
                         | (PipeTarget.ToDelegate(x => stdOutBuffer.Append(x)), PipeTarget.ToDelegate(x => stdErrBuffer.Append(x)));

            var result = await command.ExecuteAsync();

            log?.AppendLine($"Exit Code: {result.ExitCode}");
            if (stdOutBuffer.Length > 0) log?.AppendLine($"StdOut: {stdOutBuffer}");
            if (stdErrBuffer.Length > 0) log?.AppendLine($"StdErr: {stdErrBuffer}");

            // Default to 0 if no specific codes are provided
            if (acceptableExitCodes.Length == 0)
            {
                acceptableExitCodes = new[] { 0 };
            }

            if (!acceptableExitCodes.Contains(result.ExitCode))
            {
                throw new Exception($"Command 'git {argumentsString}' failed with unexpected exit code {result.ExitCode}. Error: {stdErrBuffer}");
            }
        }
        
        /// <summary>
        /// Finds the absolute path to a given executable.
        /// </summary>
        private static string FindExecutable(string name)
        {
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                // CliWrap handles PATH search on Windows automatically.
                return name;
            }
            // For macOS/Linux, we need to be more explicit due to Unity's sandboxing.
            string[] searchPaths = { "/usr/local/bin", "/usr/bin", "/bin", "/opt/homebrew/bin" };
            foreach (var path in searchPaths)
            {
                var fullPath = Path.Combine(path, name);
                if (File.Exists(fullPath))
                {
                    return fullPath;
                }
            }
            throw new FileNotFoundException($"Could not find executable '{name}'. Please ensure it is installed and in your system's PATH.");
        }
        
        public static string FindVsCodeExecutable() => FindExecutable("code");
        private static string FindGitExecutable() => FindExecutable("git");
    }
}
