"use client";

import * as React from "react";
import { createPortal } from "react-dom";
import { cn } from "@/lib/utils";

type Side = "top" | "bottom" | "left" | "right";

type Coords = { top: number; left: number };

export function Tooltip({
  children,
  content,
  side = "top",
  delay = 300,
  className,
}: {
  children: React.ReactNode;
  content: React.ReactNode;
  side?: Side;
  delay?: number;
  className?: string;
}) {
  const [open, setOpen] = React.useState(false);
  const [mounted, setMounted] = React.useState(false);
  const [coords, setCoords] = React.useState<Coords>({ top: 0, left: 0 });
  const triggerRef = React.useRef<HTMLSpanElement | null>(null);
  const tooltipRef = React.useRef<HTMLDivElement | null>(null);
  const timerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);
  const id = React.useId();
  const tooltipId = `tooltip-${id}`;

  React.useEffect(() => {
    const t = setTimeout(() => setMounted(true), 0);
    return () => clearTimeout(t);
  }, []);

  const clearTimer = React.useCallback(() => {
    if (timerRef.current) {
      clearTimeout(timerRef.current);
      timerRef.current = null;
    }
  }, []);

  const show = React.useCallback(() => {
    clearTimer();
    timerRef.current = setTimeout(() => setOpen(true), delay);
  }, [delay, clearTimer]);

  const hide = React.useCallback(() => {
    clearTimer();
    setOpen(false);
  }, [clearTimer]);

  React.useEffect(() => {
    if (!open) return;
    function handleKey(e: KeyboardEvent) {
      if (e.key === "Escape") hide();
    }
    document.addEventListener("keydown", handleKey);
    return () => document.removeEventListener("keydown", handleKey);
  }, [open, hide]);

  React.useEffect(() => {
    if (!open) return;
    function position() {
      const trigger = triggerRef.current;
      const tip = tooltipRef.current;
      if (!trigger || !tip) return;
      const tr = trigger.getBoundingClientRect();
      const tipRect = tip.getBoundingClientRect();
      const gap = 6;
      let top = 0;
      let left = 0;
      switch (side) {
        case "top":
          top = tr.top - tipRect.height - gap;
          left = tr.left + tr.width / 2 - tipRect.width / 2;
          break;
        case "bottom":
          top = tr.bottom + gap;
          left = tr.left + tr.width / 2 - tipRect.width / 2;
          break;
        case "left":
          top = tr.top + tr.height / 2 - tipRect.height / 2;
          left = tr.left - tipRect.width - gap;
          break;
        case "right":
          top = tr.top + tr.height / 2 - tipRect.height / 2;
          left = tr.right + gap;
          break;
      }
      // Defer setState to a separate task (react-hooks/set-state-in-effect rule).
      setTimeout(() => setCoords({ top, left }), 0);
    }
    position();
    window.addEventListener("scroll", position, true);
    window.addEventListener("resize", position);
    return () => {
      window.removeEventListener("scroll", position, true);
      window.removeEventListener("resize", position);
    };
  }, [open, side]);

  React.useEffect(() => {
    return () => {
      if (timerRef.current) clearTimeout(timerRef.current);
    };
  }, []);

  return (
    <>
      <span
        ref={triggerRef}
        onMouseEnter={show}
        onMouseLeave={hide}
        onFocus={show}
        onBlur={hide}
        aria-describedby={open ? tooltipId : undefined}
        className="inline-flex"
      >
        {children}
      </span>
      {mounted &&
        open &&
        createPortal(
          <div
            ref={tooltipRef}
            id={tooltipId}
            role="tooltip"
            style={{
              position: "fixed",
              top: coords.top,
              left: coords.left,
              pointerEvents: "none",
            }}
            className={cn(
              "z-50 max-w-xs rounded-md border bg-popover px-2.5 py-1.5 text-xs text-popover-foreground shadow-md",
              className,
            )}
          >
            {content}
          </div>,
          document.body,
        )}
    </>
  );
}
