const util = require('util')
const os = require('os')
const childProcess = require('child_process')
const _exec = childProcess.exec
const exec = util.promisify(_exec)
const PowerShell = require('node-powershell')

class SystemShell {
  static async exec (cmds, args) {
    throw new Error('You have to implement the method exec!')
  }
}

class LinuxSystemShell extends SystemShell {
  static async exec (cmds) {
    if (cmds instanceof String) {
      cmds = [cmds]
    }
    for (const cmd of cmds) {
      await exec(cmd)
    }
  }
}

class DarwinSystemShell extends SystemShell {
  static async exec (cmds) {
    if (cmds instanceof String) {
      cmds = [cmds]
    }
    for (const cmd of cmds) {
      await exec(cmd)
    }
  }
}

class WindowsSystemShell extends SystemShell {
  static async exec (cmds, args = { }) {
    let { type } = args
    type = type || 'ps'
    if (typeof cmds === 'string') {
      cmds = [cmds]
    }
    if (type === 'ps') {
      const ps = new PowerShell({
        executionPolicy: 'Bypass',
        noProfile: true
      })

      for (const cmd of cmds) {
        ps.addCommand(cmd)
      }

      try {
        const ret = await ps.invoke()
        // console.log('ps complete', cmds)
        return ret
      } finally {
        ps.dispose()
      }
    } else {
      let compose = 'chcp 65001  '
      for (const cmd of cmds) {
        compose += ' && ' + cmd
      }
      // compose += '&& exit'
      const ret = await childExec(compose)
      // console.log('cmd complete:', compose)
      return ret
    }
  }
}

function childExec (composeCmds) {
  return new Promise((resolve, reject) => {
    const childProcess = require('child_process')
    childProcess.exec(composeCmds, function (error, stdout, stderr) {
      if (error) {
        console.error('cmd 命令执行错误：', composeCmds, error, stderr)
        reject(error)
      } else {
        // console.log('cmd 命令完成：', stdout)
        resolve(stdout)
      }
      // console.log('关闭 cmd')
      // ps.kill('SIGINT')
    })
  })
}

function getSystemShell () {
  switch (getSystemPlatform()) {
    case 'mac':
      return DarwinSystemShell
    case 'linux':
      return LinuxSystemShell
    case 'windows':
      return WindowsSystemShell
    case 'unknown os':
    default:
      throw new Error(`UNKNOWN OS TYPE ${os.platform()}`)
  }
}
function getSystemPlatform () {
  switch (os.platform()) {
    case 'darwin':
      return 'mac'
    case 'linux':
      return 'linux'
    case 'win32':
    case 'win64':
      return 'windows'
    case 'unknown os':
    default:
      throw new Error(`UNKNOWN OS TYPE ${os.platform()}`)
  }
}

async function execute (executor, args) {
  return executor[getSystemPlatform()](getSystemShell().exec, args)
}

module.exports = {
  getSystemShell,
  getSystemPlatform,
  execute
}
