import { useEffect, useLayoutEffect, useRef, useState, type RefObject } from "react";
import { createPortal } from "react-dom";
import { MessageCircleMore } from "lucide-react";
import { useTranslation } from "react-i18next";

import { floatingSurfaceElevationClassName } from "@/components/ui/floating-surface";
import { cn } from "@/lib/utils";

const MAX_QUOTED_CONTEXT_CHARS = 4_000;

interface SelectionActionState {
  text: string;
  left: number;
  top: number;
  above: boolean;
}

interface AssistantSelectionActionProps {
  containerRef: RefObject<HTMLElement | null>;
  onQuoteSelection?: (text: string) => void;
}

function selectableAncestor(node: Node | null, container: HTMLElement): HTMLElement | null {
  const element = node instanceof Element ? node : node?.parentElement;
  const selectable = element?.closest<HTMLElement>("[data-assistant-selectable='true']") ?? null;
  return selectable && container.contains(selectable) ? selectable : null;
}

function selectedRangeRect(range: Range): DOMRect | null {
  const rect = range.getBoundingClientRect();
  if (rect.width > 0 || rect.height > 0) return rect;
  return range.getClientRects()[0] ?? null;
}

function normalizedSelectionText(selection: Selection): string {
  return selection
    .toString()
    .replace(/\u00a0/g, " ")
    .replace(/\r\n?/g, "\n")
    .trim()
    .slice(0, MAX_QUOTED_CONTEXT_CHARS);
}

export function AssistantSelectionAction({
  containerRef,
  onQuoteSelection,
}: AssistantSelectionActionProps) {
  const { t } = useTranslation();
  const [action, setAction] = useState<SelectionActionState | null>(null);
  const frameRef = useRef<number | null>(null);
  const actionRef = useRef<HTMLButtonElement>(null);

  useLayoutEffect(() => {
    const element = actionRef.current;
    if (!action || !element) return;
    const viewport = window.visualViewport;
    const viewportLeft = viewport?.offsetLeft ?? 0;
    const viewportTop = viewport?.offsetTop ?? 0;
    const viewportRight = viewportLeft + (viewport?.width ?? window.innerWidth);
    const viewportBottom = viewportTop + (viewport?.height ?? window.innerHeight);
    const rect = element.getBoundingClientRect();
    const padding = 12;
    const shiftX = rect.left < viewportLeft + padding
      ? viewportLeft + padding - rect.left
      : rect.right > viewportRight - padding
        ? viewportRight - padding - rect.right
        : 0;
    const shiftY = rect.top < viewportTop + padding
      ? viewportTop + padding - rect.top
      : rect.bottom > viewportBottom - padding
        ? viewportBottom - padding - rect.bottom
        : 0;
    element.style.translate = `${shiftX}px ${shiftY}px`;
  }, [action]);

  useEffect(() => {
    if (!onQuoteSelection) return;

    const updateFromSelection = () => {
      if (frameRef.current !== null) cancelAnimationFrame(frameRef.current);
      frameRef.current = requestAnimationFrame(() => {
        frameRef.current = null;
        const container = containerRef.current;
        const selection = window.getSelection();
        if (!container || !selection || selection.isCollapsed || selection.rangeCount === 0) {
          setAction(null);
          return;
        }
        const range = selection.getRangeAt(0);
        const start = selectableAncestor(range.startContainer, container);
        const end = selectableAncestor(range.endContainer, container);
        const text = normalizedSelectionText(selection);
        const rect = selectedRangeRect(range);
        if (!start || start !== end || !text || !rect) {
          setAction(null);
          return;
        }

        const viewport = window.visualViewport;
        const viewportTop = viewport?.offsetTop ?? 0;
        const viewportBottom = viewportTop + (viewport?.height ?? window.innerHeight);
        const above = rect.bottom + 52 > viewportBottom;
        setAction({
          text,
          left: rect.left + rect.width / 2,
          top: above ? rect.top - 8 : rect.bottom + 8,
          above,
        });
      });
    };

    const dismiss = () => setAction(null);
    const onPointerDown = (event: PointerEvent) => {
      const target = event.target;
      if (target instanceof Element && target.closest("[data-selection-follow-up='true']")) return;
      dismiss();
    };
    const onKeyDown = (event: KeyboardEvent) => {
      if (event.key === "Escape") dismiss();
    };

    document.addEventListener("selectionchange", updateFromSelection);
    document.addEventListener("pointerdown", onPointerDown, true);
    document.addEventListener("scroll", dismiss, true);
    document.addEventListener("keydown", onKeyDown);
    window.addEventListener("resize", dismiss);
    window.visualViewport?.addEventListener("resize", dismiss);
    window.visualViewport?.addEventListener("scroll", dismiss);
    return () => {
      if (frameRef.current !== null) cancelAnimationFrame(frameRef.current);
      document.removeEventListener("selectionchange", updateFromSelection);
      document.removeEventListener("pointerdown", onPointerDown, true);
      document.removeEventListener("scroll", dismiss, true);
      document.removeEventListener("keydown", onKeyDown);
      window.removeEventListener("resize", dismiss);
      window.visualViewport?.removeEventListener("resize", dismiss);
      window.visualViewport?.removeEventListener("scroll", dismiss);
    };
  }, [containerRef, onQuoteSelection]);

  if (!action || typeof document === "undefined") return null;

  return createPortal(
    <button
      ref={actionRef}
      type="button"
      data-selection-follow-up="true"
      className={cn(
        floatingSurfaceElevationClassName,
        "fixed z-[80] inline-flex h-9 max-w-[calc(100vw-24px)] items-center gap-1.5 rounded-full px-3 text-[13px] font-medium transition-colors hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
      )}
      style={{
        left: action.left,
        top: action.top,
        transform: action.above ? "translate(-50%, -100%)" : "translateX(-50%)",
      }}
      onPointerDown={(event) => event.preventDefault()}
      onClick={() => {
        onQuoteSelection?.(action.text);
        window.getSelection()?.removeAllRanges();
        setAction(null);
      }}
    >
      <MessageCircleMore className="h-3.5 w-3.5" aria-hidden />
      <span className="truncate">{t("message.askAboutSelection")}</span>
    </button>,
    document.body,
  );
}
