"use client";

import { Button } from "@/components/ui/button";
import {
  Popover,
  PopoverAnchor,
  PopoverContent,
} from "@/components/ui/popover";
import { useLocaleFunction } from "@/hooks/use-localed";
import type { Locale } from "@/lib/locale";
import { cn } from "@/lib/utils";
import type { Measurable } from "@radix-ui/rect";
import {
  type ReactNode,
  type RefObject,
  useEffect,
  useRef,
  useState,
} from "react";
import "github-markdown-css/github-markdown-light.css";
import "./markdown.css";

export default function Markdown({
  enableAIExplain = true,
  className,
  children,
}: {
  enableAIExplain?: boolean;
  className?: string;
  children: ReactNode;
}) {
  const t = useLocaleFunction();
  const ref = useRef<HTMLDivElement>(null);
  const [selection, setSelection] = useState<{
    range: Range;
    textContent: string;
  }>();
  const lastRange = useRef<Range | undefined>(undefined);

  useEffect(() => {
    if (!enableAIExplain) return;
    const handleSelectionChange = debounce(() => {
      const selection = document.getSelection();
      const articleElement = ref.current;

      if (selection && selection.rangeCount === 1 && articleElement) {
        let range = selection.getRangeAt(0);

        if (!range.collapsed) {
          range = range.cloneRange();
          const startPosition =
            range.startContainer.compareDocumentPosition(articleElement);
          const endPosition =
            range.endContainer.compareDocumentPosition(articleElement);

          if (
            startPosition & Node.DOCUMENT_POSITION_CONTAINS &&
            endPosition & Node.DOCUMENT_POSITION_CONTAINS
          ) {
            const div = document.createElement("div");
            div.appendChild(range.cloneContents());
            range.collapse(true);
            const textContent = div.innerText;
            setSelection({ range, textContent });
            return;
          }
        }
      }
      setSelection((last) => {
        lastRange.current = last?.range;
        return undefined;
      });
    }, 50);

    document.addEventListener("selectionchange", handleSelectionChange);
    return () => {
      handleSelectionChange.cancel();
      document.removeEventListener("selectionchange", handleSelectionChange);
    };
  }, [enableAIExplain]);

  const virtualRef: RefObject<Measurable> | undefined =
    (selection
      ? ({ current: selection.range } as RefObject<Measurable>)
      : lastRange.current
        ? (lastRange as RefObject<Measurable>)
        : (ref as RefObject<Measurable>)) ?? undefined;

  return (
    <>
      <article
        ref={ref}
        className={cn("site-markdown-body markdown-body", className)}
        style={{ "--bgColor-default": "transparent" } as any}
      >
        {children}
      </article>

      {enableAIExplain && (
        <Popover open={!!selection} modal={false}>
          <PopoverAnchor virtualRef={virtualRef}></PopoverAnchor>
          <PopoverContent
            onOpenAutoFocus={(event) => event.preventDefault()}
            onCloseAutoFocus={(event) => event.preventDefault()}
            side="top"
            align="start"
            sideOffset={8}
            className="w-max p-0 rounded-none bg-transparent border-none"
          >
            <Button
              onClick={() => {
                tidbai.open = true;
                setTimeout(() => {
                  tidbai.newChat(
                    message[t.locale] + "\n\n" + (selection?.textContent ?? "")
                  );
                }, 200);
              }}
            >
              {t("common.explain-with-ai")}
            </Button>
          </PopoverContent>
        </Popover>
      )}
    </>
  );
}

function debounce<T extends (...args: any[]) => any>(
  fn: T,
  delay: number
): { (...args: Parameters<T>): void; cancel: () => void } {
  let handle: ReturnType<typeof setTimeout> | undefined;

  const debouncedFn: { (...args: Parameters<T>): void; cancel: () => void } = ((
    ...args
  ) => {
    if (handle) {
      clearTimeout(handle);
    }
    handle = setTimeout(() => {
      fn(...args);
      handle = undefined;
    }, delay);
  }) as T & { cancel: () => void };

  debouncedFn.cancel = () => {
    clearTimeout(handle);
  };

  return debouncedFn;
}

const message: Record<Locale, string> = {
  zh: "Explain this content(please answer with simplified Chinese)",
  ja: "Explain this content(please answer with Japanese)",
  en: "Explain this content(please answer with English)",
  ko: "Explain this content(please answer with Korean)",
};
