'use strict'

const Benchmark = require('benchmark')
const chalk = require('chalk')
const fs = require('fs')
const path = require('path')
const rimraf = require('rimraf')

const CACHE = path.join(__dirname, '../', 'cache', 'benchmarks')
const PREVIOUS = path.join(path.dirname(CACHE), 'last-benchmark.json')
const WARN_RANGE = 5

const suite = new Benchmark.Suite({
  onStart () {
    const previousPath = process.env.COMPARETO
      ? path.resolve(process.env.COMPARETO)
      : PREVIOUS
    try {
      this.previous = require(previousPath)
    } catch (e) {}
    console.log('================================================')
    if (this.previous) {
      console.log('  Comparing to', path.relative(process.cwd(), previousPath))
      console.log('================================================')
    }
  },
  onCycle (event) {
    const bench = event.target
    const prev = this.previous && this.previous[bench.name]
    const pctDelta =
      prev && ((bench.stats.mean - prev.stats.mean) / prev.stats.mean) * 100
    let colorDiff = !prev
      ? ''
      : `${pctDelta > 0 ? '+' : ''}${pctDelta.toFixed(2)}% `
    colorDiff = ` (${
      pctDelta >= WARN_RANGE + bench.stats.rme
        ? chalk.red(colorDiff)
        : pctDelta <= -(WARN_RANGE + bench.stats.rme)
          ? chalk.green(colorDiff)
          : colorDiff
    }±${bench.stats.rme.toFixed(2)}%)`
    console.log(`     ${bench.name}`)
    console.log('------------------------------------------------')
    if (bench.error) {
      console.log('Error:', bench.error.message || bench.error)
    } else {
      console.log(
        `  ${bench.hz.toFixed(bench.hz < 100 ? 2 : 0)} ops/s @ ~${(
          bench.stats.mean * 1000
        ).toFixed(3)}ms/op${colorDiff}`
      )
      console.log(
        `  Sampled ${
          bench.stats.sample.length
        } in ${bench.times.elapsed.toFixed(2)}s.`
      )
    }
    console.log('================================================')
    rimraf.sync(CACHE)
  },
  onComplete () {
    fs.writeFileSync(
      PREVIOUS,
      JSON.stringify(
        this.reduce((acc, bench) => {
          acc[bench.name] = bench
          return acc
        }, {})
      ),
      'utf8'
    )
  }
})

fs.readdir(__dirname, (err, files) => {
  if (err) {
    throw err
  }
  files.forEach((f) => {
    if (path.extname(f) === '.js' && f !== 'index.js') {
      require('./' + f)(suite, path.join(CACHE, path.basename(f, '.js')))
    }
  })
  suite.run({ async: true })
})
