"use client";

import { Node, mergeAttributes } from "@tiptap/react";
import {
  NodeViewContent,
  NodeViewWrapper,
  ReactNodeViewRenderer,
  type NodeViewProps,
} from "@tiptap/react";
import {
  AlertTriangle,
  CheckCircle2,
  Info,
  XCircle,
} from "lucide-react";
import * as React from "react";

import { cn } from "@/lib/utils";

export type CalloutType = "info" | "warning" | "success" | "error";

const TYPE_ICON: Record<
  CalloutType,
  React.ComponentType<{ className?: string }>
> = {
  info: Info,
  warning: AlertTriangle,
  success: CheckCircle2,
  error: XCircle,
};

const TYPE_STYLES: Record<
  CalloutType,
  { box: string; icon: string }
> = {
  info: {
    box: "border border-blue-500/25 bg-blue-500/10 dark:bg-blue-500/15 dark:border-blue-400/30",
    icon: "text-blue-600 dark:text-blue-400",
  },
  warning: {
    box: "border border-amber-500/30 bg-amber-500/10 dark:bg-amber-500/15 dark:border-amber-400/35",
    icon: "text-amber-700 dark:text-amber-400",
  },
  success: {
    box: "border border-emerald-500/25 bg-emerald-500/10 dark:bg-emerald-500/15 dark:border-emerald-400/30",
    icon: "text-emerald-700 dark:text-emerald-400",
  },
  error: {
    box: "border border-red-500/25 bg-red-500/10 dark:bg-red-500/15 dark:border-red-400/30",
    icon: "text-red-600 dark:text-red-400",
  },
};

const DEFAULT_EMOJI: Record<CalloutType, string> = {
  info: "💡",
  warning: "⚠️",
  success: "✅",
  error: "⛔",
};

function CalloutView(props: NodeViewProps) {
  const { node, updateAttributes, selected } = props;
  const type = (node.attrs.type as CalloutType) ?? "info";
  const emoji =
    typeof node.attrs.emoji === "string" && node.attrs.emoji.length > 0
      ? node.attrs.emoji
      : DEFAULT_EMOJI[type];
  const Icon = TYPE_ICON[type];
  const styles = TYPE_STYLES[type];

  return (
    <NodeViewWrapper
      className={cn(
        "callout-block my-3 flex gap-3 rounded-lg px-3 py-2.5",
        styles.box,
        selected && "ring-2 ring-primary/40 ring-offset-2 ring-offset-background",
      )}
      data-type="callout"
      data-callout-type={type}
    >
      <div className="flex shrink-0 flex-col items-center gap-1 pt-0.5">
        <span
          className="cursor-pointer select-none text-lg leading-none"
          contentEditable={false}
          onClick={() => {
            const order: CalloutType[] = [
              "info",
              "warning",
              "success",
              "error",
            ];
            const next = order[(order.indexOf(type) + 1) % order.length];
            updateAttributes({
              type: next,
              emoji: DEFAULT_EMOJI[next],
            });
          }}
          title="Cycle callout type"
        >
          {emoji}
        </span>
        <Icon className={cn("size-4", styles.icon)} aria-hidden />
      </div>
      <div className="min-w-0 flex-1 [&_.ProseMirror_p]:my-1 [&_.ProseMirror_p:first-child]:mt-0 [&_.ProseMirror_p:last-child]:mb-0">
        <NodeViewContent className="callout-content outline-none" />
      </div>
    </NodeViewWrapper>
  );
}

export const Callout = Node.create({
  name: "callout",
  group: "block",
  content: "block+",
  defining: true,

  addAttributes() {
    return {
      type: {
        default: "info",
        parseHTML: (el) =>
          (el.getAttribute("data-callout-type") as CalloutType | null) ??
          "info",
        renderHTML: (attrs) => ({
          "data-callout-type": attrs.type ?? "info",
        }),
      },
      emoji: {
        default: "",
        parseHTML: (el) => el.getAttribute("data-emoji") ?? "",
        renderHTML: (attrs) =>
          attrs.emoji ? { "data-emoji": attrs.emoji } : {},
      },
    };
  },

  parseHTML() {
    return [
      {
        tag: 'div[data-type="callout"]',
      },
    ];
  },

  renderHTML({ HTMLAttributes }) {
    return [
      "div",
      mergeAttributes(HTMLAttributes, { "data-type": "callout" }),
      0,
    ];
  },

  addNodeView() {
    return ReactNodeViewRenderer(CalloutView);
  },

  addKeyboardShortcuts() {
    return {
      "Mod-Shift-Alt-c": () =>
        this.editor.commands.insertContent({
          type: this.name,
          attrs: { type: "info", emoji: DEFAULT_EMOJI.info },
          content: [{ type: "paragraph" }],
        }),
    };
  },
});
