import { useLayoutEffect, useRef, useState, useSyncExternalStore } from "react";
import { createPortal } from "react-dom";
import { hintsEnabled, subscribeHints } from "../lib/hints";

type Side = "top" | "bottom";
type Coords = { left: number; top: number; placement: Side };

const MARGIN = 8;
const MAX_HALF = 120; // half of the tooltip's max-w-[240px] — the widest it can get

/** Wrap any element to show a short glass hint caption on hover — but only while the
 *  app-wide hints toggle (Settings → Appearance) is on. Rendered in a portal with
 *  fixed positioning so it is never clipped by an overflow/stacking ancestor. */
export default function Tooltip({
  hint,
  side = "top",
  children,
}: {
  hint: string;
  side?: Side;
  children: React.ReactNode;
}) {
  const enabled = useSyncExternalStore(subscribeHints, hintsEnabled, hintsEnabled);
  const ref = useRef<HTMLSpanElement>(null);
  const tipRef = useRef<HTMLDivElement>(null);
  const [coords, setCoords] = useState<Coords | null>(null);

  function show() {
    const el = ref.current;
    if (!el) return;
    const r = el.getBoundingClientRect();
    // Prefer the requested side; flip to bottom if there's no room above.
    const placement: Side = side === "bottom" || r.top < 90 ? "bottom" : "top";
    const center = r.left + r.width / 2;
    // Clamp using the MAX half-width so the caption can never overflow the viewport, even on the
    // first paint (before we know its real width). A left-edge anchor near x=0 would otherwise push
    // the centered box off-screen. The layout effect below refines this to the actual width.
    setCoords({
      left: Math.min(Math.max(center, MAX_HALF + MARGIN), window.innerWidth - MAX_HALF - MARGIN),
      top: placement === "top" ? r.top - 8 : r.bottom + 8,
      placement,
    });
  }
  function hide() {
    setCoords(null);
  }

  // Once rendered, re-center on the anchor using the caption's ACTUAL width (a short hint doesn't
  // need the full 240px reservation), still clamped inside the viewport.
  useLayoutEffect(() => {
    const tip = tipRef.current;
    const el = ref.current;
    if (!coords || !tip || !el) return;
    const r = el.getBoundingClientRect();
    const half = tip.offsetWidth / 2;
    const left = Math.min(
      Math.max(r.left + r.width / 2, half + MARGIN),
      window.innerWidth - half - MARGIN
    );
    if (Math.abs(left - coords.left) > 0.5) setCoords((c) => (c ? { ...c, left } : c));
  }, [coords]);

  if (!enabled || !hint) return <>{children}</>;

  return (
    <span
      ref={ref}
      onMouseEnter={show}
      onMouseLeave={hide}
      onFocus={show}
      onBlur={hide}
      className="inline-flex"
    >
      {children}
      {coords &&
        createPortal(
          <div
            ref={tipRef}
            role="tooltip"
            style={{
              position: "fixed",
              left: coords.left,
              top: coords.top,
              transform: `translateX(-50%) translateY(${coords.placement === "top" ? "-100%" : "0"})`,
            }}
            className="glass pointer-events-none z-[100] w-max max-w-[240px] px-2.5 py-1.5 rounded-lg text-xs leading-snug text-fg font-normal normal-case tracking-normal animate-[fadeIn_0.12s_ease]"
          >
            {hint}
          </div>,
          document.body
        )}
    </span>
  );
}
