const { spawn, spawnSync } = require('child_process');
const fs = require('fs')

function nodeShellExec() {

    var args = Array.from(arguments);
    if(args.length > 1){
      var opts = args[2] = args[2] || {}
      opts.title ? null : opts.title = `${args[0]} ${args[1]  }`
      
    }
    else {
      var opts = {
        title : `${args[0]}`
        // , inherit: true, shell: true
      }
    }
    opts.stdin = 'pipe';
    
    // // const spawn = require('child_process').spawn;
    // const s = spawn(
    //     'C:\\Program Files\\Git\\bin\\sh.exe'
    // , ['notepad', 'index'], { cwd: __dirname });



    // var interval = null;
    // var t = setTimeout(function(){
    //   interval = setInterval(function(){
      
    //     console.log('Awaiting close : ' + child.spawnargs)
  
    //   }, 1500)
    //   // console.log('Awaiting close : ' + child.spawnargs)

    // }, 0)

    // child.on('close', (code) => {
    //   console.error('Prematurely closed even before promise...')
    // })
    // D:\chess\instances\elixir_01\elxr/.elxr/run-1630002739610/download.bat https://github.com/git-for-windows/git/releases/download/v2.33.0.windows.2/Git-2.33.0.2-64-bit.exe D:\\chess\\instances\\elixir_01\\elxr/Downloads/Git-2.33.0.2-64-bit.exe
    // D:\\chess\\instances\\elixir_01\\elxr/.elxr/run-1630002923584/download.bat https://github.com/git-for-windows/git/releases/download/v2.33.0.windows.2/Git-2.33.0.2-64-bit.exe D:\\chess\\instances\\elixir_01\\elxr/Downloads/Git-2.33.0.2-64-bit.exe
  
    // console.dir(args[2])
    console.dir(`${args[0]} ${args[1].join(' ') }`)

    const child = spawn(...args);
    var p = new Promise(function(resolve, reject){
      // console.log(...args)
      if(!opts.detached) {
        var messages = []; // PB : TODO -- Explore stream for Task level  aggregation to prevent interleaved messages from multiple tasks...
        var success = true;
        
        if(opts.stdio !== 'ignore') {
          child.stdout.setEncoding('utf8');
          child.stderr.setEncoding('utf8');
          child.stdout.on('data', (chunk) => { 
            chunk.trim() === '' ? null : messages.push(chunk); /* console.log('d: ' + chunk) */ 
            process.stdout.write( chunk )
          });
          child.on('error', (chunk) => { success = false; messages.push(chunk); /* console.error('e: ' + chunk) */ 
            console.log('Error exit not handled.')
          }  );
          child.stderr.on('data', (chunk) => { 
            if(messages.join('').indexOf('fatal: not a git repository') > -1) opts.haserrors = true; 
            messages.push(chunk); 
            // process.stdout.write( chunk )
            // console.error('stderr e: ' + chunk)
          });
        }
        child.on('close', (code) => {
          // console.log('Proper close was fired')
          var logEntry = { code, success }
          if(+code !== 0 || opts.haserrors) { 
            success = false; logEntry = { messages, result: `${opts.title} exited with code ${code}`, success, code }
          };
          if(opts.stdio !== 'ignore') {
            logEntry = { result: `${opts.title} exited with code ${code}`, messages,  code }
            logEntry.success = success;
            if(opts.evaluateResult) logEntry = opts.evaluateResult(success, logEntry);
            if(opts.runas){
              // success ? logEntry.success = true : null;
              fs.writeFileSync('run.log', ', ' + JSON.stringify(logEntry), {'flag':'a+'} )
            }
            else {
              // console.log( messages.join('') )
              // process.stdout.write( JSON.stringify(logEntry) )
            }
          }
          else if(opts.evaluateResult) {
            try { logEntry = opts.evaluateResult(false, logEntry); }
            catch(e){ reject(e) }
          }
          // clearInterval(interval)
          if(code !== 0 || opts.haserrors) {
            args[2].benign || args[2].ignorefailures ? (logEntry.benign = true, logEntry.ignorefailures = true) : null 
            return reject(logEntry)
          }
          resolve(logEntry)
        });
      }
      else {
        child.unref()
        // clearInterval(interval)
        resolve(true);
      }
    });
    p.process = child; 
    return p;
}

var prompt = function(choices, label, defaultchoice, selectedchoice){
  // prompt accepts either an array or an object as choices.
  var choices = choices || [];
  defaultchoice = defaultchoice || choices[0] || selectedchoice || choices[Object.keys(choices)[0]]
  var ci = 0;
  return this.prompter.ask(
      `${label} \n` + Object.keys(choices).map(
          choice => { 
            ++ci; var choice_label = isNaN(+choice) ? `${ci}) (${choice})` : ci + ')'; 
            return `  ${choice_label} ${choices[choice]} `
          }).join('\n') 
        + `\n  d) default ( <= ${ defaultchoice || choices[0]} ) : `
        + `\n  selected ( <= ${ selectedchoice || defaultchoice || choices[0]} ) : `
    ).then(choice => {
      // propName = promptable.interpret(propValue)
      if(!choice && selectedchoice) return selectedchoice;
      if(!choice) return defaultchoice || choices[0];
      if(choice && isNaN(+choice)) {
        if(choice === 'd') return defaultchoice || choices[0];
        try {
          return JSON.parse(choice);
        }
        catch(e) {
          // console.error(e)
          return choice // Treat it as a string if it is not parsable
        }
      }
      return choices[(+choice) - 1];
    })
} 

const readline = require("readline");
var cli = {
    nodeShellExec
  , get prompter() {
    var prompt_interface = { 
      ask : function(q){
        // Needs to be serialized. Parallel asks are not possible.
        const clii = readline.createInterface({ input: process.stdin, output: process.stdout });
        return new Promise((resolve, reject)=>{
          clii.question(q, (answer)=>{ 
            try {
              clii.close();
              console.log("readline.createInterface closed");
              resolve(answer) 
            }
            catch(e) {
              reject(e)
            }
          })
        })
      }
    }
    return prompt_interface
  }
  , prompt
  , createTask(task, str){

    var tasks = {
      getTaskCheckExists : (str)=>{
        return (command, options) => {
          options = options || {}
          return () => {
            var p = nodeShellExec.apply(null, [str, [command]])
            if (options.ignorefailures) {
              return p.then(() => { return true }).catch(e => { // Ignore. Not a major error.
                return false;
              })
            }
            else return p.then(() => { return true });
          }
        }
      }
    }
    return tasks[task](str)
  }
}

module.exports = cli