# Source: commandsBlob/commands/system.js

    /**
    * @file system.js
    * @module system
    * @description Contains all of the system level commands.
    * @requires module:commandBroker
    * @requires module:ruleBroker
    * @requires module:workflowBroker
    * @requires module:configurator
    * @requires module:loggers
    * @requires module:data
    * @requires {@link https://www.npmjs.com/package/@haystacks/constants|@haystacks/constants}
    * @requires {@link https://www.npmjs.com/package/figlet|figlet}
    * @requires {@link https://www.npmjs.com/package/path|path}
    * @author Seth Hollingsead
    * @date 2022/04/20
    * @copyright Copyright © 2022-… by Seth Hollingsead. All rights reserved
    */

    // Internal imports
    import commandBroker from '../../brokers/commandBroker.js';
    import ruleBroker from '../../brokers/ruleBroker.js';
    import workflowBroker from '../../brokers/workflowBroker.js';
    import configurator from '../../executrix/configurator.js';
    import loggers from '../../executrix/loggers.js';
    import D from '../../structures/data.js';
    // External imports
    import hayConst from '@haystacks/constants';
    import figlet from 'figlet';
    import path from 'path';

    const {bas, biz, cfg, msg, sys, wrd} = hayConst;
    const baseFileName = path.basename(import.meta.url, path.extname(import.meta.url));
    // commandsBlob.commands.system.
    const namespacePrefix = sys.ccommandsBlob + bas.cDot + wrd.ccommands + bas.cDot + baseFileName + bas.cDot;

    /**
    * @function echoCommand
    * @description Returns the input as the output without any changes.
    * @param {array<boolean|string|integer>} inputData String that should be echoed.
    * inputData[0] === 'echoCommand'
    * @param {string} inputMetaData Not used for this business rule.
    * @return {array<boolean,string|integer|boolean|object|array>} An array with a boolean True or False value to
    * indicate if the application should exit or not exit, followed by the command output.
    * @author Seth Hollingsead
    * @date 2022/02/04
    */
    function echoCommand(inputData, inputMetaData) {
      let functionName = echoCommand.name;
      loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
      loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + JSON.stringify(inputData));
      loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + inputMetaData);
      let returnData = [true, ''];
      let errorMessage = '';
      if (inputData) {
        inputData.shift();
        console.log(inputData.join(bas.cSpace));
        returnData[1] = inputData.join(bas.cSpace);
      } else {
        // Nothing to echo.
        errorMessage = msg.cNothingToEcho;
        console.log(errorMessage);
        returnData[1] = errorMessage;
      }
      loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
      loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
      return returnData;
    }

    /**
    * @function exit
    * @description Returns false so the entire application can exit.
    * @param {array<boolean|string|integer>} inputData Not used for this command.
    * inputData[0] === 'exit'
    * @param {string} inputMetaData Not used for this command.
    * @return {array<boolean,string|integer|boolean|object|array>} An array with a boolean False value to
    * indicate if the application exit, followed by the command output.
    * @author Seth Hollingsead
    * @date 2022/02/04
    */
    function exit(inputData, inputMetaData) {
      let functionName = exit.name;
      loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
      loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + JSON.stringify(inputData));
      loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + inputMetaData);
      let returnData = [false, true];
      loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
      loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
      return returnData;
    }

    /**
    * @function version
    * @description Displays the current version number for the current application.
    * @param {array<boolean|string|integer>} inputData Not used for this command.
    * inputData[0] = 'version'
    * inputData[1] === 'application|framework' (optional)
    * @param {string} inputMetaData Not used for this command.
    * @return {array<boolean,string|integer|boolean|object|array>} An array with a boolean True or False value to
    * indicate if the application should exit or not exit, followed by the command output.
    * @author Seth Hollingsead
    * @date 2022/02/04
    */
    function version(inputData, inputMetaData) {
      let functionName = version.name;
      loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
      loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + JSON.stringify(inputData));
      loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + inputMetaData);
      let returnData = [true, ''];
      let configVersion = '';
      let appContext = '';
      if (inputData.length === 2) {
         appContext = inputData[1];
         if (appContext.toUpperCase() === wrd.cAPPLICATION) {
            configVersion = configurator.getConfigurationSetting(wrd.csystem, sys.cApplicationVersionNumber);
         } else if (appContext.toUpperCase() === wrd.cFRAMEWORK) {
            configVersion = configurator.getConfigurationSetting(wrd.csystem, sys.cFrameworkVersionNumber);
         } else {
            configVersion = configurator.getConfigurationSetting(wrd.csystem, sys.cApplicationVersionNumber);
         }
      } else {
        configVersion = configurator.getConfigurationSetting(wrd.csystem, sys.cApplicationVersionNumber);
      }
      console.log(configVersion);
      returnData[1] = configVersion;
      loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
      loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
      return returnData;
    }

    /**
    * @function about
    * @description Displays the message about the current application.
    * @param {array<boolean|string|integer>} inputData Not used for this command.
    * inputData[0] === 'about'
    * inputData[1] === 'application|framework' (optional)
    * @param {string} inputMetaData Not used for this command.
    * @return {array<boolean,string|integer|boolean|object|array>} An array with a boolean True or False value to
    * indicate if the application should exit or not exit, followed by the command output.
    * @author Seth Hollingsead
    * @date 2022/02/04
    */
    function about(inputData, inputMetaData) {
      let functionName = about.name;
      loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
      loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + JSON.stringify(inputData));
      loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + inputMetaData);
      let returnData = [true, ''];
      let configDescription = '';
      let appContext = '';
      if (inputData.length === 2) {
        appContext = inputData[1];
        if (appContext.toUpperCase() === wrd.cAPPLICATION) {
          configDescription = configurator.getConfigurationSetting(wrd.csystem, sys.cApplicationDescription);
        } else if (appContext.toUpperCase() === wrd.cFRAMEWORK) {
          configDescription = configurator.getConfigurationSetting(wrd.csystem, sys.cFrameworkDescription);
        } else {
          configDescription = configurator.getConfigurationSetting(wrd.csystem, sys.cApplicationDescription);
        }
      } else {
        configDescription = configurator.getConfigurationSetting(wrd.csystem, sys.cApplicationDescription);
      }
      console.log(configDescription);
      returnData[1] = configDescription;
      loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
      loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
      return returnData;
    }

    /**
    * @function name
    * @description Displays the name of the current application in standard font format, nothing special.
    * Optional argument to output in figlet font.
    * @param {array<boolean|string|integer>} inputData An array that could really contain anything depending
    * on what the user entered, but the function converts and filters out for a boolean
    * True or False value internally to the function.
    * inputData[0] === 'name'
    * inputData[1] === 'application|framework' (optional)
    * inputData[2] === 'true|false' (optional)
    * @param {string} inputMetaData Not used for this command.
    * @return {array<boolean,string|integer|boolean|object|array>} An array with a boolean True or False value to
    * indicate if the application should exit or not exit, followed by the command output.
    * @author Seth Hollingsead
    * @date 2022/02/04
    */
    function name(inputData, inputMetaData) {
      let functionName = name.name;
      loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
      loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + JSON.stringify(inputData));
      loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + inputMetaData);
      let returnData = [true, ''];
      let reportedName = '';
      let figletFont = '';
      let appContext = '';
      let useFancyFont = false;
      if (inputData.length === 2) {
        appContext = inputData[1];
      } // End-if (inputData.length === 2)
      if (inputData.length === 3) {
        appContext = inputData[1];
        useFancyFont = ruleBroker.processRules([inputData[2], ''], [biz.cstringToDataType]);
      } // End-if (inputData.length === 3)
      if (appContext !== '') {
        if (appContext.toUpperCase() === wrd.cAPPLICATION) {
          reportedName = configurator.getConfigurationSetting(wrd.csystem, sys.cApplicationName);
        } else if (appContext.toUpperCase() === wrd.cFRAMEWORK) {
          reportedName = configurator.getConfigurationSetting(wrd.csystem, sys.cFrameworkName);
        } else {
          reportedName = configurator.getConfigurationSetting(wrd.csystem, sys.cApplicationName);
        }
      } else {
        reportedName = configurator.getConfigurationSetting(wrd.csystem, sys.cApplicationName);
      }
      if (useFancyFont === true) {
        figletFont = configurator.getConfigurationSetting(wrd.csystem, cfg.cfigletFont);
        console.log(figlet.textSync(reportedName, {font: figletFont, horizontalLayout: sys.cfull}));
      } else {
        console.log(reportedName);
      }
      returnData[1] = reportedName;
      loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
      loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
      return returnData;
    }

    /**
    * @function clearScreen
    * @description Clears all data from the console cache by printing a bunch of blank lines to the screen.
    * @param {string} inputData Not used for this command.
    * @param {string} inputMetaData Not used for this command.
    * @return {array<boolean,string|integer|boolean|object|array>} An array with a boolean True or False value to
    * indicate if the application should exit or not exit, followed by the command output.
    * @author Seth Hollingsead
    * @date 2022/02/04
    */
    // eslint-disable-next-line no-unused-vars
    function clearScreen(inputData, inputMetaData) {
      let functionName = clearScreen.name;
      loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
      // loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + JSON.stringify(inputData));
      // loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + JSON.stringify(inputMetaData));
      let returnData = [true, {}];
      // console.clear(); // This will clear the screen, but not the cache, you can still scroll up and see the previous commands.
      // process.stdout.write('\u001B[2J\u-001B[0;0f'); // Same as above.
      // eslint-disable-next-line no-undef
      process.stdout.write('\u001b[H\u001b[2J\u001b[3J');
      returnData[1] = true;
      loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
      loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
      return returnData;
    }

    /**
    * @function help
    * @description Displays all the information about all of the commands in the system,
    * including both system defined commands and client defined commands.
    * @param {array<boolean|string|integer>} inputData Not used for this command.
    * inputData[0] = 'help'
    * @param {string} inputMetaData Not used for this command.
    * @return {array<boolean,string|integer|boolean|object|array>} An array with a boolean True or False value to
    * indicate if the application should exit or not exit, followed by the command output.
    * @author Seth Hollingsead
    * @date 2022/02/22
    */
    // eslint-disable-next-line no-unused-vars
    function help(inputData, inputMetaData) {
      let functionName = help.name;
      loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
      let returnData = [true, []];
      let errorMessage = '';
      if (inputData.length > 1) {
        // calling getCommandNamespaceDataObject() function,
        // because the user entered some namespace we should look for!
        let namespaceCommandsData = commandBroker.getCommandNamespaceDataObject(undefined, inputData[1]);
        // namespaceCommandsData is:
        loggers.consoleLog(namespacePrefix + functionName, msg.cnamespaceCommandsDataIs + JSON.stringify(namespaceCommandsData));
        if (namespaceCommandsData === false) {
          // ERROR: The command namespace was not found.
          // Please make sure you have entered the correct name and try again.
          errorMessage = msg.chelpCommandMessage01 + bas.cSpace + msg.chelpCommandMessage02;
          console.log(errorMessage);
          returnData[1] = errorMessage;
        } else {
          // NOW call getAllCommandAliasData with the above found data!
          loggers.consoleLog(namespacePrefix + functionName, msg.chelpCommandMessage03);
          let flattenedNamespaceCommandAliasData = commandBroker.getAllCommandAliasData(namespaceCommandsData);
          loggers.consoleTableLog(baseFileName + bas.cDot + functionName, flattenedNamespaceCommandAliasData[0], [wrd.cName, wrd.cDescription]);
          returnData[1] = ruleBroker.processRules([flattenedNamespaceCommandAliasData[0], ''], [biz.carrayDeepClone]);
        }
      } else {
        let allCommandAliasFlatData = commandBroker.getAllCommandAliasData(D[sys.cCommandsAliases]);
        returnData[1] = ruleBroker.processRules([allCommandAliasFlatData, ''], [biz.carrayDeepClone]);
        // allCommandAliasFlatData is:
        loggers.consoleLog(namespacePrefix + functionName, msg.callCommandAliasFlatDataIs + JSON.stringify(allCommandAliasFlatData[0]));
        loggers.consoleTableLog(baseFileName + bas.cDot + functionName, allCommandAliasFlatData[0], [wrd.cName, wrd.cDescription]);
      }
      loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
      loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
      return returnData;
    }

    /**
    * @function workflowHelp
    * @description Displays all the information about all the workflows in the system,
    * including both system defined workflows & client defined workflows.
    * @param {array<boolean|string|integer>} inputData Not used for this command.
    * inputData[0] = 'workflowHelp'
    * @param {string} inputMetaData Not used for this command.
    * @return {array<boolean,string|integer|boolean|object|array>} An array with a boolean True or False value to
    * indicate if the application should exit or not exit, followed by the command output.
    * @author Seth Hollingsead
    * @date 2022/02/22
    */
    function workflowHelp(inputData, inputMetaData) {
      let functionName = workflowHelp.name;
      loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function);
      loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + JSON.stringify(inputData));
      loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + inputMetaData);
      let returnData = [true, []];
      let errorMessage = '';
      // The old way of printing out all the workflows, when it was a flat data structure.
      // loggers.consoleTableLog(baseFileName + bas.cDot + functionName, D[sys.cCommandWorkflows][wrd.cWorkflows], [wrd.cName]);
      if (inputData.length > 1) {
        // calling getWorkflowNamespaceDataObject() function,
        // because the user entered some namespace we should look for!
        loggers.consoleLog(namespacePrefix + functionName, msg.cworkfowHelpMessage01 + msg.cworkfowHelpMessage02);
        let namespaceWorkflowData = workflowBroker.getWorkflowNamespaceDataObject(undefined, inputData[1]);
        // namespaceWorkflowData is:
        loggers.consoleLog(namespacePrefix + functionName, msg.cnamespaceWorkflowDataIs + JSON.stringify(namespaceWorkflowData));
        if (namespaceWorkflowData === false) {
          // ERROR: The workflow namespace was not found.
          // Please make sure you have entered the correct name and try again.
          errorMessage = msg.cworkflowHelpCommandMessage01 + bas.cSpace + msg.chelpCommandMessage02;
          console.log(errorMessage);
          returnData[1] = errorMessage;
        } else {
          // NOW call getAllWorkflows with the above found data!
          loggers.consoleLog(namespacePrefix + functionName, msg.cworkfowHelpMessage03);
          let flattenedNamespaceWorkflowData = workflowBroker.getAllWorkflows(namespaceWorkflowData);
          loggers.consoleTableLog(baseFileName + bas.cDot + functionName, flattenedNamespaceWorkflowData);
          returnData[1] = ruleBroker.processRules([flattenedNamespaceWorkflowData, ''], [biz.carrayDeepClone]);
        }
      } else {
        // User did not enter any parameters,
        // just call getAllWorkflows functions with no input,
        // will return all and print all.
        loggers.consoleLog(namespacePrefix + functionName, msg.cworkfowHelpMessage04 + msg.cworkfowHelpMessage05 + msg.cworkfowHelpMessage06);
        let allWorkflowData = workflowBroker.getAllWorkflows();
        loggers.consoleLog(namespacePrefix + functionName, msg.callWorkflowDataIs + JSON.stringify(allWorkflowData));
        loggers.consoleTableLog(baseFileName + bas.cDot + functionName, allWorkflowData);
        returnData[1] = ruleBroker.processRules([allWorkflowData, ''], [biz.carrayDeepClone]);
      }
      loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData));
      loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function);
      return returnData;
    }

    export default {
      echoCommand,
      exit,
      version,
      about,
      name,
      clearScreen,
      help,
      workflowHelp
    };