import prepare from '@pnpm/prepare'
import ncpCB = require('ncp')
import path = require('path')
import exists = require('path-exists')
import {
  addDependenciesToPackage,
  installPkgs,
  rebuild,
  rebuildPkgs,
} from 'supi'
import tape = require('tape')
import promisifyTape from 'tape-promise'
import promisify = require('util.promisify')
import {
  pathToLocalPkg,
  testDefaults,
 } from './utils'

const ncp = promisify(ncpCB.ncp)
const test = promisifyTape(tape)

test('rebuilds dependencies', async (t: tape.Test) => {
  const project = prepare(t)

  const pkgs = ['pre-and-postinstall-scripts-example', 'zkochan/install-scripts-example#prepare']
  await addDependenciesToPackage(pkgs, await testDefaults({ targetDependenciesField: 'devDependencies', ignoreScripts: true }))

  let modules = await project.loadModules()
  t.deepEqual(modules!.pendingBuilds, [
    '/pre-and-postinstall-scripts-example/1.0.0',
    'github.com/zkochan/install-scripts-example/2de638b8b572cd1e87b74f4540754145fb2c0ebb',
  ])

  await rebuild(await testDefaults())

  modules = await project.loadModules()
  t.ok(modules)
  t.equal(modules!.pendingBuilds.length, 0)

  {
    t.notOk(await exists('node_modules/pre-and-postinstall-scripts-example/generated-by-prepare.js'))
    t.ok(await exists('node_modules/pre-and-postinstall-scripts-example/generated-by-preinstall.js'))

    const generatedByPreinstall = project.requireModule('pre-and-postinstall-scripts-example/generated-by-preinstall')
    t.ok(typeof generatedByPreinstall === 'function', 'generatedByPreinstall() is available')

    const generatedByPostinstall = project.requireModule('pre-and-postinstall-scripts-example/generated-by-postinstall')
    t.ok(typeof generatedByPostinstall === 'function', 'generatedByPostinstall() is available')
  }

  {
    const scripts = project.requireModule('install-scripts-example-for-pnpm/output.json')
    t.equal(scripts[0], 'preinstall')
    t.equal(scripts[1], 'install')
    t.equal(scripts[2], 'postinstall')
    t.equal(scripts[3], 'prepare')
  }
})

test('rebuild does not fail when a linked package is present', async (t: tape.Test) => {
  const project = prepare(t)
  await ncp(pathToLocalPkg('local-pkg'), path.resolve('..', 'local-pkg'))

  await addDependenciesToPackage(['link:../local-pkg', 'is-positive'], await testDefaults())

  await rebuild(await testDefaults())

  // see related issue https://github.com/pnpm/pnpm/issues/1155
  t.pass('rebuild did not fail')
})

test('rebuilds specific dependencies', async (t: tape.Test) => {
  const project = prepare(t)
  await addDependenciesToPackage([
    'pre-and-postinstall-scripts-example',
    'zkochan/install-scripts-example'
  ], await testDefaults({ targetDependenciesField: 'devDependencies', ignoreScripts: true }))

  await rebuildPkgs(['install-scripts-example-for-pnpm'], await testDefaults())

  await project.hasNot('pre-and-postinstall-scripts-example/generated-by-preinstall')
  await project.hasNot('pre-and-postinstall-scripts-example/generated-by-postinstall')

  const generatedByPreinstall = project.requireModule('install-scripts-example-for-pnpm/generated-by-preinstall')
  t.ok(typeof generatedByPreinstall === 'function', 'generatedByPreinstall() is available')

  const generatedByPostinstall = project.requireModule('install-scripts-example-for-pnpm/generated-by-postinstall')
  t.ok(typeof generatedByPostinstall === 'function', 'generatedByPostinstall() is available')
})

test('rebuild with pending option', async (t: tape.Test) => {
  const project = prepare(t)
  await addDependenciesToPackage(['pre-and-postinstall-scripts-example'], await testDefaults({ ignoreScripts: true }))
  await addDependenciesToPackage(['zkochan/install-scripts-example'], await testDefaults({ ignoreScripts: true }))

  let modules = await project.loadModules()
  t.deepEqual(modules!.pendingBuilds, [
    '/pre-and-postinstall-scripts-example/1.0.0',
    'github.com/zkochan/install-scripts-example/6d879afcee10ece4d3f0e8c09de2993232f3430a',
  ])

  await project.hasNot('pre-and-postinstall-scripts-example/generated-by-preinstall')
  await project.hasNot('pre-and-postinstall-scripts-example/generated-by-postinstall')

  await project.hasNot('install-scripts-example-for-pnpm/generated-by-preinstall')
  await project.hasNot('install-scripts-example-for-pnpm/generated-by-postinstall')

  await rebuild(await testDefaults({ rawNpmConfig: { pending: true } }))

  modules = await project.loadModules()
  t.ok(modules)
  t.equal(modules!.pendingBuilds.length, 0)

  {
    const generatedByPreinstall = project.requireModule('pre-and-postinstall-scripts-example/generated-by-preinstall')
    t.ok(typeof generatedByPreinstall === 'function', 'generatedByPreinstall() is available')

    const generatedByPostinstall = project.requireModule('pre-and-postinstall-scripts-example/generated-by-postinstall')
    t.ok(typeof generatedByPostinstall === 'function', 'generatedByPostinstall() is available')
  }

  {
    const generatedByPreinstall = project.requireModule('install-scripts-example-for-pnpm/generated-by-preinstall')
    t.ok(typeof generatedByPreinstall === 'function', 'generatedByPreinstall() is available')

    const generatedByPostinstall = project.requireModule('install-scripts-example-for-pnpm/generated-by-postinstall')
    t.ok(typeof generatedByPostinstall === 'function', 'generatedByPostinstall() is available')
  }
})

test('rebuild dependencies in correct order', async (t: tape.Test) => {
  const project = prepare(t)

  await addDependenciesToPackage(['with-postinstall-a'], await testDefaults({ ignoreScripts: true }))

  let modules = await project.loadModules()
  t.ok(modules)
  t.doesNotEqual(modules!.pendingBuilds.length, 0)

  await project.hasNot('.localhost+4873/with-postinstall-b/1.0.0/node_modules/with-postinstall-b/output.json')
  await project.hasNot('with-postinstall-a/output.json')

  await rebuild(await testDefaults({ rawNpmConfig: { pending: true } }))

  modules = await project.loadModules()
  t.ok(modules)
  t.equal(modules!.pendingBuilds.length, 0)

  t.ok(+project.requireModule('.localhost+4873/with-postinstall-b/1.0.0/node_modules/with-postinstall-b/output.json')[0] < +project.requireModule('with-postinstall-a/output.json')[0])
})
