// 'sanbashi' is a word in Japanese that refers to a pier or dock, usually very large in size, such as found in Tokyo's O-daiba region

let Glob = require('glob')
let Path = require('path')
let Inquirer = require('inquirer')
let os = require('os')
const Child = require('child_process')
const debug = require('./debug')

const DOCKERFILE_REGEX = /\bDockerfile(.\w*)?$/
let Sanbashi = function () {}

Sanbashi.getDockerfiles = function (rootdir, recursive) {
  let match = recursive ? './**/Dockerfile?(.)*' : 'Dockerfile*'
  let dockerfiles = Glob.sync(match, {
    cwd: rootdir,
    nonull: false,
    nodir: true
  })
  if (recursive) {
    dockerfiles = dockerfiles.filter(df => df.match(/Dockerfile\.[\w]+$/))
  } else {
    dockerfiles = dockerfiles.filter(df => df.match(/Dockerfile$/))
  }
  return dockerfiles.map(file => Path.join(rootdir, file))
}

Sanbashi.getJobs = function (resourceRoot, dockerfiles) {
  return dockerfiles
  // convert all Dockerfiles into job Objects
    .map((dockerfile) => {
      let match = dockerfile.match(DOCKERFILE_REGEX)
      if (!match) return
      let proc = (match[1] || '.standard').slice(1)
      return {
        name: proc,
        resource: `${resourceRoot}/${proc}`,
        dockerfile: dockerfile,
        postfix: Path.basename(dockerfile) === 'Dockerfile' ? 0 : 1,
        depth: Path.normalize(dockerfile).split(Path.sep).length
      }
    })
    // prefer closer Dockerfiles, then prefer Dockerfile over Dockerfile.web
    .sort((a, b) => {
      return a.depth - b.depth || a.postfix - b.postfix
    })
    // group all Dockerfiles for the same process type together
    .reduce((jobs, job) => {
      jobs[job.name] = jobs[job.name] || []
      jobs[job.name].push(job)
      return jobs
    }, {})
}

Sanbashi.chooseJobs = async function (jobs) {
  let chosenJobs = []
  for (let processType in jobs) {
    let group = jobs[processType]
    if (group.length > 1) {
      let prompt = {
        type: 'list',
        name: processType,
        choices: group.map(j => j.dockerfile),
        message: `Found multiple Dockerfiles with process type ${processType}. Please choose one to build and push `
      }
      let answer = await Inquirer.prompt(prompt)
      chosenJobs.push(group.find(o => o.dockerfile === answer[processType]))
    } else {
      chosenJobs.push(group[0])
    }
  }
  return chosenJobs
}

Sanbashi.filterByProcessType = function (jobs, procs) {
  let filteredJobs = {}
  procs.forEach((processType) => {
    filteredJobs[processType] = jobs[processType]
  })
  return filteredJobs
}

Sanbashi.buildImage = function (dockerfile, resource, buildArg, path) {
  let cwd = path || Path.dirname(dockerfile)
  let args = ['build', '-f', dockerfile, '-t', resource]

  for (let i = 0; i < buildArg.length; i++) {
    if (buildArg[i].length !== 0) {
      args.push('--build-arg')
      args.push(buildArg[i])
    }
  }

  args.push(cwd)
  return Sanbashi.cmd('docker', args)
}

Sanbashi.pushImage = function (resource) {
  let args = ['push', resource]
  return Sanbashi.cmd('docker', args)
}

Sanbashi.pullImage = function (resource) {
  let args = ['pull', resource]
  return Sanbashi.cmd('docker', args)
}

Sanbashi.runImage = function (resource, command, port) {
  let args = ['run', '--user', os.userInfo().uid, '-e', `PORT=${port}`]
  if (command === '') {
    args.push(resource)
  } else {
    args.push('-it', resource, command)
  }
  return Sanbashi.cmd('docker', args)
}

Sanbashi.version = function () {
  return Sanbashi
    .cmd('docker', ['version', '-f', '{{.Client.Version}}'], {output: true})
    .then(version => version.split(/\./))
    .then(([major, minor]) => [parseInt(major) || 0, parseInt(minor) || 0]) // ensure exactly 2 components
}

Sanbashi.imageID = function (tag) {
  return Sanbashi
    .cmd('docker', ['inspect', tag, '--format={{.Id}}'], {output: true})
    .then(id => id.trimRight()) // Trim the new line at the end of the string
}

Sanbashi.cmd = function (cmd, args, options = {}) {
  debug(cmd, args)
  let stdio = [process.stdin, process.stdout, process.stderr]
  if (options.input) {
    stdio[0] = 'pipe'
  }
  if (options.output) {
    stdio[1] = 'pipe'
  }

  return new Promise((resolve, reject) => {
    let child = Child.spawn(cmd, args, {stdio: stdio})

    if (child.stdin) {
      child.stdin.end(options.input)
    }
    let stdout
    if (child.stdout) {
      stdout = ''
      child.stdout.on('data', (data) => {
        stdout += data.toString()
      })
    }
    child.on('error', (err) => {
      if (err.code === 'ENOENT' && err.path === 'docker') {
        reject(new Error(`Cannot find docker, please ensure docker is installed.
        If you need help installing docker, visit https://docs.docker.com/install/#supported-platforms`))
      } else {
        reject(err)
      }
    })
    child.on('exit', (code, signal) => {
      if (signal || code) reject(new Error(signal || code))
      else resolve(stdout)
    })
  })
}

module.exports = Sanbashi
