import puppeteer from 'puppeteer'

const puppeteerOptions = process.env.CI
  ? { args: ['--no-sandbox', '--disable-setuid-sandbox'] }
  : {}

export function setupPuppeteer() {
  let browser: puppeteer.Browser
  let page: puppeteer.Page

  beforeEach(async () => {
    browser = await puppeteer.launch(puppeteerOptions)
    page = await browser.newPage()

    page.on('console', e => {
      if (e.type() === 'error') {
        const err = e.args()[0] as any
        console.error(
          `Error from Puppeteer-loaded page:\n`,
          err._remoteObject.description
        )
      }
    })
  })

  afterEach(async () => {
    await browser.close()
  })

  async function click(selector: string, options?: puppeteer.ClickOptions) {
    await page.click(selector, options)
  }

  async function count(selector: string) {
    return (await page.$$(selector)).length
  }

  async function text(selector: string) {
    return await page.$eval(selector, node => node.textContent)
  }

  async function value(selector: string) {
    return await page.$eval(selector, (node: HTMLInputElement) => node.value)
  }

  async function html(selector: string) {
    return await page.$eval(selector, node => node.innerHTML)
  }

  async function classList(selector: string) {
    return await page.$eval(selector, (node: any) => [...node.classList])
  }

  async function children(selector: string) {
    return await page.$eval(selector, (node: any) => [...node.children])
  }

  async function isVisible(selector: string) {
    const display = await page.$eval(selector, (node: HTMLElement) => {
      return window.getComputedStyle(node).display
    })
    return display !== 'none'
  }

  async function isChecked(selector: string) {
    return await page.$eval(selector, (node: HTMLInputElement) => node.checked)
  }

  async function isFocused(selector: string) {
    return await page.$eval(selector, node => node === document.activeElement)
  }

  async function setValue(selector: string, value: string) {
    const el = (await page.$(selector))!
    await el.evaluate((node: HTMLInputElement) => (node.value = ''))
    await el.type(value)
  }

  async function enterValue(selector: string, value: string) {
    const el = (await page.$(selector))!
    await el.evaluate((node: HTMLInputElement) => (node.value = ''))
    await el.type(value)
    await el.press('Enter')
  }

  async function clearValue(selector: string) {
    return await page.$eval(
      selector,
      (node: HTMLInputElement) => (node.value = '')
    )
  }

  return {
    page: () => page,
    click,
    count,
    text,
    value,
    html,
    classList,
    children,
    isVisible,
    isChecked,
    isFocused,
    setValue,
    enterValue,
    clearValue
  }
}
