"use client";

import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
import { EditorContent, Node, useEditor, useEditorState } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import { Placeholder } from "@tiptap/extensions";
import { Bold, Italic, Link2, List, ListOrdered, RemoveFormatting, Underline } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Separator } from "@/components/ui/separator";
import { Skeleton } from "@/components/ui/skeleton";
import { Toggle } from "@/components/ui/toggle";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { cn } from "@/lib/utils";

/**
 * Rich text (HTML) editor for email bodies, signatures and templates (Tiptap v3, styled with theme tokens).
 *   <RichTextEditor value={html} onChange={setHtml} placeholder="Write your message…" aria-label="Message" />
 * - `value` / `onChange` are HTML strings; an empty editor reports "".
 * - Tables that arrive in the value (e.g. a "PO lines table" merge block) are kept intact as one read-only block
 *   that can be moved or deleted but not edited, so generated tables are never flattened.
 * - The ref exposes `insertText(text)` (merge-field tokens at the cursor) and `focus()`.
 * - `toolbarEnd` renders extra controls at the right end of the toolbar.
 */
export const RichTextEditor = forwardRef(function RichTextEditor({ id, value = "", onChange, placeholder = "Write your message…", invalid = false, disabled = false, minHeight = "min-h-48", toolbarEnd, className, "aria-label": ariaLabel, "aria-describedby": describedBy }, ref) {
  const editor = useEditor({
    immediatelyRender: false,
    editable: !disabled,
    extensions: [
      StarterKit.configure({
        heading: false,
        code: false,
        codeBlock: false,
        horizontalRule: false,
        link: { openOnClick: false, autolink: true, defaultProtocol: "https", HTMLAttributes: { rel: "noopener noreferrer", target: "_blank" } },
      }),
      Placeholder.configure({ placeholder }),
      HtmlTableBlock,
    ],
    content: value || "",
    editorProps: {
      attributes: {
        ...(id ? { id } : {}),
        role: "textbox",
        "aria-multiline": "true",
        ...(ariaLabel ? { "aria-label": ariaLabel } : {}),
        ...(describedBy ? { "aria-describedby": describedBy } : {}),
        ...(invalid ? { "aria-invalid": "true" } : {}),
        class: cn("px-3 py-2.5 text-sm leading-relaxed outline-none", minHeight, CONTENT_CLASSES),
      },
    },
    onUpdate: ({ editor: current }) => onChange?.(current.isEmpty ? "" : current.getHTML()),
  });

  // Outside changes (template applied, draft loaded) replace the content; our own edits are ignored here.
  useEffect(() => {
    if (!editor || editor.isDestroyed) return;
    const current = editor.isEmpty ? "" : editor.getHTML();
    if ((value || "") !== current) editor.commands.setContent(value || "", { emitUpdate: false });
  }, [editor, value]);

  useEffect(() => { editor?.setEditable(!disabled); }, [disabled, editor]);

  useImperativeHandle(ref, () => ({
    insertText: (text) => editor?.chain().focus().insertContent(text).run(),
    focus: () => editor?.commands.focus(),
  }), [editor]);

  return <div className={cn(
    "overflow-hidden rounded-md border border-input bg-transparent shadow-xs transition-[color,box-shadow] focus-within:border-ring focus-within:ring-[3px] focus-within:ring-ring/50 dark:bg-input/30",
    invalid && "border-destructive ring-destructive/20",
    disabled && "opacity-60",
    className,
  )}>
    {editor ? <Toolbar editor={editor} disabled={disabled} end={toolbarEnd} /> : <div className="flex h-10 items-center gap-1 border-b px-1.5"><Skeleton className="h-6 w-40" /></div>}
    {editor ? <EditorContent editor={editor} /> : <div className={cn("grid content-start gap-2 p-3", minHeight)}><Skeleton className="h-4 w-3/4" /><Skeleton className="h-4 w-1/2" /></div>}
  </div>;
});

// Theme-token styling for the editable HTML (no CSS file): paragraphs, lists, links, quotes, kept tables, placeholder.
const CONTENT_CLASSES = [
  "[&_p]:my-1.5 first:[&_p]:mt-0 [&_ul]:my-1.5 [&_ul]:list-disc [&_ul]:pl-6 [&_ol]:my-1.5 [&_ol]:list-decimal [&_ol]:pl-6",
  "[&_a]:text-foreground [&_a]:underline [&_a]:underline-offset-2 [&_strong]:font-semibold",
  "[&_blockquote]:border-l-2 [&_blockquote]:pl-3 [&_blockquote]:text-muted-foreground",
  "[&_table]:my-2 [&_table]:w-full [&_table]:border-collapse [&_table]:text-xs [&_th]:border [&_th]:bg-muted/50 [&_th]:px-2 [&_th]:py-1 [&_th]:text-left [&_th]:font-medium [&_td]:border [&_td]:px-2 [&_td]:py-1",
  "[&_.ProseMirror-selectednode]:outline-2 [&_.ProseMirror-selectednode]:outline-ring",
  "[&_p.is-editor-empty:first-child]:before:pointer-events-none [&_p.is-editor-empty:first-child]:before:float-left [&_p.is-editor-empty:first-child]:before:h-0 [&_p.is-editor-empty:first-child]:before:text-muted-foreground [&_p.is-editor-empty:first-child]:before:content-[attr(data-placeholder)]",
].join(" ");

