import * as chalk from 'chalk'
import * as clearConsole from 'react-dev-utils/clearConsole'
import * as checkRequiredFiles from 'react-dev-utils/checkRequiredFiles'
import {
  choosePort,
  createCompiler,
  prepareUrls
} from 'react-dev-utils/WebpackDevServerUtils'
import * as openBrowser from 'react-dev-utils/openBrowser'
import { checkBrowsers } from 'react-dev-utils/browsersHelper'
import * as WebpackDevServer from 'webpack-dev-server'
import Berun from '@berun/berun'
import verifyPackageTree from './utils/verifyPackageTree'

const webpack = require('webpack')

// MAIN MODULE EXPORTS, WITH DEFAULT FLOW

export default async (berun: Berun) => {
  try {
    await taskDevBuildPreFlightArgs(berun)
    await taskDevBuildPreFlightChecks(berun)
    await taskDevBuildGetPort(berun)
    await taskDevBuildCompile(berun)
  } catch (err) {
    if (err && err.message) {
      console.log(err.message)
    }
    process.exit(1)
  }
}

// EXPORT INDIVIDUAL FUNCTIONS FOR MORE FINE-GRAINED CUSTOM TASKS
// SEE @berun/runner-webpack-static for an example of where these are used

export {
  taskDevBuildPreFlightArgs,
  taskDevBuildPreFlightChecks,
  taskDevBuildGetPort,
  taskDevBuildCompile
}

async function taskDevBuildPreFlightArgs(berun) {
  // Process CLI arguments

  const argv = process.argv.slice(2)

  berun.sparkyContext.debug = argv.indexOf('--debug') !== -1

  berun.sparkyContext.isInteractive = process.stdout.isTTY
}

async function taskDevBuildPreFlightChecks(berun) {
  if (process.env.SKIP_PREFLIGHT_CHECK !== 'true') {
    verifyPackageTree()
    // Warn and crash if required files are missing
    if (
      !checkRequiredFiles([
        //  berun.options.paths.appHtml,
        berun.options.paths.appIndexJs
      ])
    ) {
      process.exit(1)
    }
  }

  if (process.env.HOST) {
    console.log(
      chalk.cyan(
        `Attempting to bind to HOST environment variable: ${chalk.yellow(
          chalk.bold(process.env.HOST)
        )}`
      )
    )
    console.log(
      `If this was unintentional, check that you haven't mistakenly set it in your shell.`
    )
    console.log(
      `Learn more here: ${chalk.yellow('http://bit.ly/CRA-advanced-config')}`
    )
    console.log()
  }

  // We require that you explictly set browsers and do not fall back to
  // browserslist defaults.

  await checkBrowsers(berun.options.paths.appPath)
}

async function taskDevBuildGetPort(berun) {
  // Tools like Cloud9 rely on this.
  const DEFAULT_PORT = process.env.PORT ? parseInt(process.env.PORT, 10) : 3000
  const HOST = process.env.HOST || '0.0.0.0'

  const port = await choosePort(HOST, DEFAULT_PORT)

  if (port === null) {
    throw new Error('Could not find a suitable port')
  }

  const protocol = process.env.HTTPS === 'true' ? 'https' : 'http'

  berun.sparkyContext.url = { protocol, HOST, port }
}

function taskDevBuildCompile(berun) {
  const { protocol, HOST, port } = berun.sparkyContext.url

  const appName = require(berun.options.paths.appPackageJson).name
  const urls = prepareUrls(protocol, HOST, port)

  // Serve webpack assets generated by the compiler over a web server.
  const serverConfig = berun.devserver.toConfig()
  let devServer

  const devSocket = {
    warnings: warnings =>
      devServer.sockWrite(devServer.sockets, 'warnings', warnings),
    errors: errors => devServer.sockWrite(devServer.sockets, 'errors', errors)
  }

  // Create a webpack compiler that is configured with custom messages.
  const compiler = createCompiler({
    appName,
    config: berun.webpack.toConfig(),
    urls,
    useYarn: berun.options.paths.useYarn,
    webpack,
    useTypeScript: false && berun.options.paths.isTypescript,
    devSocket
  })

  devServer = new WebpackDevServer(compiler, serverConfig)

  return new Promise((resolve, _) => {
    // Launch WebpackDevServer.
    devServer.listen(port, HOST, err => {
      if (err) {
        console.log(err)
        return
      }
      if (berun.sparkyContext.isInteractive) {
        clearConsole()
      }
      console.log(chalk.cyan('Starting the development server...\n'))
      openBrowser(urls.localUrlForBrowser)
    })
    ;['SIGINT', 'SIGTERM'].forEach((sig: any) => {
      process.on(sig, () => {
        devServer.close()
        resolve()
      })
    })
  })
}
