import playwright from 'playwright';
import faker from 'faker';
import random from 'random';
import assert from "assert";
import { v4 as uuid } from "uuid";
import fs from "fs";
import url from "url";
import path from "path";
import { User } from "./models.js";

const WAIT_TIME = 5000;
const HEADLESS = !process.env.PLAYVIEW;
const COVERAGE = process.env.NODE_V8_COVERAGE;

export const tid = (name) => `[data-testid="${name}"]`;

export const form = async (page, fields, button_id) => {
  for(const [key, value] of Object.entries(fields)) {
    if(typeof value === "boolean") {
      if(value) {
        await page.check(key);
      } else {
        await page.uncheck(key);
      }
    } else {
      // it's another input to fill
      await page.fill(key, value);
    }
  }

  if(button_id) {
    await page.click(button_id);
  }
}

export const playstart = async (url) => {
  if(!HEADLESS) console.log("Running playwright in visible mode (not HEADLESS).");
  if(COVERAGE) console.log(`Coverage reports going to ${COVERAGE}.`);

  const browser = await playwright['chromium'].launch({headless: HEADLESS});
  const context = await browser.newContext();
  const p = await context.newPage();

  if(COVERAGE) {
    await p.coverage.startJSCoverage();
  }

  p.on('console', async msg =>  {
    console.debug(`CONSOLE ${msg.type()}>>`, msg)
  });
  await p.goto(url);
  return {browser, context, p};
}

export const playstop = async (browser, p) => {
  if(COVERAGE) {
    const coverage = await p.coverage.stopJSCoverage();
    for(const entry of coverage) {
      entry.url = path.join(process.cwd(), "public", url.parse(entry.url).pathname);
    }
    const v8out = {result: coverage, timestamp: Date.now()};
    fs.writeFileSync(`${COVERAGE}/${uuid()}.json`, JSON.stringify(v8out));
  }

  await browser.close();
}

export const wait = async (p, css_id) => {
  try {
    await p.waitForSelector(css_id, { timeout: WAIT_TIME });
  } catch(error) {
    console.error(error, `!!!!!!!!!!!!!!!!!!!!!!!!!!!! Failed waiting for id ${css_id}`);
    throw error;
  }
}

export const expect = async (t, p, css_id) => {
  await wait(p, css_id);

  let tag = await p.$(css_id);
  let text = tag ? await tag.textContent() : "NULL!";
  t.true(text !== null);
  return text;
}

export const sleep = (ms) => {
  return new Promise(resolve => setTimeout(resolve, ms));
}

export const random_user = () => {
  let email = faker.internet.email();
  let rnd = random.int(0, 10 * 1000);
  email = email.replace('@', `${rnd}@`);
  let full_name = faker.name.findName();

  let initials = full_name.split(' ').map(x => x[0]).join('');
  let password = faker.internet.password();
  assert(password, "Looks like faker is failing at passwords again.");

  return {
    email,
    full_name,
    initials,
    password
  }
}

export const register_user = async (is_admin=false) => {
  const rando = random_user();
  rando.password_repeat = rando.password;
  // copy it to keep the original password
  const user = await User.register({...rando});

  if(is_admin) {
    // make this user an admin
    user.admin = true;
    await User.update({id: user.id}, {admin: true});
  }
  // then replace it so we can use it later
  user.raw_password = rando.password;
  return user;
}

/* A helper function for other tests to use logins. */
export const login = async (t, p, with_user) => {
  const user = with_user ? with_user : await register_user();

  await expect(t, p, tid("login-form"));
  // fill out correct form, and redirect to watch
  await p.fill('#email', user.email);
  await p.fill("#password", user.raw_password);
  await p.click(tid('login-button'));
  await sleep(1000);

  return user;
}
