"use client";

import {
  useCallback,
  useEffect,
  useId,
  useLayoutEffect,
  useRef,
  useState,
  type ReactNode,
} from "react";
import { createPortal } from "react-dom";
import { Info } from "lucide-react";

type Placement = "top" | "bottom" | "right";

interface TooltipProps {
  content: string;
  children?: ReactNode;
  placement?: Placement;
  /** @deprecated Use placement */
  side?: Placement;
  className?: string;
  label?: string;
}

const GAP = 8;
const VIEWPORT_PADDING = 12;

function computePosition(
  trigger: DOMRect,
  tooltip: DOMRect,
  preferred: Placement
): { top: number; left: number; placement: Placement } {
  const placements: Placement[] = ([
    preferred,
    "right",
    "bottom",
    "top",
  ] as Placement[]).filter((value, index, array) => array.indexOf(value) === index);

  const fits = (top: number, left: number) =>
    top >= VIEWPORT_PADDING &&
    left >= VIEWPORT_PADDING &&
    top + tooltip.height <= window.innerHeight - VIEWPORT_PADDING &&
    left + tooltip.width <= window.innerWidth - VIEWPORT_PADDING;

  for (const placement of placements) {
    let top = 0;
    let left = 0;

    if (placement === "right") {
      top = trigger.top + (trigger.height - tooltip.height) / 2;
      left = trigger.right + GAP;
    } else if (placement === "bottom") {
      top = trigger.bottom + GAP;
      left = trigger.left + (trigger.width - tooltip.width) / 2;
    } else {
      top = trigger.top - tooltip.height - GAP;
      left = trigger.left + (trigger.width - tooltip.width) / 2;
    }

    top = Math.max(
      VIEWPORT_PADDING,
      Math.min(top, window.innerHeight - tooltip.height - VIEWPORT_PADDING)
    );
    left = Math.max(
      VIEWPORT_PADDING,
      Math.min(left, window.innerWidth - tooltip.width - VIEWPORT_PADDING)
    );

    if (fits(top, left) || placement === placements[placements.length - 1]) {
      return { top, left, placement };
    }
  }

  return { top: VIEWPORT_PADDING, left: VIEWPORT_PADDING, placement: preferred };
}

export function Tooltip({
  content,
  children,
  placement,
  side,
  className = "",
  label = "More information",
}: TooltipProps) {
  const resolvedPlacement = placement ?? side ?? "right";
  const tooltipId = useId();
  const triggerRef = useRef<HTMLSpanElement>(null);
  const tooltipRef = useRef<HTMLSpanElement>(null);
  const [open, setOpen] = useState(false);
  const [mounted, setMounted] = useState(false);
  const [coords, setCoords] = useState<{ top: number; left: number } | null>(null);

  useEffect(() => {
    setMounted(true);
  }, []);

  const updatePosition = useCallback(() => {
    const trigger = triggerRef.current;
    const tooltip = tooltipRef.current;
    if (!trigger || !tooltip) return;

    const next = computePosition(
      trigger.getBoundingClientRect(),
      tooltip.getBoundingClientRect(),
      resolvedPlacement
    );
    setCoords({ top: next.top, left: next.left });
  }, [resolvedPlacement]);

  useLayoutEffect(() => {
    if (!open) {
      setCoords(null);
      return;
    }
    updatePosition();
  }, [open, content, updatePosition]);

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

    const handleReposition = () => updatePosition();
    window.addEventListener("scroll", handleReposition, true);
    window.addEventListener("resize", handleReposition);
    return () => {
      window.removeEventListener("scroll", handleReposition, true);
      window.removeEventListener("resize", handleReposition);
    };
  }, [open, updatePosition]);

  const show = () => setOpen(true);
  const hide = () => setOpen(false);

  const tooltipNode =
    mounted && open
      ? createPortal(
          <span
            ref={tooltipRef}
            id={tooltipId}
            role="tooltip"
            style={{
              position: "fixed",
              top: coords?.top ?? -9999,
              left: coords?.left ?? -9999,
              visibility: coords ? "visible" : "hidden",
              zIndex: 9999,
            }}
            className="pointer-events-none w-max max-w-[min(16rem,calc(100vw-1.5rem))] rounded-lg border border-[var(--border)] bg-[var(--bg-elevated)] px-2.5 py-1.5 text-[11px] leading-snug text-[var(--text-muted)] shadow-[0_8px_24px_rgba(0,0,0,0.35)]"
          >
            {content}
          </span>,
          document.body
        )
      : null;

  return (
    <>
      <span
        ref={triggerRef}
        className={`inline-flex items-center ${className}`}
        onMouseEnter={show}
        onMouseLeave={hide}
        onFocus={show}
        onBlur={hide}
      >
        {children ?? (
          <button
            type="button"
            tabIndex={0}
            aria-label={label}
            aria-describedby={open ? tooltipId : undefined}
            className="inline-flex h-4 w-4 shrink-0 cursor-help items-center justify-center rounded-full text-[var(--text-faint)] transition-colors hover:bg-[var(--bg-hover)] hover:text-[var(--text-muted)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent)]/35"
          >
            <Info className="h-3.5 w-3.5" />
          </button>
        )}
      </span>
      {tooltipNode}
    </>
  );
}

export function HelpTooltip({
  content,
  label,
  className,
  placement = "right",
  side,
}: {
  content: string;
  label?: string;
  className?: string;
  placement?: Placement;
  side?: Placement;
}) {
  return (
    <Tooltip
      content={content}
      label={label}
      className={className}
      placement={placement ?? side}
    />
  );
}

export function LabelWithHint({
  label,
  hint,
  htmlFor,
  hintLabel,
}: {
  label: string;
  hint: string;
  htmlFor?: string;
  hintLabel?: string;
}) {
  return (
    <div className="flex items-center gap-1.5">
      <label htmlFor={htmlFor} className="text-sm font-medium text-[var(--text-muted)]">
        {label}
      </label>
      <HelpTooltip content={hint} label={hintLabel} placement="right" />
    </div>
  );
}
