"use client"

import * as React from "react"

import { CMD_K_FORWARD_TYPE } from "@/app/(app)/(create)/components/action-menu"
import { CreateDevtools } from "@/app/(app)/(create)/components/create-devtools"
import {
  REDO_FORWARD_TYPE,
  UNDO_FORWARD_TYPE,
} from "@/app/(app)/(create)/components/history-buttons"
import { DARK_MODE_FORWARD_TYPE } from "@/app/(app)/(create)/components/mode-switcher"
import { usePreviewOverrideValue } from "@/app/(app)/(create)/components/preview-override"
import { PreviewSwitcher } from "@/app/(app)/(create)/components/preview-switcher"
import { RANDOMIZE_FORWARD_TYPE } from "@/app/(app)/(create)/components/random-button"
import { sendToIframe } from "@/app/(app)/(create)/hooks/use-iframe-sync"
import { OPEN_PRESET_FORWARD_TYPE } from "@/app/(app)/(create)/hooks/use-open-preset"
import { RESET_FORWARD_TYPE } from "@/app/(app)/(create)/hooks/use-reset"
import {
  serializeDesignSystemSearchParams,
  useDesignSystemSearchParams,
} from "@/app/(app)/(create)/lib/search-params"

// Hoisted — avoids recreating on every message event. (js-hoist-regexp)
const MAC_REGEX = /Mac|iPhone|iPad|iPod/

function isSameParams(
  a: ReturnType<typeof useDesignSystemSearchParams>[0] | null,
  b: ReturnType<typeof useDesignSystemSearchParams>[0]
) {
  if (!a) {
    return false
  }

  return Object.keys(b).every(
    (key) => a[key as keyof typeof b] === b[key as keyof typeof b]
  )
}

export function Preview() {
  const [params] = useDesignSystemSearchParams()
  const override = usePreviewOverrideValue()
  const iframeRef = React.useRef<HTMLIFrameElement>(null)
  const lastSentParamsRef = React.useRef<typeof params | null>(null)

  // Hover previews overlay the committed params without touching the URL —
  // clearing the override re-sends the committed params, reverting the preview.
  const mergedParams = React.useMemo(
    () => (override ? { ...params, ...override } : params),
    [params, override]
  )

  React.useEffect(() => {
    const iframe = iframeRef.current
    if (!iframe) {
      return
    }

    const sendParams = () => {
      sendToIframe(iframe, "design-system-params", mergedParams)
      lastSentParamsRef.current = mergedParams
    }

    // Skip content-identical re-sends (e.g. an override matching the committed
    // params) — each message triggers a full param sync in the iframe.
    if (
      iframe.contentWindow &&
      !isSameParams(lastSentParamsRef.current, mergedParams)
    ) {
      sendParams()
    }

    iframe.addEventListener("load", sendParams)
    return () => {
      iframe.removeEventListener("load", sendParams)
    }
  }, [mergedParams])

  React.useEffect(() => {
    const handleMessage = (event: MessageEvent) => {
      const iframeWindow = iframeRef.current?.contentWindow
      if (
        !iframeWindow ||
        event.origin !== window.location.origin ||
        event.source !== iframeWindow ||
        !event.data ||
        typeof event.data !== "object"
      ) {
        return
      }

      const type = event.data.type
      if (type === CMD_K_FORWARD_TYPE) {
        const isMac = MAC_REGEX.test(navigator.userAgent)
        document.dispatchEvent(
          new KeyboardEvent("keydown", {
            key: event.data.key || "k",
            metaKey: isMac,
            ctrlKey: !isMac,
            bubbles: true,
            cancelable: true,
          })
        )
      } else if (type === RANDOMIZE_FORWARD_TYPE) {
        document.dispatchEvent(
          new KeyboardEvent("keydown", {
            key: event.data.key || "r",
            bubbles: true,
            cancelable: true,
          })
        )
      } else if (type === OPEN_PRESET_FORWARD_TYPE) {
        document.dispatchEvent(
          new KeyboardEvent("keydown", {
            key: event.data.key || "o",
            bubbles: true,
            cancelable: true,
          })
        )
      } else if (type === UNDO_FORWARD_TYPE) {
        const isMac = MAC_REGEX.test(navigator.userAgent)
        document.dispatchEvent(
          new KeyboardEvent("keydown", {
            key: "z",
            metaKey: isMac,
            ctrlKey: !isMac,
            bubbles: true,
            cancelable: true,
          })
        )
      } else if (type === REDO_FORWARD_TYPE) {
        const isMac = MAC_REGEX.test(navigator.userAgent)
        document.dispatchEvent(
          new KeyboardEvent("keydown", {
            key: "z",
            shiftKey: true,
            metaKey: isMac,
            ctrlKey: !isMac,
            bubbles: true,
            cancelable: true,
          })
        )
      } else if (type === RESET_FORWARD_TYPE) {
        document.dispatchEvent(
          new KeyboardEvent("keydown", {
            key: "R",
            shiftKey: true,
            bubbles: true,
            cancelable: true,
          })
        )
      } else if (type === DARK_MODE_FORWARD_TYPE) {
        document.dispatchEvent(
          new KeyboardEvent("keydown", {
            key: event.data.key || "d",
            bubbles: true,
            cancelable: true,
          })
        )
      }
    }

    window.addEventListener("message", handleMessage)
    return () => {
      window.removeEventListener("message", handleMessage)
    }
  }, [])

  const iframeSrc = React.useMemo(() => {
    // The iframe src needs to include the serialized design system params
    // for the initial load, but not be reactive to them as it would cause
    // full-iframe reloads on every param change (flashes & loss of state).
    // Further updates of the search params will be sent to the iframe
    // via a postMessage channel, for it to sync its own history onto the host's.
    return serializeDesignSystemSearchParams(
      `/preview/${params.base}/${params.item}`,
      params
    )
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [params.base, params.item])

  return (
    <div className="relative flex flex-1 flex-col justify-center overflow-hidden rounded-2xl ring ring-foreground/10 md:ring-muted dark:ring-foreground/10">
      <div className="relative z-0 mx-auto flex w-full flex-1 flex-col overflow-hidden">
        <div className="absolute inset-0 bg-muted dark:bg-muted/30" />
        <iframe
          key={params.base + params.item}
          ref={iframeRef}
          src={iframeSrc}
          className="z-10 size-full flex-1"
          title="Preview"
        />
      </div>
      <CreateDevtools />
      <PreviewSwitcher />
    </div>
  )
}
