"use client";

import * as React from "react";
import { useEditor, EditorContent, type Editor } from "@tiptap/react";
import { BubbleMenu } from "@tiptap/react/menus";
import StarterKit from "@tiptap/starter-kit";
import Placeholder from "@tiptap/extension-placeholder";
import Image from "@tiptap/extension-image";
import TaskList from "@tiptap/extension-task-list";
import TaskItem from "@tiptap/extension-task-item";
import Highlight from "@tiptap/extension-highlight";
import Typography from "@tiptap/extension-typography";
import Underline from "@tiptap/extension-underline";
import TextAlign from "@tiptap/extension-text-align";
import HorizontalRule from "@tiptap/extension-horizontal-rule";
import CodeBlock from "@tiptap/extension-code-block";
import { Table, TableRow, TableCell, TableHeader } from "@tiptap/extension-table";
import {
  Bold,
  Italic,
  Underline as UnderlineIcon,
  Strikethrough,
  Code,
  Link2,
} from "lucide-react";

import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from "@/components/ui/tooltip";

import { SlashCommand } from "./slash-menu";
import { EditorToolbar } from "./toolbar";
import { BlockDragHandle } from "./drag-handle";
import { Callout } from "./extensions/callout";
import { Toggle } from "./extensions/toggle";
import { Mention } from "./extensions/mention";
import { Embed } from "./extensions/embed";
import { Divider } from "./extensions/divider";
import { AiBlock } from "./extensions/ai-block";

function bubbleCh(editor: Editor) {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  return editor.chain().focus() as any;
}

