const path = require('path');
const { readFileSync, writeFileSync, mkdirSync } = require('fs');
const { once } = require('events');

const pixelmatch = require('pixelmatch');
const { PNG } = require('pngjs');
const {
  Builder,
  By,
  Key,
  until,
  Capabilities,
  logging,
} = require('selenium-webdriver');

const server = require('../../server');

async function test() {
  let pixelDifference;
  let error;

  let capabilities;
  switch (process.env.BROWSER) {
    case 'ie':
      capabilities = Capabilities.ie();
      capabilities.set('ignoreProtectedModeSettings', true);
      capabilities.set('ignoreZoomSetting', true);
      capabilities.set('ie.options', {
        enableFullPageScreenshot: true,
        ensureCleanSession: true,
      });
      break;

    case 'edge':
      capabilities = Capabilities.edge();
      break;

    case 'safari':
      capabilities = Capabilities.safari();
      capabilities.set('safari.options', { technologyPreview: false });
      break;

    case 'firefox': {
      capabilities = Capabilities.firefox().setLoggingPrefs({ browser: 'ALL' });
      break;
    }
    case 'chrome': {
      capabilities = Capabilities.chrome().setLoggingPrefs({ browser: 'ALL' });
      capabilities.set('chromeOptions', {
        args: ['--headless', '--no-sandbox', '--disable-gpu'],
      });
      break;
    }
  }

  let driver = await new Builder().withCapabilities(capabilities).build();

  if (!server.listening) await once(server, 'listening');

  try {
    await driver.get(`http://127.0.0.1:${server.address().port}`);

    // wait for last choice to init
    await driver.wait(
      until.elementLocated(By.css('#reset-multiple ~ .choices__list')),
      10000,
      'waiting for all Choices instances to init',
    );

    // Resize window
    await driver
      .manage()
      .window()
      .maximize();
    await driver
      .manage()
      .window()
      // magic numbers here to make sure all demo page are fit inside
      .setRect({ x: 0, y: 0, width: 630, height: 4000 });

    // and click on press space on it, so it should open choices
    await driver
      .findElement(By.css('#reset-multiple ~ .choices__list button'))
      .sendKeys(Key.SPACE);
    await driver.sleep(1000);

    // take screenshot
    const image = await driver.takeScreenshot();
    const imageBuffer = Buffer.from(image, 'base64');

    const snapshotName = `${process.env.BROWSER}-${process.platform}.png`;
    const artifactsPath = 'screenshot';
    mkdirSync(artifactsPath, { recursive: true });

    writeFileSync(path.join(artifactsPath, snapshotName), imageBuffer);

    // compare with snapshot
    const screenshot = PNG.sync.read(imageBuffer);
    const snapshot = PNG.sync.read(
      readFileSync(path.resolve(__dirname, `./__snapshots__/${snapshotName}`)),
    );
    const { width, height } = screenshot;
    const diff = new PNG({ width, height });
    pixelDifference = pixelmatch(
      screenshot.data,
      snapshot.data,
      diff.data,
      width,
      height,
      {
        threshold: 1,
      },
    );
    writeFileSync(path.join(artifactsPath, 'diff.png'), PNG.sync.write(diff));

    // getting console logs
    // ensure no errors in console (only supported in Chrome currently)
    if (process.env.BROWSER === 'chrome') {
      const entries = await driver
        .manage()
        .logs()
        .get(logging.Type.BROWSER);
      if (
        Array.isArray(entries) &&
        entries.some(entry => entry.level.name_ === 'SEVERE')
      )
        throw new Error(JSON.stringify(entries));
    }
  } catch (err) {
    console.error(err);
    error = err;
  } finally {
    await Promise.all([
      driver.quit(),
      new Promise(resolve => server.close(resolve)),
    ]);
  }
  if (pixelDifference > 200) {
    console.error(
      `Snapshot is different from screenshot by ${pixelDifference} pixels`,
    );
    process.exit(1);
  }
  if (error) process.exit(1);
}

process.on('unhandledRejection', err => {
  console.error(err);
  process.exit(1);
});
process.once('uncaughtException', err => {
  console.error(err);
  process.exit(1);
});
setImmediate(test);
