////
// Our tiny browser, only for NOSE apps.
// Check /public/browser-nav.js for the 2nd part.

import { $, $$ } from "./dom"
import { focusInput } from "./focus"
import { scrollback } from "./dom"
import { addInput } from "./scrollback"
import { randomId } from "../shared/utils"

const HEIGHT = 540
const WIDTH = 960

const controls = $("browser-controls") as HTMLDivElement
const address = $("browser-address") as HTMLSpanElement
let iframe: HTMLIFrameElement
let realUrl = ""
let showInput = true

export function isBrowsing(): boolean {
  return document.querySelector("iframe.browser.active") !== null
}

export function openBrowser(url: string, openedVia: "click" | "command" = "click") {
  showInput = openedVia === "click"

  iframe = $$("iframe.browser.active") as HTMLIFrameElement
  iframe.src = url
  iframe.sandbox.add("allow-scripts", "allow-same-origin", "allow-forms")
  iframe.height = String(HEIGHT)
  iframe.width = String(WIDTH)
  iframe.tabIndex = 0

  window.addEventListener("message", handleAppMessage)
  window.addEventListener("keydown", handleBrowserKeydown)
  controls.addEventListener("click", handleClick)
  iframe.addEventListener("load", handlePageLoad)

  controls.style.display = ""
  const main = document.querySelector("#content")
  main?.prepend(iframe)

  setAddress(url)
}

function closeBrowser() {
  window.removeEventListener("keydown", handleBrowserKeydown)
  window.removeEventListener("message", handleAppMessage)
  controls.removeEventListener("click", handleClick)
  iframe.removeEventListener("load", handlePageLoad)

  const id = randomId()
  if (showInput)
    addInput(id, "browse " + realUrl, "ok")

  iframe.style.transformOrigin = "top left"
  iframe.style.transform = "scale(0.5)"
  iframe.style.pointerEvents = "none"
  iframe.tabIndex = -1
  iframe.classList.remove("fullscreen", "active")
  scrollback.append(iframe)

  controls.style.display = "none"
  focusInput()
}

function handleAppMessage(event: MessageEvent) {
  const origin = event.origin
  if (!origin.includes('localhost') && !origin.match(/\.local$/)) {
    return
  }

  const { type, data } = event.data

  switch (type) {
    case 'NAV_READY':
      break

    case 'URL_CHANGED':
      setAddress(data.url)
      break

    case 'TITLE_CHANGED':
      // TODO: browser titles
      console.log('Page title:', data.title)
      break

    case 'NAV_BLOCKED':
      showNavigationError(data.url, data.reason)
      break

    case 'KEYDOWN':
      const keyEvent = new KeyboardEvent('keydown', {
        key: data.key,
        ctrlKey: data.ctrlKey,
        shiftKey: data.shiftKey,
        altKey: data.altKey,
        metaKey: data.metaKey,
        bubbles: true
      })
      window.dispatchEvent(keyEvent)
      break
  }
}

function handleBrowserKeydown(e: KeyboardEvent) {
  if (e.key === "Escape" || (e.ctrlKey && e.key === "c")) {
    e.preventDefault()
    closeBrowser()
  }
}

function handleClick(e: MouseEvent) {
  const target = e.target
  if (!(target instanceof HTMLElement)) return

  switch (target.id) {
    case "back-button":
      navigateBack(); break
    case "forward-button":
      navigateForward(); break
    case "stop-button":
      stopLoading(); break
    case "reload-button":
      reloadBrowser(); break
    case "fullscreen-button":
      fullscreenBrowser(); break
    case "close-button":
      e.stopImmediatePropagation()
      closeBrowser()
      break
    default:
      return
  }

  e.preventDefault()
}

function handlePageLoad() {
}

function setAddress(url: string) {
  realUrl = url
  address.textContent = url.replace(/https?:\/\//, "")
}

function navigateBack() {
  sendNavCommand('back')
}

function navigateForward() {
  sendNavCommand('forward')
}

function reloadBrowser() {
  iframe.src = realUrl
}

function stopLoading() {
  sendNavCommand('stop')
}

function fullscreenBrowser() {
  controls.style.display = 'none'
  iframe.classList.add('fullscreen')
  document.body.append(iframe)
}

function sendNavCommand(action: 'back' | 'forward' | 'stop') {
  if (!iframe.contentWindow) return

  iframe.contentWindow.postMessage({
    type: 'NAV_COMMAND',
    action
  }, '*')
}

function showNavigationError(url: string, reason: string) {
  alert(`NAVIGATION BLOCKED\n\n${url}\n\n${reason}`)
}