import { DomHighlighter } from "~/vendor/dom-highlighter"
import { ValueEmitter } from "~/shared/emitter"
// eslint-disable-next-line import/no-unresolved
import css from "~/vendor/dom-highlighter/style.css?string"
import { createElement } from "~/vendor/dom-highlighter/src/utils"

const isMobile = false
const hasTouchSupport = "ontouchstart" in globalThis
const showViewportSizeOnResize = false

const HIDE_CLASSNAME = "__chobitsu-hide__"

function isValidNode(node: Node): boolean {
  if (node.nodeType === 1) {
    const { classList } = node as Element
    if (
      classList.contains(HIDE_CLASSNAME) ||
      classList.contains("html2canvas-container")
    ) {
      return false
    }
  }

  const isValid = !(node.nodeType === 3 && (node.nodeValue || "").trim() === "")
  if (isValid && node.parentNode) {
    return isValidNode(node.parentNode)
  }

  return isValid
}

function getElementFromPoint(e: any) {
  if (hasTouchSupport) {
    const touch = e.touches[0] || e.changedTouches[0]
    return document.elementFromPoint(touch.clientX, touch.clientY)
  }

  return document.elementFromPoint(e.clientX, e.clientY)
}

const viewportSize = createElement("div", {
  className: HIDE_CLASSNAME,
  style: {
    position: "fixed",
    right: 0,
    top: 0,
    background: "#fff",
    fontSize: 13,
    opacity: 0.5,
    padding: "4px 6px",
  },
})

function listen<K extends keyof WindowEventMap>(
  element: Window,
  type: K,
  listener: (this: Window, ev: WindowEventMap[K]) => any,
  options?: boolean | AddEventListenerOptions,
): () => void

function listen<K extends keyof HTMLElementEventMap>(
  element: HTMLElement,
  type: K,
  listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any,
  options?: boolean | AddEventListenerOptions,
): () => void

function listen(
  element: EventTarget,
  type: string,
  listener: (e: Event) => void,
  options?: boolean | AddEventListenerOptions,
) {
  element.addEventListener(type, listener, options)
  return () => {
    element.removeEventListener(type, listener, options)
  }
}

export class Picker {
  onPicked = new ValueEmitter<Node>()

  domHighlighter!: DomHighlighter
  container!: HTMLDivElement
  enabled = false
  disposables: (() => void)[] = []

  constructor() {}

  enable() {
    if (this.enabled) {
      return
    }

    this.container = createElement("div", {
      className: HIDE_CLASSNAME,
      style: { all: "initial" },
    })

    document.documentElement.appendChild(this.container)

    const shadowRoot = this.container.attachShadow({ mode: "open" })
    const domHighlighterContainer = createElement("div", {
      style: {
        position: "fixed",
        left: 0,
        top: 0,
        width: "100%",
        height: "100%",
        zIndex: 100000,
        pointerEvents: "none",
        fontSize: "13px",
      },
    })

    shadowRoot.append(
      createElement("style", { textContent: css }),
      domHighlighterContainer,
    )

    this.domHighlighter = new DomHighlighter(domHighlighterContainer, {
      monitorResize: true,
      showInfo: !isMobile,
    })

    let viewportSizeTimer: number

    this.disposables.push(
      listen(window, "resize", () => {
        if (!showViewportSizeOnResize) return

        viewportSize.innerText = `${window.innerWidth}px × ${window.innerHeight}px`
        if (viewportSizeTimer) {
          clearTimeout(viewportSizeTimer)
        } else {
          document.documentElement.appendChild(viewportSize)
        }
        viewportSizeTimer = window.setTimeout(() => {
          viewportSize.remove()
          viewportSizeTimer = 0
        }, 1000)
      }),
    )

    this.enabled = true

    const moveListener = (e: TouchEvent | MouseEvent) => {
      const node = getElementFromPoint(e)
      if (!node || !isValidNode(node)) {
        return
      }

      this.highlightNode(node)
    }

    const outListener = () => {
      this.hide()
    }

    const clickListener = (e: any) => {
      e.preventDefault()
      e.stopImmediatePropagation()

      this.onPicked.emit(getElementFromPoint(e)!)
      this.hide()
    }

    if (hasTouchSupport) {
      this.disposables.push(
        listen(window, "touchstart", moveListener),
        listen(window, "touchmove", moveListener),
        listen(window, "touchend", clickListener),
      )
    } else {
      this.disposables.push(
        listen(window, "mousemove", moveListener),
        listen(window, "mouseout", outListener),
        listen(window, "click", clickListener),
      )
    }
  }

  private highlightNode(node: Node) {
    if (node.nodeType !== 1 && node.nodeType !== 3) {
      return
    }

    this.domHighlighter.highlight(node as HTMLElement, {
      showInfo: !isMobile,
      showRulers: false,
      showAccessibilityInfo: !isMobile,
      showExtensionLines: false,
      // @ts-expect-error
      contrastAlgorithm: "aa",
      contentColor: "rgba(111, 168, 220, .66)",
      paddingColor: "rgba(147, 196, 125, .55)",
      borderColor: "rgba(255, 229, 153, .66)",
      marginColor: "rgba(246, 178, 107, .66)",
    })
  }

  hide() {
    this.domHighlighter.hide()
  }

  disable() {
    if (!this.enabled) {
      return
    }

    this.domHighlighter.destroy()
    this.container.remove()
    this.enabled = false
    this.disposables.forEach(dispose => dispose())
  }
}

const a = new Picker()
a.onPicked.listen(node => {
  console.log("picked", node)
})
setInterval(() => {
  if (
    process.env.NODE_ENV === "development" &&
    document.body.classList.contains("picker")
  ) {
    a.enable()
  } else {
    a.disable()
  }
}, 500)
