var fs = require('fs')
var ini = require('ini')
var path = require('path')
var chalk = require('chalk')
var Git = require('git-tools')

var validate = require('./lib/validate')
var sanitize = require('./lib/sanitize')
var defaults = require('./lib/defaults')

// If there is a path in process.env.PATH that looks like this:
// path = prefix + suffix (where suffix is the function argument)
// then slice off the suffix and return the prefix, otherwise undefiend
function sliceEnvPath (suffix) {
  var p = process.env.PATH.split(':').filter(
    function (p) {return p.endsWith(suffix)}
  )

  if (p.length === 1) {
    p = p[0].split(path.sep)

    p = p.slice(0, p.length - suffix.split(path.sep).length)
    return p.join(path.sep)
  }

  return undefined
}

// Need to find the path to the project that is installing or running
// commitplease. Previously, process.cwd() made the job easy but its
// output changed with node v8.1.2 (at least compared to 7.10.0)
function getProjectPath () {
  // Rely on npm to inject some path into PATH; However, the injected
  // path can both be relative or absolute, so add extra path.resolve()

  // During npm install, npm will inject a path that ends with
  // commitplease/node_modules/.bin into process.env.PATH
  var p = sliceEnvPath(
    path.join('node_modules', 'commitplease', 'node_modules', '.bin')
  )

  if (p !== undefined) {
    return path.resolve(p)
  }

  // During npm run, npm will inject a path that ends with
  // node_modules/.bin into process.env.PATH
  p = sliceEnvPath(path.join('node_modules', '.bin'))

  if (p !== undefined) {
    return path.resolve(p)
  }

  // During git commit there will be no process.env.PATH modifications
  // So, assume we are being run by git which will set process.cwd()
  // to the root of the project as described in the manual:
  // https://git-scm.com/docs/githooks/2.9.0
  return path.resolve(process.cwd())
}

function getOptions () {
  var projectPath = getProjectPath()

  var pkg = path.join(projectPath, 'package.json')
  var npm = path.join(projectPath, '.npmrc')

  pkg = fs.existsSync(pkg) && require(pkg) || {}
  npm = fs.existsSync(npm) && ini.parse(fs.readFileSync(npm, 'utf8')) || {}

  pkg = pkg.commitplease || {}
  npm = npm.commitplease || {}

  var options = Object.assign(pkg, npm)

  var base = {
    'projectPath': projectPath,
    'oldMessagePath': defaults.oldMessagePath,
    'oldMessageSeconds': defaults.oldMessageSeconds
  }

  if (options === undefined ||
      options.style === undefined ||
      options.style === 'jquery') {
    return Object.assign(base, defaults.jquery, options)
  } else if (options.style === 'angular') {
    return Object.assign(base, defaults.angular, options)
  }

  console.error(chalk.red(
    'Style ' + options.style + ' is not recognised\n' +
    'Did you mistype it in package.json?'
  ))

  process.exit(1)
}

function runValidate (message, options) {
  var errors = validate(sanitize(message), options)

  if (errors.length) {
    console.error('Invalid commit message, please fix:\n')
    console.error(chalk.red('- ' + errors.join('\n- ')))
    console.error()
    console.error('Commit message was:')
    console.error()
    console.error(chalk.green(sanitize(message)))

    console.error('\nSee ' + options.guidelinesUrl)

    // save a poorly formatted message and reuse it at a later commit
    fs.writeFileSync(defaults.oldMessagePath, message)

    process.exit(1)
  }
}

module.exports = function () {
  var argv = process.argv.slice(2)
  var help = argv.some(function (value) {
    if (value === '-h' || value === '--help') {
      return true
    }
  })

  if (argv.length > 1 || help) {
    console.log(
      'Usage: commitplease [committish]\n\n' +
      'committish      a commit range passed to git log\n\n' +
      'Examples:\n\n' +
      '1. Check all commits on branch master:\n' +
      'commitplease master\n\n' +
      '2. Check all commits on branch feature but not on master:\n' +
      'commitplease master..feature\n\n' +
      '3. Check the latest 1 commit (n works too):\n' +
      'commitplease -1\n\n' +
      '4. Check all commits between 84991d and 2021ce\n' +
      'commitplease 84991d..2021ce\n\n' +
      '5. Check all commits starting with 84991d\n' +
      'commitplease 84991d..\n\n' +
      'Docs on git commit ranges: https://bit.ly/commit-range'
    )

    process.exit(0)
  }

  var options = getOptions()
  var message = path.join('.git', 'COMMIT_EDITMSG')

  if (path.normalize(argv[0]) === message) {
    runValidate(fs.readFileSync(message, 'utf8').toString(), options)

    process.exit(0)
  }

  var committish = 'HEAD'
  if (argv.length !== 0) {
    committish = argv[0]
  }

  var repo = new Git(process.cwd())

  var secret = '--++== CoMMiTPLeaSe ==++--'
  var format = '--format=%B' + secret

  repo.exec('log', format, committish, function (error, messages) {
    if (error) {
      if (/Not a git repository/.test(error.message)) {
        console.log(error.message)

        process.exit(0)
      }

      if (/does not have any commits yet/.test(error.message)) {
        console.log(error.message)

        process.exit(0)
      }

      console.error(error)

      process.exit(1)
    }

    messages = messages.trim().split(secret)
    messages.pop()

    for (var i = 0; i < messages.length; ++i) {
      runValidate(messages[i], options)
    }
  })
}

module.exports.defaults = defaults
module.exports.getOptions = getOptions
