// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using System;
using System.IO;
using System.Text;

namespace Microsoft.DotNet.Build.Tasks
{
    public class GenerateRunScript : Task
    {
        [Required]
        public string[] SetCommands { get; set; }

        [Required]
        public string[] RunCommands { get; set; }

        [Required]
        public string TemplatePath { get; set; }

        [Required]
        public string OutputPath { get; set; }

        public override bool Execute()
        {
            if (RunCommands.Length == 0)
            {
                Log.LogError("Please provide at least one test command to execute via the RunCommands property.");
                return false;
            }

            if (!File.Exists(TemplatePath))
            {
                Log.LogError($"Runner script template {TemplatePath} was not found.");
                return false;
            }

            string templateContent = File.ReadAllText(TemplatePath);
            Directory.CreateDirectory(Path.GetDirectoryName(OutputPath));

            Log.LogMessage($"Run commands = {string.Join(Environment.NewLine, RunCommands)}");

            string extension = Path.GetExtension(Path.GetFileName(OutputPath)).ToLowerInvariant();
            switch (extension)
            {
                case ".sh":
                case ".cmd":
                case ".bat":
                    WriteRunScript(templateContent, extension);
                    break;
                default:
                    Log.LogError($"Generating runner scripts with extension '{extension}' is not supported.");
                    return false;
            }

            return true;
        }

        private void WriteRunScript(string templateContent, string extension)
        {
            bool isUnix = extension == ".sh";
            string lineFeed = isUnix ? "\n" : "\r\n";
            string[] newlineSeparator = new string[] { Environment.NewLine };

            var setCommandsBuilder = new StringBuilder();
            for (int i = 0; i < SetCommands.Length; i++)
            {
                string[] setCommandsSplit = SetCommands[i].Split(newlineSeparator, StringSplitOptions.None);
                for (int j = 0; j < setCommandsSplit.Length; j++)
                {
                    setCommandsBuilder.Append(setCommandsSplit[j]);
                    if ((j < setCommandsSplit.Length - 1) || (i < SetCommands.Length - 1))
                    {
                        setCommandsBuilder.Append(lineFeed);
                    }
                }
            }
            templateContent = templateContent.Replace("[[SetCommands]]", setCommandsBuilder.ToString());

            var runCommandsBuilder = new StringBuilder();
            for (int i = 0; i < RunCommands.Length; i++)
            {
                string[] runCommandsSplit = RunCommands[i].Split(newlineSeparator, StringSplitOptions.None);
                for (int j = 0; j < runCommandsSplit.Length; j++)
                {
                    runCommandsBuilder.Append(runCommandsSplit[j]);
                    if ((j < runCommandsSplit.Length - 1) || (i < RunCommands.Length - 1))
                    {
                        runCommandsBuilder.Append(lineFeed);
                    }
                }
            }
            templateContent = templateContent.Replace("[[RunCommands]]", runCommandsBuilder.ToString());


            var setCommandEchoesBuilder = new StringBuilder();
            foreach (string setCommand in SetCommands)
            {
                foreach (string setCommandOneLine in setCommand.Split(newlineSeparator, StringSplitOptions.None))
                {
                    setCommandEchoesBuilder.Append($"echo {SanitizeEcho(setCommandOneLine,isUnix)}{lineFeed}");
                }
            }
            templateContent = templateContent.Replace("[[SetCommandsEcho]]", setCommandEchoesBuilder.ToString());

            var runCommandEchoesBuilder = new StringBuilder();
            foreach (string runCommand in RunCommands)
            {
                foreach (string runCommandOneLine in runCommand.Split(newlineSeparator, StringSplitOptions.None))
                {
                    runCommandEchoesBuilder.Append($"echo {SanitizeEcho(runCommandOneLine,isUnix)}{lineFeed}");
                }
            }
            templateContent = templateContent.Replace("[[RunCommandsEcho]]", runCommandEchoesBuilder.ToString());


            if (isUnix)
            {
                // Just in case any Windows EOLs have made it in by here, clean any up.
                templateContent = templateContent.Replace("\r\n", "\n");
            }

            using (StreamWriter sw = new StreamWriter(new FileStream(OutputPath, FileMode.Create)))
            {
                sw.NewLine = lineFeed;
                sw.Write(templateContent);
                sw.WriteLine();
            }

            Log.LogMessage($"Wrote {extension} run script to {OutputPath}");
        }

        private static string SanitizeEcho(string command, bool isUnix){
            // Escape backtick and question mark characters to avoid running commands instead of echo'ing them.
            string sanitizedRunCommand = command.Replace("`", "\\`")
                                                    .Replace("?", "\\")
                                                    .Replace("\r","")
                                                    .Replace("\n"," ")
                                                    .Replace("&", "^&")
                                                    .Replace(">", "^>");

            if (isUnix)
            {
                // Remove parentheses and quotes from echo command before wrapping it in quotes to avoid errors on Linux.
                sanitizedRunCommand = "\"" + sanitizedRunCommand.Replace("\"", "")
                                    .Replace("(", "")
                                    .Replace(")", "") + "\"";
            }
            return sanitizedRunCommand;
        }
    }
}
