import { useState, useEffect, useRef, useCallback, useMemo } from "react";
import { NodeViewWrapper } from "@tiptap/react";
import type { NodeViewProps } from "@tiptap/react";
import type { ComponentDef, ComponentField } from "../components/registry";

const DEFAULT_IFRAME_HEIGHT = 600;
const SAFETY_MIN_HEIGHT = 80;

function FieldRow({
  field,
  value,
  onChange,
}: {
  field: ComponentField;
  value: unknown;
  onChange: (val: unknown) => void;
}) {
  if (field.type === "boolean") {
    return (
      <label className="embed-field-row embed-field-checkbox">
        <input
          type="checkbox"
          checked={!!value}
          onChange={(e) => onChange(e.target.checked)}
        />
        {field.label}
      </label>
    );
  }
  return (
    <div className="embed-field-row">
      <span className="embed-field-label">{field.label}</span>
      <input
        type="text"
        value={String(value ?? "")}
        placeholder={field.placeholder || field.label}
        onChange={(e) => onChange(e.target.value)}
        className="embed-field-input"
      />
    </div>
  );
}

function parseStoredHeight(raw: unknown): number {
  if (typeof raw === "number" && raw > 0) return Math.round(raw);
  const n = parseInt(String(raw ?? ""), 10);
  return Number.isFinite(n) && n > 0 ? n : DEFAULT_IFRAME_HEIGHT;
}

/**
 * NodeView for `<div data-component="iframe">`.
 *
 * Renders a live preview of the remote URL inside an `<iframe src="...">`.
 * Auto-resizes when the embedded page sends `postMessage({ type: "embedResize", height })`
 * (same protocol as `HtmlEmbedView` so our own widgets work out of the box),
 * otherwise falls back to the manual `height` attribute.
 *
 * No content is stored in `Y.Map("embeds")` because the HTML lives at the
 * remote URL; only node attributes (src, title, desc, height, wide) travel
 * through the document.
 */
export function makeIframeEmbedView(def: ComponentDef) {
  function IframeEmbedNodeView({ node, updateAttributes }: NodeViewProps) {
    const src = String(node.attrs.src || "").trim();
    const title = String(node.attrs.title || "");
    const storedHeight = parseStoredHeight(node.attrs.height);

    const [iframeHeight, setIframeHeight] = useState(storedHeight);
    const [showSettings, setShowSettings] = useState(!src);
    const [reloadToken, setReloadToken] = useState(0);
    const iframeRef = useRef<HTMLIFrameElement>(null);

    // Keep iframe height in sync with the stored attribute when it changes
    // from elsewhere (undo/redo, settings panel edit).
    useEffect(() => {
      setIframeHeight(storedHeight);
    }, [storedHeight]);

    // Listen for height reports from same-origin/cooperating iframes.
    const lastPersistedRef = useRef<number>(storedHeight);
    const persistTimerRef = useRef(0);
    useEffect(() => {
      const handler = (e: MessageEvent) => {
        if (e.data?.type !== "embedResize") return;
        const frame = iframeRef.current;
        if (!frame || e.source !== frame.contentWindow) return;
        const h = Math.max(0, Math.ceil(e.data.height));
        if (!h) return;
        setIframeHeight((prev) => (prev === h ? prev : h));
        if (h !== lastPersistedRef.current) {
          clearTimeout(persistTimerRef.current);
          persistTimerRef.current = window.setTimeout(() => {
            if (h !== lastPersistedRef.current) {
              lastPersistedRef.current = h;
              updateAttributes({ height: String(h) });
            }
          }, 800);
        }
      };
      window.addEventListener("message", handler);
      return () => {
        window.removeEventListener("message", handler);
        clearTimeout(persistTimerRef.current);
      };
    }, [updateAttributes]);

    const handleFieldChange = useCallback(
      (fieldName: string, value: unknown) => {
        updateAttributes({ [fieldName]: value });
      },
      [updateAttributes],
    );

    const reload = useCallback(() => setReloadToken((n) => n + 1), []);

    // `key` on the iframe forces a remount when src changes or on reload,
    // which works around cross-origin pages that don't expose the History API.
    const iframeKey = useMemo(() => `${src}#${reloadToken}`, [src, reloadToken]);

    const hasSrc = !!src;

    return (
      <NodeViewWrapper data-component="iframe">
        <div contentEditable={false} className="embed-view">
          {/* Header */}
          <div className="embed-header">
            <div className="embed-header-left">
              <span className="embed-header-icon">{def.icon}</span>
              <span className="embed-header-label">
                {title || src || "Iframe"}
              </span>
              {src && title && (
                <span className="embed-header-src">{src}</span>
              )}
            </div>
            <div className="embed-header-actions">
              {hasSrc && (
                <button
                  className="embed-btn"
                  onClick={reload}
                  title="Reload iframe"
                  aria-label="Reload iframe"
                >
                  Reload
                </button>
              )}
              <button
                className="embed-btn"
                onClick={() => setShowSettings(!showSettings)}
                title="Settings"
              >
                {showSettings ? "Close" : "Settings"}
              </button>
            </div>
          </div>

          {/* Settings panel */}
          {showSettings && (
            <div className="embed-settings">
              {def.fields.map((f) => (
                <FieldRow
                  key={f.name}
                  field={f}
                  value={node.attrs[f.name]}
                  onChange={(v) => handleFieldChange(f.name, v)}
                />
              ))}
            </div>
          )}

          {/* Preview */}
          {hasSrc ? (
            <div className="embed-preview">
              <iframe
                key={iframeKey}
                ref={iframeRef}
                src={src}
                title={title || src}
                className="embed-iframe"
                referrerPolicy="no-referrer-when-downgrade"
                allow="accelerometer; autoplay; clipboard-write; encrypted-media; fullscreen; gyroscope; picture-in-picture"
                style={{
                  height: iframeHeight,
                  minHeight: Math.min(storedHeight, SAFETY_MIN_HEIGHT),
                }}
              />
            </div>
          ) : (
            <div className="embed-empty">
              <span className="embed-empty-icon">{def.icon}</span>
              <span>Enter a URL in settings to embed a page</span>
              <button
                className="embed-btn embed-btn-primary"
                onClick={() => setShowSettings(true)}
              >
                Open Settings
              </button>
            </div>
          )}
        </div>
      </NodeViewWrapper>
    );
  }

  IframeEmbedNodeView.displayName = "IframeEmbedView";
  return IframeEmbedNodeView;
}