// A <table> is stored whole as one atom node: generated tables (PO lines, shipment documents) survive editing.
const HtmlTableBlock = Node.create({
  name: "htmlTableBlock",
  group: "block",
  atom: true,
  selectable: true,
  draggable: true,
  addAttributes() {
    return { html: { default: "" } };
  },
  parseHTML() {
    return [{ tag: "table", priority: 60, getAttrs: (element) => ({ html: element.outerHTML }) }];
  },
  renderHTML({ node }) {
    const wrapper = document.createElement("div");
    wrapper.innerHTML = node.attrs.html;
    return wrapper.firstElementChild || wrapper;
  },
});

function Toolbar({ editor, disabled, end }) {
  const active = useEditorState({
    editor,
    selector: ({ editor: current }) => ({
      bold: current.isActive("bold"),
      italic: current.isActive("italic"),
      underline: current.isActive("underline"),
      bulletList: current.isActive("bulletList"),
      orderedList: current.isActive("orderedList"),
      link: current.isActive("link"),
    }),
  });

  const mark = (label, Icon, pressed, toggle, shortcut) => <ToolbarTip label={shortcut ? `${label} (${shortcut})` : label}>
    <Toggle size="sm" aria-label={label} pressed={pressed} disabled={disabled} onPressedChange={toggle} onMouseDown={(event) => event.preventDefault()}><Icon /></Toggle>
  </ToolbarTip>;

  return <div className="flex flex-wrap items-center gap-0.5 border-b bg-muted/30 px-1.5 py-1" role="toolbar" aria-label="Formatting">
    {mark("Bold", Bold, active.bold, () => editor.chain().focus().toggleBold().run(), "Ctrl+B")}
    {mark("Italic", Italic, active.italic, () => editor.chain().focus().toggleItalic().run(), "Ctrl+I")}
    {mark("Underline", Underline, active.underline, () => editor.chain().focus().toggleUnderline().run(), "Ctrl+U")}
    <Separator orientation="vertical" className="mx-1 h-5!" />
    {mark("Bulleted list", List, active.bulletList, () => editor.chain().focus().toggleBulletList().run())}
    {mark("Numbered list", ListOrdered, active.orderedList, () => editor.chain().focus().toggleOrderedList().run())}
    <Separator orientation="vertical" className="mx-1 h-5!" />
    <LinkControl editor={editor} active={active.link} disabled={disabled} />
    <ToolbarTip label="Clear formatting">
      <Button type="button" variant="ghost" size="icon-sm" aria-label="Clear formatting" disabled={disabled} onMouseDown={(event) => event.preventDefault()} onClick={() => editor.chain().focus().unsetAllMarks().clearNodes().run()}><RemoveFormatting /></Button>
    </ToolbarTip>
    {end && <div className="ml-auto flex items-center gap-1">{end}</div>}
  </div>;
}

function ToolbarTip({ label, children }) {
  return <Tooltip><TooltipTrigger asChild>{children}</TooltipTrigger><TooltipContent>{label}</TooltipContent></Tooltip>;
}

function LinkControl({ editor, active, disabled }) {
  const [open, setOpen] = useState(false);
  const [href, setHref] = useState("");

  const openChange = (next) => {
    if (next) setHref(editor.getAttributes("link").href || "");
    setOpen(next);
  };
  const apply = () => {
    const url = href.trim();
    const chain = editor.chain().focus().extendMarkRange("link");
    if (!url) chain.unsetLink().run();
    else chain.setLink({ href: /^(https?:|mailto:)/i.test(url) ? url : `https://${url}` }).run();
    setOpen(false);
  };

  return <Popover open={open} onOpenChange={openChange}>
    <Tooltip>
      <TooltipTrigger asChild>
        <PopoverTrigger asChild>
          <Toggle size="sm" aria-label="Link" pressed={active} disabled={disabled} onMouseDown={(event) => event.preventDefault()}><Link2 /></Toggle>
        </PopoverTrigger>
      </TooltipTrigger>
      <TooltipContent>Link</TooltipContent>
    </Tooltip>
    <PopoverContent align="start" className="grid w-80 gap-2">
      <label htmlFor="rte-link" className="text-sm font-medium">Link address</label>
      <Input id="rte-link" autoFocus value={href} placeholder="https://… or mailto:…" onChange={(event) => setHref(event.target.value)}
        onKeyDown={(event) => { if (event.key === "Enter") { event.preventDefault(); apply(); } }} />
      <div className="flex justify-end gap-2">
        {active && <Button type="button" variant="ghost" size="sm" onClick={() => { editor.chain().focus().extendMarkRange("link").unsetLink().run(); setOpen(false); }}>Remove link</Button>}
        <Button type="button" size="sm" onClick={apply}>Apply</Button>
      </div>
    </PopoverContent>
  </Popover>;
}
