"use client";

import type { Editor } from "@tiptap/react";
import { useEditorState } from "@tiptap/react";
import { AlignLeft, AlignCenter, AlignRight, AlignJustify, Trash2 } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Separator } from "@/components/ui/separator";
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
import { createPortal } from "react-dom";

const WIDTHS = ["25%", "50%", "75%", "100%"];
const ALIGNMENTS = [
  { value: "none", icon: AlignJustify, label: "Default" },
  { value: "left", icon: AlignLeft, label: "Float Left" },
  { value: "center", icon: AlignCenter, label: "Center" },
  { value: "right", icon: AlignRight, label: "Float Right" },
] as const;

export function ImageBubbleMenu({ editor }: { editor: Editor }) {
  const [alt, setAlt] = useState("");
  const [rect, setRect] = useState<{ top: number; left: number; width: number } | null>(null);
  // keepOpen: stays true while user is interacting with the menu (e.g. typing alt text)
  const [keepOpen, setKeepOpen] = useState(false);
  // saved node position — lets us dispatch updates even after editor loses focus
  const savedPosRef = useRef<number | null>(null);
  const menuRef = useRef<HTMLDivElement>(null);

  const { isImageActive } = useEditorState({
    editor,
    selector: (ctx) => ({
      isImageActive: ctx.editor.isActive("image"),
    }),
  });

  // When an image becomes the active selection → save position + sync alt text
  useEffect(() => {
    if (isImageActive) {
      const { from } = editor.state.selection;
      savedPosRef.current = from;
      setAlt(editor.getAttributes("image").alt ?? "");
      setKeepOpen(false);
    }
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [isImageActive]);

  // Close keepOpen when the user clicks outside the menu
  useEffect(() => {
    if (!keepOpen) return;
    const handler = (e: MouseEvent) => {
      if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
        setKeepOpen(false);
      }
    };
    document.addEventListener("mousedown", handler);
    return () => document.removeEventListener("mousedown", handler);
  }, [keepOpen]);

  // Track the DOM position of the selected image so we can position the menu
  useLayoutEffect(() => {
    const pos = savedPosRef.current;
    if ((!isImageActive && !keepOpen) || pos === null) { setRect(null); return; }

    let dom: Element | null = null;
    try {
      const nodeDOM = editor.view.nodeDOM(pos);
      if (nodeDOM instanceof Element) dom = nodeDOM;
      else if (nodeDOM instanceof Node) dom = (nodeDOM as HTMLElement).querySelector?.("img") ?? null;
    } catch { /* noop */ }

    if (!dom) { setRect(null); return; }
    const r = dom.getBoundingClientRect();
    setRect({ top: r.top + window.scrollY, left: r.left + window.scrollX, width: r.width });
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [isImageActive, keepOpen, editor.state.selection]);

  // Directly dispatch a setNodeMarkup transaction — works without editor focus
  const updateAttr = useCallback(
    (attrs: Record<string, unknown>) => {
      const pos = savedPosRef.current;
      if (pos === null) return;
      const node = editor.state.doc.nodeAt(pos);
      if (!node) return;
      const tr = editor.state.tr.setNodeMarkup(pos, undefined, { ...node.attrs, ...attrs });
      editor.view.dispatch(tr);
    },
    [editor],
  );

  const isVisible = isImageActive || keepOpen;
  if (!isVisible || !rect) return null;

  // Read current attrs directly from saved doc position
  const nodeAttrs =
    savedPosRef.current !== null
      ? (editor.state.doc.nodeAt(savedPosRef.current)?.attrs ?? {})
      : {};
  const activeAlign: string = nodeAttrs.align ?? "none";
  const activeWidth: string | undefined = nodeAttrs.width;

  // Position the menu centred above the image
  const menuWidth = 480;
  const leftClamped = Math.max(8, Math.min(rect.left + rect.width / 2 - menuWidth / 2, window.innerWidth - menuWidth - 8));

  const menu = (
    <div
      ref={menuRef}
      style={{ position: "absolute", top: rect.top - 56, left: leftClamped, zIndex: 50 }}
    >
      <div className="bg-popover border border-border rounded-lg shadow-xl p-2 flex flex-wrap items-center gap-1.5 max-w-sm">
        {/* Alt text — focus this input naturally; keepOpen keeps the menu alive */}
        <Input
          value={alt}
          onFocus={() => setKeepOpen(true)}
          onChange={(e) => {
            setAlt(e.target.value);
            updateAttr({ alt: e.target.value });
          }}
          placeholder="Alt text (SEO)…"
          className="h-7 w-36 text-xs"
        />

        <Separator orientation="vertical" className="h-5" />

        {/* Alignment */}
        {ALIGNMENTS.map(({ value, icon: Icon, label }) => (
          <Button
            key={value}
            type="button"
            variant="ghost"
            size="icon"
            className={`h-7 w-7 ${activeAlign === value ? "bg-accent text-accent-foreground" : "text-muted-foreground"}`}
            title={label}
            onMouseDown={(e) => e.preventDefault()}
            onClick={() => updateAttr({ align: value })}
          >
            <Icon size={12} />
          </Button>
        ))}

        <Separator orientation="vertical" className="h-5" />

        {/* Width */}
        {WIDTHS.map((w) => (
          <Button
            key={w}
            type="button"
            variant="ghost"
            size="sm"
            className={`h-7 px-1.5 text-xs ${activeWidth === w ? "bg-accent text-accent-foreground" : "text-muted-foreground"}`}
            onMouseDown={(e) => e.preventDefault()}
            onClick={() => updateAttr({ width: w })}
          >
            {w}
          </Button>
        ))}

        <Separator orientation="vertical" className="h-5" />

        {/* Delete */}
        <Button
          type="button"
          variant="ghost"
          size="icon"
          className="h-7 w-7 text-destructive hover:text-destructive hover:bg-destructive/10"
          title="Remove image"
          onMouseDown={(e) => e.preventDefault()}
          onClick={() => { setKeepOpen(false); editor.chain().focus().deleteSelection().run(); }}
        >
          <Trash2 size={12} />
        </Button>
      </div>
    </div>
  );

  // Render into document.body so it's not clipped by overflow:hidden containers
  if (typeof document === "undefined") return null;
  return createPortal(menu, document.body);
}

