require('@packages/ts/register')

const Jimp = require('jimp')
const path = require('path')
const Promise = require('bluebird')
const { useFixedBrowserLaunchSize } = require('@tooling/system-tests/lib/pluginUtils')

/**
 * @type {Cypress.PluginConfig}
 */
module.exports = (on, config) => {
  let performance = {
    track: () => Promise.resolve(),
  }

  // TODO: fix this - in open mode, this will throw an error
  // since the relative path is different in open vs run mode
  try {
    performance = require('@tooling/system-tests/lib/performance')
  } catch (err) {
    console.error(err)
  }

  // save some time by only reading the originals once
  let cache = {}

  let browserArgs = null

  function getCachedImage (name) {
    const cachedImage = cache[name]

    if (cachedImage) return Promise.resolve(cachedImage)

    const imagePath = path.join(__dirname, '..', 'screenshots', `${name}.png`)

    return Jimp.read(imagePath).then((image) => {
      cache[name] = image

      return image
    })
  }

  on('before:browser:launch', (browser, options) => {
    useFixedBrowserLaunchSize(browser, options, config)

    if (browser.family === 'firefox' && process.env.FIREFOX_FORCE_STRICT_SAMESITE) {
      // @see https://www.jardinesoftware.net/2019/10/28/samesite-by-default-in-2020/
      // this option will eventually become default, but for now, it seems to have inconsistent
      // behavior in the extension vs regular web browsing - default in webextension is still
      // "no_restriction"
      // options.preferences['network.cookie.sameSite.laxByDefault'] = true
      options.preferences['network.cookie.sameSite.noneRequiresSecure'] = true
    }

    if (browser.family === 'chromium' && browser.name !== 'electron') {
      if (process.env.CHROMIUM_EXTRA_LAUNCH_ARGS) {
        options.args = options.args.concat(process.env.CHROMIUM_EXTRA_LAUNCH_ARGS.split(' '))
      }
    }

    browserArgs = options.args

    return options
  })

  on('task', {
    'console:log' (msg) {
      console.log(msg)

      return null
    },

    'console:error' (msg) {
      console.error(msg)

      return null
    },

    'returns:undefined' () {},

    'errors' (message) {
      throw new Error(message)
    },

    'plugins:crash' (message) {
      console.log('\nPURPOSEFULLY CRASHING THE PLUGIN PROCESS FROM TEST')
      process.exit(1)
    },

    'compare:screenshots' ({ a, b, devicePixelRatio, blackout = false }) {
      function isBlack (rgba) {
        return `${rgba.r}${rgba.g}${rgba.b}` === '000'
      }

      const comparePath = path.join(__dirname, '..', 'screenshots', `${b}.png`)

      return Promise.all([
        getCachedImage(a),
        Jimp.read(comparePath),
      ])
      .spread((originalImage, compareImage) => {
        const blackout1XCoord = 11 * devicePixelRatio
        const blackout2XCoord = 31 * devicePixelRatio
        const blackoutYCoord = 11 * devicePixelRatio

        if (
          blackout && (
            !isBlack(Jimp.intToRGBA(compareImage.getPixelColor(blackout1XCoord, blackoutYCoord))) ||
            !isBlack(Jimp.intToRGBA(compareImage.getPixelColor(blackout2XCoord, blackoutYCoord)))
          )
        ) {
          throw new Error('Blackout not present!')
        }

        if (originalImage.hash() !== compareImage.hash()) {
          throw new Error('Screenshot mismatch!')
        }

        return null
      })
    },

    'record:fast_visit_spec' ({ percentiles, url, browser, currentRetry }) {
      percentiles.forEach(([percent, percentile]) => {
        console.log(`${percent}%\t of visits to ${url} finished in less than ${percentile}ms`)
      })

      const data = {
        url,
        browser,
        currentRetry,
        ...percentiles.reduce((acc, pair) => {
          acc[pair[0]] = pair[1]

          return acc
        }, {}),
      }

      return performance.track('fast_visit_spec percentiles', data)
      .return(null)
    },

    'get:browser:args' () {
      return browserArgs
    },

    'get:config:value' (key) {
      return config[key]
    },
  })

  return config
}
