var childProcess = require('child_process')
var escalade = require('escalade/sync')
var path = require('path')
var fs = require('fs')

var BrowserslistError = require('./error')

function detectLockfile () {
  var packageDir = escalade('.', function (dir, names) {
    return names.indexOf('package.json') !== -1 ? dir : ''
  })

  if (!packageDir) {
    throw new BrowserslistError(
      'Cannot find package.json. ' +
      'Is it a right project to run npx browserslist --update-db?'
    )
  }

  var lockfileNpm = path.join(packageDir, 'package-lock.json')
  var lockfileYarn = path.join(packageDir, 'yarn.lock')
  var lockfilePnpm = path.join(packageDir, 'pnpm-lock.yaml')

  /* istanbul ignore next */
  if (fs.existsSync(lockfilePnpm)) {
    return { mode: 'pnpm', file: lockfilePnpm }
  } else if (fs.existsSync(lockfileNpm)) {
    return { mode: 'npm', file: lockfileNpm }
  } else if (fs.existsSync(lockfileYarn)) {
    return { mode: 'yarn', file: lockfileYarn }
  } else {
    throw new BrowserslistError(
      'No lockfile found. Run "npm install", "yarn install" or "pnpm install"'
    )
  }
}

function getCurrentVersion (lock) {
  var match
  /* istanbul ignore if */
  if (lock.mode === 'pnpm') {
    match = /\/caniuse-lite\/([^:]+):/.exec(lock.content)
    if (match[1]) return match[1]
  } else if (lock.mode === 'npm') {
    var dependencies = JSON.parse(lock.content).dependencies
    if (dependencies && dependencies['caniuse-lite']) {
      return dependencies['caniuse-lite'].version
    }
  } else if (lock.mode === 'yarn') {
    match = /caniuse-lite@[^:]+:\r?\n\s+version\s+"([^"]+)"/.exec(lock.content)
    if (match[1]) return match[1]
  }
  return null
}

function getLatestInfo (lock) {
  if (lock.mode !== 'yarn') {
    return JSON.parse(
      childProcess.execSync('npm show caniuse-lite --json').toString()
    )
  } else {
    return JSON.parse(
      childProcess.execSync('yarn info caniuse-lite --json').toString()
    ).data
  }
}

function updateLockfile (lock, latest) {
  if (lock.mode === 'npm') {
    var fixed = deletePackage(JSON.parse(lock.content))
    return JSON.stringify(fixed, null, '  ')
  } else {
    var lines = lock.content.split('\n')
    var i
    /* istanbul ignore if */
    if (lock.mode === 'pnpm') {
      for (i = 0; i < lines.length; i++) {
        if (lines[i].indexOf('caniuse-lite:') >= 0) {
          lines[i] = lines[i].replace(/: .*$/, ': ' + latest.version)
        } else if (lines[i].indexOf('/caniuse-lite') >= 0) {
          lines[i] = lines[i].replace(/\/[^/:]+:/, '/' + latest.version + ':')
          for (i = i + 1; i < lines.length; i++) {
            if (lines[i].indexOf('integrity: ') !== -1) {
              lines[i] = lines[i].replace(
                /integrity: .+/, 'integrity: ' + latest.dist.integrity
              )
            } else if (lines[i].indexOf(' /') !== -1) {
              break
            }
          }
        }
      }
    } else if (lock.mode === 'yarn') {
      for (i = 0; i < lines.length; i++) {
        if (lines[i].indexOf('caniuse-lite@') !== -1) {
          lines[i + 1] = lines[i + 1].replace(
            /version "[^"]+"/, 'version "' + latest.version + '"'
          )
          lines[i + 2] = lines[i + 2].replace(
            /resolved "[^"]+"/, 'resolved "' + latest.dist.tarball + '"'
          )
          lines[i + 3] = lines[i + 3].replace(
            /integrity .+/, 'integrity ' + latest.dist.integrity
          )
          i += 4
        }
      }
    }
    return lines.join('\n')
  }
}

function deletePackage (node) {
  if (node.dependencies) {
    delete node.dependencies['caniuse-lite']
    for (var i in node.dependencies) {
      node.dependencies[i] = deletePackage(node.dependencies[i])
    }
  }
  return node
}

module.exports = function updateDB (print) {
  var lock = detectLockfile()
  lock.content = fs.readFileSync(lock.file).toString()

  var current = getCurrentVersion(lock)
  var latest = getLatestInfo(lock)

  if (typeof current === 'string') {
    print('Current version: ' + current + '\n')
  }
  print(
    'New version: ' + latest.version + '\n' +
    'Removing old caniuse-lite from lock file\n'
  )

  fs.writeFileSync(lock.file, updateLockfile(lock, latest))

  var install = lock.mode === 'yarn' ? 'yarn add' : lock.mode + ' install'
  print(
    'Installing new caniuse-lite version\n' +
    '$ ' + install + ' caniuse-lite\n'
  )
  try {
    childProcess.execSync(install + ' caniuse-lite')
  } catch (e) /* istanbul ignore next */ {
    print(e.stack)
    print('\nProblem with `' + lock.mode + ' install` call. Run it manually.\n')
    process.exit(1)
  }

  var del = lock.mode === 'yarn' ? 'yarn remove' : lock.mode + ' uninstall'
  print(
    'Cleaning package.json dependencies from caniuse-lite\n' +
    '$ ' + del + ' caniuse-lite\n'
  )
  childProcess.execSync(del + ' caniuse-lite')

  print('caniuse-lite has been successfully updated\n')
}
