using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Text;

namespace File_Folder_Helper.Helpers;

internal static class HelperGit
{

    private record ProcessResult(string Errors,
                                 int ExitCode,
                                 string Output);

    private static async Task<ProcessResult> RunProcessAsync(string application, string arguments, string workingDirectory, CancellationToken cancellationToken)
    {
        using Process process = new();
        StringBuilder outputBuilder = new();
        StringBuilder errorsBuilder = new();
        process.StartInfo = new ProcessStartInfo
        {
            CreateNoWindow = true,
            UseShellExecute = false,
            RedirectStandardError = true,
            RedirectStandardOutput = true,
            FileName = application,
            Arguments = arguments,
            WorkingDirectory = workingDirectory,
        };
        process.OutputDataReceived += (_, args) => outputBuilder.AppendLine(args.Data);
        process.ErrorDataReceived += (_, args) => errorsBuilder.AppendLine(args.Data);
        _ = process.Start();
        process.BeginErrorReadLine();
        process.BeginOutputReadLine();
        await process.WaitForExitAsync(cancellationToken);
        return new(errorsBuilder.ToString().Trim(), process.ExitCode, outputBuilder.ToString().Trim());
    }

    private static async Task<string> RunAsync(string arguments, string workingDirectory, CancellationToken cancellationToken)
    {
        ProcessResult result = await RunProcessAsync("git", arguments, workingDirectory, cancellationToken);
        if (result.ExitCode != 0)
            throw new Exception($"{result.ExitCode} {result.Errors}");
        return result.Output;
    }

    private static List<string> GetOthersModifiedAndDeletedExcludingStandardFilesAsList(string repositoryDirectory, bool usePathCombine, CancellationToken cancellationToken)
    {
        List<string> results = [];
        Task<string> task = RunAsync("ls-files --others --modified --deleted --exclude-standard", repositoryDirectory, cancellationToken);
        task.Wait(cancellationToken);
        string[] files = task.Result.Split("\r\n");
        foreach (string file in files)
        {
            if (!usePathCombine)
                results.Add(file);
            else
                results.Add(Path.GetFullPath(Path.Combine(repositoryDirectory, file)));
        }
        return results;
    }

    internal static ReadOnlyCollection<string> GetOthersModifiedAndDeletedExcludingStandardFiles(string repositoryDirectory, bool usePathCombine, CancellationToken cancellationToken)
    {
        List<string> results = [];
        List<string> relativePathFiles;
        DirectoryInfo directoryInfo;
        bool usePathCombineInner = false;
        string checkDirectory = Path.Combine(repositoryDirectory, ".git");
        if (Directory.Exists(checkDirectory))
            results.AddRange(GetOthersModifiedAndDeletedExcludingStandardFilesAsList(repositoryDirectory, usePathCombine, cancellationToken));
        string[] subdirectories = Directory.GetDirectories(repositoryDirectory, "*", SearchOption.TopDirectoryOnly);
        foreach (string subdirectory in subdirectories)
        {
            directoryInfo = new(subdirectory);
            if (directoryInfo.LinkTarget is null)
                continue;
            checkDirectory = Path.Combine(directoryInfo.LinkTarget, ".git");
            if (!Directory.Exists(checkDirectory))
                continue;
            relativePathFiles = GetOthersModifiedAndDeletedExcludingStandardFilesAsList(directoryInfo.LinkTarget, usePathCombineInner, cancellationToken);
            foreach (string relativePathFile in relativePathFiles)
                results.Add(Path.GetFullPath(Path.Combine(subdirectory, relativePathFile)));
        }
        return new(results);
    }

    internal static ReadOnlyCollection<string> RemoteRemove(string repositoryDirectory, string name, CancellationToken cancellationToken)
    {
        List<string> results = [];
        Task<string> task = RunAsync($"remote rm {name}", repositoryDirectory, cancellationToken);
        task.Wait(cancellationToken);
        string[] lines = task.Result.Split("\r\n");
        foreach (string line in lines)
            results.Add(line);
        return new(results);
    }

    internal static ReadOnlyCollection<string> RemoteAdd(string repositoryDirectory, string name, string url, CancellationToken cancellationToken)
    {
        List<string> results = [];
        Task<string> task = RunAsync($"remote add {name} {url}", repositoryDirectory, cancellationToken);
        task.Wait(cancellationToken);
        string[] lines = task.Result.Split("\r\n");
        foreach (string line in lines)
            results.Add(line);
        return new(results);
    }

    internal static ReadOnlyCollection<string> PushBranch(string repositoryDirectory, string remote, string name, CancellationToken cancellationToken)
    {
        List<string> results = [];
        Task<string> task = RunAsync($"push {remote} {name}", repositoryDirectory, cancellationToken);
        task.Wait(cancellationToken);
        string[] lines = task.Result.Split("\r\n");
        foreach (string line in lines)
            results.Add(line);
        return new(results);
    }

}