const editorContentStyles = cn(
  "editor-prose min-h-[280px] w-full max-w-none px-14 py-10 text-[15px] leading-7 text-foreground antialiased",
  "focus:outline-none [&_.ProseMirror]:outline-none [&_.ProseMirror]:min-h-[240px]",
  "[&_.ProseMirror_p.is-empty]:relative [&_.ProseMirror_p.is-empty::before]:pointer-events-none [&_.ProseMirror_p.is-empty::before]:float-left [&_.ProseMirror_p.is-empty::before]:h-0 [&_.ProseMirror_p.is-empty::before]:text-muted-foreground [&_.ProseMirror_p.is-empty::before]:content-[attr(data-placeholder)]",
  "[&_.ProseMirror_p]:my-1 [&_.ProseMirror_p]:leading-7",
  "[&_.ProseMirror_h1]:mb-3 [&_.ProseMirror_h1]:mt-8 [&_.ProseMirror_h1]:text-3xl [&_.ProseMirror_h1]:font-bold [&_.ProseMirror_h1]:tracking-tight [&_.ProseMirror_h1]:text-foreground",
  "[&_.ProseMirror_h2]:mb-2 [&_.ProseMirror_h2]:mt-6 [&_.ProseMirror_h2]:text-2xl [&_.ProseMirror_h2]:font-semibold [&_.ProseMirror_h2]:tracking-tight",
  "[&_.ProseMirror_h3]:mb-2 [&_.ProseMirror_h3]:mt-5 [&_.ProseMirror_h3]:text-xl [&_.ProseMirror_h3]:font-semibold",
  "[&_.ProseMirror_ul]:my-2 [&_.ProseMirror_ul]:list-disc [&_.ProseMirror_ul]:pl-6",
  "[&_.ProseMirror_ol]:my-2 [&_.ProseMirror_ol]:list-decimal [&_.ProseMirror_ol]:pl-6",
  "[&_.ProseMirror_li]:my-0.5 [&_.ProseMirror_li_p]:my-0",
  "[&_.ProseMirror_blockquote]:my-3 [&_.ProseMirror_blockquote]:border-l-4 [&_.ProseMirror_blockquote]:border-teal [&_.ProseMirror_blockquote]:bg-muted/40 [&_.ProseMirror_blockquote]:py-1 [&_.ProseMirror_blockquote]:pl-4 [&_.ProseMirror_blockquote]:pr-2 [&_.ProseMirror_blockquote]:italic [&_.ProseMirror_blockquote]:text-muted-foreground",
  "[&_.ProseMirror_pre]:my-4 [&_.ProseMirror_pre]:overflow-x-auto [&_.ProseMirror_pre]:rounded-lg [&_.ProseMirror_pre]:border [&_.ProseMirror_pre]:border-border [&_.ProseMirror_pre]:bg-muted/80 [&_.ProseMirror_pre]:p-4 [&_.ProseMirror_pre]:font-mono [&_.ProseMirror_pre]:text-sm",
  "[&_.ProseMirror_code]:rounded-md [&_.ProseMirror_code]:bg-muted [&_.ProseMirror_code]:px-1.5 [&_.ProseMirror_code]:py-0.5 [&_.ProseMirror_code]:font-mono [&_.ProseMirror_code]:text-[0.9em] [&_.ProseMirror_code]:text-foreground",
  "[&_.ProseMirror_pre_code]:bg-transparent [&_.ProseMirror_pre_code]:p-0",
  "[&_.ProseMirror_hr]:my-6 [&_.ProseMirror_hr]:border-border",
  "[&_.ProseMirror_img]:my-4 [&_.ProseMirror_img]:max-h-[480px] [&_.ProseMirror_img]:w-auto [&_.ProseMirror_img]:max-w-full [&_.ProseMirror_img]:rounded-lg [&_.ProseMirror_img]:border [&_.ProseMirror_img]:border-border",
  "[&_.ProseMirror_table]:my-4 [&_.ProseMirror_table]:w-full [&_.ProseMirror_table]:border-collapse [&_.ProseMirror_table]:overflow-hidden [&_.ProseMirror_table]:rounded-md [&_.ProseMirror_table]:border [&_.ProseMirror_table]:border-border",
  "[&_.ProseMirror_td]:border [&_.ProseMirror_td]:border-border [&_.ProseMirror_td]:bg-background [&_.ProseMirror_td]:px-3 [&_.ProseMirror_td]:py-2",
  "[&_.ProseMirror_th]:border [&_.ProseMirror_th]:border-border [&_.ProseMirror_th]:bg-muted [&_.ProseMirror_th]:px-3 [&_.ProseMirror_th]:py-2 [&_.ProseMirror_th]:text-left [&_.ProseMirror_th]:font-medium",
  "[&_.ProseMirror_.ProseMirror-selectednode]:ring-2 [&_.ProseMirror_.ProseMirror-selectednode]:ring-primary/40",
  "dark:[&_.ProseMirror_blockquote]:bg-muted/20",
);

export type BlockEditorProps = {
  content?: string;
  onChange?: (html: string) => void;
  editable?: boolean;
  className?: string;
  placeholder?: string;
};

