/* global location, confirm, history */
import { useLayoutEffect, useState } from 'preact/hooks'
import getMeta from './get-meta.js'
import getSize from './get-size.js'

const DOWNLOADABLE_EXTENSIONS = new Set(['zip', 'pdf', 'docx', 'xlsx', 'pptx', 'txt', 'csv', 'xps'])
const DOMAIN_WHITELIST = [
  'google.com',
  'facebook.com',
  'fuelrewards.com',
  'instagram.com',
  'linkedin.com',
  'primeenergy.com.br',
  'raizen.com',
  'raizen.com.br',
  'sonnen.de',
  'twitter.com',
  'ubitricity.com',
  'vk.com',
  'weibo.com',
  'x.com',
  'youku.com',
  'youtube.com',
  'share.google',
]
// Treat localhost as a trusted domain (similar behaviour to Shell domains) to ensure consistent behaviour in dev and prod.
const LITERAL_WHITELIST = ['i.youku.com', 'localhost', 'wa.me']
const WHITELIST = new Set([...DOMAIN_WHITELIST.flatMap(w => [w, `www.${w}`]), ...LITERAL_WHITELIST])

// -----------------------------------------------------------------------------
// Main export
// -----------------------------------------------------------------------------

// Simple link cache.
const cache = {}

export default function useLink ({ href, text, target }) {
  const [state, setState] = useState({ text, target })
  // Resolve cached links.
  const key = `${href} :: ${text} :: ${target}`
  if (cache[key]) {
    return cache[key]
  }

  useLayoutEffect(() => {
    const resolveLink = async () => {
      if (!href) {
        return null
      }

      // Javascript link sanitisation.
      const urlObj = new URL(href, location)

      // eslint-disable-next-line sonarjs/code-eval
      if (['javascript:', 'vbscript:', 'data:'].some(p => href.startsWith(p))) {
        console.error(`Invalid URL: '${href}'`)
        return null
      }

      const newState = { ...state, text, href }

      if (isHashOnlyLink(urlObj)) {
        return newState
      }

      if (isDownloadLink(urlObj)) {
        const downloadSuffix = await getDownloadSuffix(href)
        return { ...newState, text: text + downloadSuffix, target: '_blank', isDownload: true }
      }

      if (isExternalLink(urlObj) && isTrustedLink(urlObj)) {
        return { ...newState, onClick: target === '_blank' ? undefined : navigationHandler }
      }

      if (isExternalLink(urlObj)) {
        return { ...newState, target: '_blank', onClick: getDisclaimerHandler('external-disclaimer-text') }
      }

      if (isPageLink(urlObj)) {
        // Do not handle navigation when forced to new window.
        return { ...newState, onClick: target === '_blank' ? undefined : navigationHandler }
      }

      return newState
    }

    resolveLink().then(newState => {
      newState && setState(newState)
      cache[key] = newState ?? state
    })
  }, [href, text, target])

  return state
}

// -----------------------------------------------------------------------------
// Helpers
// -----------------------------------------------------------------------------

export function isDownloadLink ({ href }) {
  const ext = href?.substring?.(href?.lastIndexOf?.('.') + 1)
  return DOWNLOADABLE_EXTENSIONS.has(ext)
}

export function isTrustedLink ({ hostname }) {
  return hostname?.includes?.('shell') || WHITELIST.has(hostname)
}

function isExternalLink ({ hostname }) {
  return hostname && hostname !== location.hostname
}

function isHashOnlyLink ({ hash, href }) {
  return hash && href.replace(/#.*/, '') === location.href.replace(/#.*/, '')
}

function isPageLink ({ hostname, pathname }) {
  return location.hostname === hostname && (pathname.endsWith('/') || pathname.includes('.html'))
}

function getDisclaimerHandler (meta) {
  const disclaimerText = getMeta(meta)
  return disclaimerText ? event => !confirm(disclaimerText) && event.preventDefault() : null
}

async function getDownloadSuffix (href) {
  const ext = href.substring(href.lastIndexOf('.') + 1)

  try {
    const res = await fetch(href, { headers: { Range: 'bytes=0-0' } })
    const contentRange = res.headers.get('content-range')
    const length = /bytes \d+-\d+\/(\d+)/.exec(contentRange)?.[1]
    const fileSize = getSize(length)
    return ` (${ext.toUpperCase()}, ${fileSize})`
  } catch (error) {
    console.log(`Unable to resolve file size: ${href}`, error)
    return ` (${ext.toUpperCase()})`
  }
}

function navigationHandler (event) {
  const el = event.currentTarget
  const elUrl = el.getAttribute('action') || el.getAttribute('href')

  // Disable handler when forcing a new tab (ctrl + click).
  if (!event.defaultPrevented && !event.ctrlKey && !event.altKey && !event.metaKey) {
    history.pushState({}, '', elUrl)
    globalThis.dispatchEvent(new Event('popstate'))
    event.preventDefault()
  }
}
