"use client";
import { useIsMobile } from "@/hooks/use-mobile";
import Mention from "@tiptap/extension-mention";
import {
  EditorContent,
  Range,
  useEditor,
  UseEditorOptions,
  Editor,
} from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import { TipTapMentionJsonContent } from "app-types/util";
import { cn } from "lib/utils";
import {
  FC,
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
  RefObject,
} from "react";
import { createPortal } from "react-dom";
import { createRoot } from "react-dom/client";

interface MentionInputProps {
  disabled?: boolean;
  defaultContent?: TipTapMentionJsonContent | string;
  content?: TipTapMentionJsonContent | string;
  onChange?: (content: {
    json: TipTapMentionJsonContent;
    text: string;
    mentions: { label: string; id: string }[];
  }) => void;
  onEnter?: () => void;
  placeholder?: string;
  suggestionChar?: string;
  className?: string;
  disabledMention?: boolean;
  editorRef?: RefObject<Editor | null>;
  onFocus?: () => void;
  onBlur?: () => void;
  fullWidthSuggestion?: boolean;
  MentionItem?: FC<{
    label: string;
    id: string;
  }>;
  Suggestion?: FC<{
    top: number;
    left: number;
    onClose: () => void;
    onSelectMention: (item: { label: string; id: string }) => void;
    style?: React.CSSProperties;
  }>;
}

export default function MentionInput({
  defaultContent,
  content,
  onChange,
  disabled,
  onEnter,
  placeholder = "",
  suggestionChar = "@",
  MentionItem,
  disabledMention,
  Suggestion,
  className,
  editorRef,
  onFocus,
  onBlur,
  fullWidthSuggestion = false,
}: MentionInputProps) {
  const isMobile = useIsMobile();
  const [open, setOpen] = useState(false);
  const position = useRef<{
    top: number;
    left: number;
    range: Range;
  } | null>(null);
  const containerRef = useRef<HTMLDivElement>(null);
  const [containerWidth, setContainerWidth] = useState<number | undefined>(
    undefined,
  );
  const latestContent = useRef<{
    json: TipTapMentionJsonContent;
    text: string;
  } | null>(null);

  // Memoize editor configuration
  const editorConfig = useMemo<UseEditorOptions>(() => {
    return {
      editable: !disabled,
      immediatelyRender: false,
      extensions: [
        StarterKit.configure({
          codeBlock: false,
          blockquote: false,
          code: false,
        }),
        Mention.configure({
          HTMLAttributes: {
            class: "mention",
          },
          renderHTML: (props) => {
            const el = document.createElement("div");
            el.className = "inline-flex";
            const root = createRoot(el);
            if (MentionItem)
              root.render(
                <MentionItem
                  label={props.node.attrs.label}
                  id={props.node.attrs.id}
                />,
              );
            return el;
          },
          suggestion: {
            char: suggestionChar,
            render: () => {
              return {
                onStart: (props) => {
                  if (fullWidthSuggestion) {
                    const containerRect =
                      containerRef.current?.getBoundingClientRect();
                    if (containerRect) {
                      position.current = {
                        top: containerRect.top,
                        left: containerRect.left,
                        range: props.range,
                      };
                      setContainerWidth(containerRect.width);
                      setOpen(true);
                    }
                  } else {
                    const rect = props.clientRect?.();
                    if (rect) {
                      position.current = {
                        top: rect.top,
                        left: rect.left,
                        range: props.range,
                      };
                      setContainerWidth(undefined);
                      setOpen(true);
                    }
                  }
                },
                onExit: () => setOpen(false),
              };
            },
          },
        }),
      ],
      content: defaultContent ?? content,
      autofocus: true,
      onUpdate: ({ editor }) => {
        const json = editor.getJSON() as TipTapMentionJsonContent;
        const text = editor.getText();
        const mentions = json?.content
          ?.flatMap(({ content }) => {
            return content
              ?.filter((v) => v.type == "mention")
              .map(
                (v) =>
                  (
                    v as {
                      type: "mention";
                      attrs: {
                        id: string;
                        label: string;
                      };
                    }
                  ).attrs,
              );
          })
          .filter(Boolean) as { label: string; id: string }[];
        latestContent.current = {
          json,
          text,
        };
        onChange?.({
          json,
          text,
          mentions,
        });
      },
      onFocus: () => {
        onFocus?.();
      },
      onBlur: () => {
        onBlur?.();
      },
      editorProps: {
        attributes: {
          class:
            "w-full max-h-80 min-h-[2rem] break-words overflow-y-auto resize-none focus:outline-none px-2 py-1 prose prose-sm dark:prose-invert ",
        },
      },
    };
  }, [disabled, MentionItem, suggestionChar, onChange]);

  const editor = useEditor(editorConfig);

  // Expose editor through ref
  useEffect(() => {
    if (editorRef && editor) {
      editorRef.current = editor;
    }
  }, [editor]);

  useEffect(() => {
    editor?.setEditable(!disabled);
  }, [disabled]);

  // Memoize handlers
  const handleKeyDown = useCallback(
    (e: React.KeyboardEvent) => {
      const isSubmit =
        !open &&
        e.key === "Enter" &&
        editor?.getText().trim().length &&
        !e.shiftKey &&
        !e.metaKey &&
        !e.nativeEvent.isComposing;
      if (isSubmit) {
        e.preventDefault();
        onEnter?.();
        if (isMobile) {
          editor?.commands.blur();
        }
      }
    },
    [editor, onEnter, open],
  );

  // Memoize the DOM structure
  const suggestion = useMemo(() => {
    if (!open || disabledMention) return null;
    if (!Suggestion) return null;
    return createPortal(
      <Suggestion
        top={position.current?.top ?? 0}
        left={position.current?.left ?? 0}
        onClose={() => {
          setOpen(false);
        }}
        onSelectMention={(item) => {
          editor
            ?.chain()
            .focus()
            .insertContentAt(position.current!.range, [
              {
                type: "mention",
                attrs: item,
              },
            ])
            .run();
          setOpen(false);
        }}
        style={{
          width:
            fullWidthSuggestion && containerWidth
              ? `${containerWidth}px`
              : undefined,
        }}
      />,
      document.body,
    );
  }, [open, disabledMention, containerWidth, fullWidthSuggestion]);

  const placeholderElement = useMemo(() => {
    if (!editor?.isEmpty) return null;

    return (
      <div className="absolute top-1 left-2 text-muted-foreground pointer-events-none">
        {placeholder}
      </div>
    );
  }, [editor?.isEmpty, placeholder]);

  useEffect(() => {
    if (open) {
      return () => {
        editor?.commands.focus();
      };
    }
    position.current = null;
    editor?.commands.focus();
  }, [open]);

  useEffect(() => {
    if (content != undefined && onChange) {
      if (
        typeof content == "string" &&
        content != latestContent.current?.text
      ) {
        editor?.commands.setContent(content);
      } else if (
        typeof content != "string" &&
        content != latestContent.current?.json
      ) {
        editor?.commands.setContent(content);
      }
    }
  }, [content]);

  const focus = useCallback(() => {
    editor?.commands.focus();
  }, [editor]);

  return (
    <div
      ref={containerRef}
      onClick={focus}
      className={cn("relative w-full", className)}
    >
      <EditorContent editor={editor} onKeyDown={handleKeyDown} />
      {suggestion}
      {placeholderElement}
    </div>
  );
}
