const test = require('ava')

const { vanillaPuppeteer, addExtra, compareLooseVersionStrings } = require('./util')
const Plugin = require('..')

// Fix CI issues with old versions
const isOldPuppeteerVersion = () => {
  const version = process.env.PUPPETEER_VERSION
  const isOld = version && (version === '1.9.0' || version === '1.6.2')
  return isOld
}

/* global HTMLIFrameElement */
/* global Notification */
test('stealth: will pass Paul Irish', async t => {
  const browser = await addExtra(vanillaPuppeteer)
    .use(Plugin())
    .launch({ headless: true })
  const page = await browser.newPage()
  await page.exposeFunction('compareLooseVersionStrings', compareLooseVersionStrings)
  const detectionResults = await page.evaluate(detectHeadless)
  await browser.close()

  if (isOldPuppeteerVersion()) {
    t.true(true)
    return
  }

  const wasHeadlessDetected = Object.values(detectionResults).some(Boolean)
  if (wasHeadlessDetected) {
    console.log(detectionResults)
  }
  t.false(wasHeadlessDetected)
})

async function detectHeadless() {
  const results = {}

  async function test(name, fn) {
    const detectionPassed = await fn()
    if (detectionPassed) console.log(`Chrome headless detected via ${name}`)
    results[name] = detectionPassed
  }

  await test('userAgent', _ => {
    return /HeadlessChrome/.test(window.navigator.userAgent)
  })

  // navigator.webdriver behavior change since release 89.0.4339.0. See also #448
  if (await compareLooseVersionStrings(navigator.userAgent, '89.0.4339.0') >= 0) {
    await test('navigator.webdriver is not false', _ => {
      return navigator.webdriver !== false
    })
  } else {
    // Detects the --enable-automation || --headless flags
    // Will return true in headful if --enable-automation is provided
    await test('navigator.webdriver present', _ => {
      return 'webdriver' in navigator
    })

    await test('navigator.webdriver not undefined', _ => {
      return navigator.webdriver !== undefined
    })

    /* eslint-disable no-proto */
    await test('navigator.webdriver property overridden', _ => {
      return (
        Object.getOwnPropertyDescriptor(navigator.__proto__, 'webdriver') !==
        undefined
      )
    })

    await test('navigator.webdriver prop detected', _ => {
      for (const prop in navigator) {
        if (prop === 'webdriver') {
          return true
        }
      }
      return false
    })
  }

  await test('window.chrome missing', _ => {
    return /Chrome/.test(window.navigator.userAgent) && !window.chrome
  })

  await test('permissions API', async _ => {
    const permissionStatus = await navigator.permissions.query({
      name: 'notifications'
    })
    return (
      Notification.permission === 'denied' &&
      permissionStatus.state === 'prompt'
    )
  })

  await test('permissions API overriden', _ => {
    const permissions = window.navigator.permissions
    if (permissions.query.toString() !== 'function query() { [native code] }')
      return true
    if (
      permissions.query.toString.toString() !==
      'function toString() { [native code] }'
    )
      return true
    if (
      permissions.query.toString.hasOwnProperty('[[Handler]]') && // eslint-disable-line
      permissions.query.toString.hasOwnProperty('[[Target]]') && // eslint-disable-line
      permissions.query.toString.hasOwnProperty('[[IsRevoked]]') // eslint-disable-line
    )
      return true
    if (permissions.hasOwnProperty('query')) return true // eslint-disable-line
  })

  await test('navigator.plugins empty', _ => {
    return navigator.plugins.length === 0
  })

  await test('navigator.languages blank', _ => {
    return navigator.languages === ''
  })

  await test('iFrame for fresh window object', _ => {
    // evaluateOnNewDocument scripts don't apply within [srcdoc] (or [sandbox]) iframes
    // https://github.com/GoogleChrome/puppeteer/issues/1106#issuecomment-359313898
    const iframe = document.createElement('iframe')
    iframe.srcdoc = 'page intentionally left blank'
    document.body.appendChild(iframe)

    // Verify iframe prototype isn't touched
    const descriptors = Object.getOwnPropertyDescriptors(
      HTMLIFrameElement.prototype
    )

    if (
      descriptors.contentWindow.get.toString() !==
      'function get contentWindow() { [native code] }'
    )
      return true
    // Verify iframe isn't remapped to main window
    if (iframe.contentWindow === window) return true

    // Here we would need to rerun all tests with `iframe.contentWindow` as `window`
    // Example:
    return iframe.contentWindow.navigator.plugins.length === 0
  })

  // This detects that a devtools protocol agent is attached.
  // So it will also pass true in headful Chrome if the devtools window is attached
  await test('toString', _ => {
    let gotYou = 0
    const spooky = /./
    spooky.toString = function() {
      gotYou++
      return 'spooky'
    }
    console.debug(spooky)
    return gotYou > 1
  })

  return results
}
