const fs = require('fs');
const {chromium} = require('playwright');
const waitForNetworkSettled = require('./networkSettled');
const maths = require('./maths');
async function run(browser, config) {
  const context = await browser.newContext({
    screen: {width: 1920, height: 1080},
  });
  const page = await context.newPage();
  const client = await page.context().newCDPSession(page)
  await client.send('Network.enable')
  await client.send('Network.emulateNetworkConditions', {
    offline: false,
    downloadThroughput: (4 * 1024 * 1024) / 8,
    uploadThroughput: (4 * 1024 * 1024) / 8,
    latency: 20
  })
  if (config.runType === "native") {
    await page.route(/.*?adequa\.js/, route => route.abort());
    await page.route(/.*?potions\.js/, route => route.abort());
  }
  await page.goto(config.pageUrl, {waituntil: 'domcontentloaded'});
  if(config.cookieBannerAcceptSelector) {
    await waitForNetworkSettled(page, async () => {
      await page.click(config.cookieBannerAcceptSelector);
    });
  }
  const start = Date.now();
  if(config.runType === "potions") {
    await page.waitForFunction(() => {
      try {
        return !!adequa.widgets.products.listeners[0];
      } catch (e) {
        return false
      }
    });
  }
  await page.click(config.addToCartButtonSelector);
  await page.waitForSelector(
    config.runType === "potions" ? config.potionsBannerSelector : config.nativeBannerSelector);
  const time = Date.now() - start;
  await context.close();
  return time;
}
(async (config) => {
  const browser = await chromium.launch({headless:false});
  const runTiming = [];
  for (const i of Array(config.numberOfRuns).keys()) {
    console.log(`${i + 1}/${config.numberOfRuns}`)
    runTiming.push(await run(browser, config));
  }
  await browser.close();
  fs.writeFileSync(
    `results/${config.pageUrl
      .replace("https:", "")
      .replaceAll("/", "")
      .replaceAll(".", "")}_domloaded_${config.runType}`,
    JSON.stringify({
      min: Math.min(...runTiming),
      max: Math.max(...runTiming),
      mean: maths.mean(runTiming),
      median: maths.median(runTiming),
      p75: maths.quantile(runTiming, .75),
      p95: maths.quantile(runTiming, .95),
      p99: maths.quantile(runTiming, .99),
      raw: runTiming
    }));
})({
  pageUrl: "https://www.alltricks.fr/",
  cookieBannerAcceptSelector: "#didomi-notice-agree-button",
  addToCartButtonSelector: "form > div > button",
  potionsBannerSelector: "#adq_products",
  nativeBannerSelector: ".allbox-cart-back:not(.mfp-hide) .recommendedProductModule .alltricks-Product--3columns",
  runType: "potions", 
  numberOfRuns: 10
});
module.exports = async function waitForNetworkSettled(page, action, longPolls = 0) {
  let networkSettledCallback;
  const networkSettledPromise = new Promise(f => networkSettledCallback = f);
  let requestCounter = 0;
  let actionDone = false;
  const maybeSettle = () => {
    if (actionDone && requestCounter <= longPolls)
      networkSettledCallback();
  };
  const onRequest = () => {
    ++requestCounter;
  };
  const onRequestDone = () => {
    const evaluate = page.evaluate(() => new Promise(f => setTimeout(f, 0)));
    evaluate.catch(e => null).then(() => {
      --requestCounter;
      maybeSettle();
    });
  };
  page.on('request', onRequest);
  page.on('requestfinished', onRequestDone);
  page.on('requestfailed', onRequestDone);
  const result = await action();
  actionDone = true;
  maybeSettle();
  await networkSettledPromise;
  page.removeListener('request', onRequest);
  page.removeListener('requestfinished', onRequestDone);
  page.removeListener('requestfailed', onRequestDone);
  return result;
};
const asc = arr => arr.sort((a, b) => a - b);
const sum = arr => arr.reduce((a, b) => a + b, 0);
const mean = arr => sum(arr) / arr.length;
const quantile = (arr, q) => {
  const sorted = asc(arr);
  const pos = (sorted.length - 1) * q;
  const base = Math.floor(pos);
  const rest = pos - base;
  if (sorted[base + 1] !== undefined) {
    return sorted[base] + rest * (sorted[base + 1] - sorted[base]);
  } else {
    return sorted[base];
  }
};
const median = arr => quantile(arr, .50);
module.exports = {
  asc,
  sum,
  mean,
  quantile,
  median
}