export function BlockEditor({
  content,
  onChange,
  editable = true,
  className,
  placeholder,
}: BlockEditorProps) {
  const containerRef = React.useRef<HTMLDivElement>(null);

  const extensions = React.useMemo(
    () => [
      StarterKit.configure({
        heading: { levels: [1, 2, 3] },
        codeBlock: false,
        horizontalRule: false,
        link: {
          openOnClick: false,
          HTMLAttributes: {
            class:
              "text-primary underline underline-offset-2 decoration-primary/50",
          },
        },
      }),
      Placeholder.configure({
        placeholder:
          placeholder ??
          'Write something, or type "/" for commands…',
      }),
      CodeBlock.configure({
        HTMLAttributes: {
          class: "font-mono text-sm",
        },
      }),
      HorizontalRule,
      Image.configure({
        HTMLAttributes: {
          class: "rounded-lg border border-border",
        },
      }),
      TaskList,
      TaskItem.configure({
        nested: true,
        HTMLAttributes: {
          class: "flex gap-2",
        },
      }),
      Highlight.configure({
        multicolor: true,
      }),
      Typography,
      Underline,
      TextAlign.configure({
        types: ["heading", "paragraph"],
      }),
      Table.configure({
        resizable: false,
      }),
      TableRow,
      TableHeader,
      TableCell,
      SlashCommand,
      Callout,
      Toggle,
      Mention,
      Embed,
      Divider,
      AiBlock,
    ],
    [placeholder],
  );

  const editor = useEditor(
    {
      immediatelyRender: false,
      extensions,
      content: content ?? "<p></p>",
      editable,
      editorProps: {
        attributes: {
          class: "focus:outline-none",
        },
      },
      onUpdate: ({ editor: ed }) => {
        onChange?.(ed.getHTML());
      },
    },
    [extensions, editable],
  );

  React.useEffect(() => {
    if (!editor) return;
    editor.setEditable(editable);
  }, [editable, editor]);

  return (
    <TooltipProvider delayDuration={200}>
      <div
        className={cn(
          "group/editor overflow-hidden rounded-xl border border-border bg-card text-card-foreground shadow-sm",
          className,
        )}
      >
        <EditorToolbar editor={editor} />
        <div ref={containerRef} className="relative bg-background dark:bg-background">
          <BlockDragHandle editor={editor} containerRef={containerRef} />
          <EditorContent editor={editor} className={editorContentStyles} />
          {editor ? (
            <BubbleMenu
              editor={editor}
              options={{
                placement: "top",
              }}
              className="flex items-center gap-0.5 rounded-lg border border-border bg-popover p-1 shadow-lg"
            >
              <BubbleBtn
                label="Bold"
                shortcut="⌘B"
                active={editor.isActive("bold")}
                onClick={() => bubbleCh(editor).toggleBold().run()}
              >
                <Bold className="size-4" />
              </BubbleBtn>
              <BubbleBtn
                label="Italic"
                shortcut="⌘I"
                active={editor.isActive("italic")}
                onClick={() => bubbleCh(editor).toggleItalic().run()}
              >
                <Italic className="size-4" />
              </BubbleBtn>
              <BubbleBtn
                label="Underline"
                shortcut="⌘U"
                active={editor.isActive("underline")}
                onClick={() => bubbleCh(editor).toggleUnderline().run()}
              >
                <UnderlineIcon className="size-4" />
              </BubbleBtn>
              <BubbleBtn
                label="Strikethrough"
                shortcut="⌘⇧S"
                active={editor.isActive("strike")}
                onClick={() => bubbleCh(editor).toggleStrike().run()}
              >
                <Strikethrough className="size-4" />
              </BubbleBtn>
              <BubbleBtn
                label="Code"
                shortcut="⌘E"
                active={editor.isActive("code")}
                onClick={() => bubbleCh(editor).toggleCode().run()}
              >
                <Code className="size-4" />
              </BubbleBtn>
              <BubbleBtn
                label="Link"
                shortcut="⌘K"
                active={editor.isActive("link")}
                onClick={() => {
                  const prev = editor.getAttributes("link").href as
                    | string
                    | undefined;
                  const href = window.prompt("URL", prev ?? "https://");
                  if (href === null) return;
                  if (href === "") {
                    bubbleCh(editor).unsetLink().run();
                    return;
                  }
                  bubbleCh(editor).setLink({ href }).run();
                }}
              >
                <Link2 className="size-4" />
              </BubbleBtn>
            </BubbleMenu>
          ) : null}
        </div>
      </div>
    </TooltipProvider>
  );
}

function BubbleBtn({
  label,
  shortcut,
  active,
  onClick,
  children,
}: {
  label: string;
  shortcut: string;
  active: boolean;
  onClick: () => void;
  children: React.ReactNode;
}) {
  return (
    <Tooltip>
      <TooltipTrigger asChild>
        <Button
          type="button"
          variant="ghost"
          size="icon"
          className={cn(
            "size-8",
            active && "bg-primary/15 text-primary hover:bg-primary/20",
          )}
          onClick={onClick}
        >
          {children}
        </Button>
      </TooltipTrigger>
      <TooltipContent side="top">
        {label}
        <span className="ml-2 text-xs opacity-70">{shortcut}</span>
      </TooltipContent>
    </Tooltip>
  );
}
