/* Shared render runtime — copied per product, domain-free. Status tone, money,
   time and the floating layer live here so no screen assigns a tone or spells a
   duration its own way.

   Local addition, reported in README: Floating / FloatingTip. These portal to the
   document in viewport coordinates, reposition on scroll and resize, flip when
   there is no room, and clamp to the viewport. The design system's own Menu,
   Popover and Tooltip now do all of that too — these remain because they carry a
   different API the products lean on: one element as the trigger, a render
   callback that receives `close`, and no compound Trigger/Popup pair. Reach for
   the design system's overlays in new work. */

const { Badge } = window.OriginDesignSystem_7c998b;

const TONE_VARIANT = { neutral: "secondary", info: "info", success: "success", warning: "warning", danger: "destructive" };

/* The only place a status becomes a label and a colour. */
function StatusBadge({ values, value, size = "sm" }) {
  const found = statusValue(values, value);
  if (!found) { console.warn("[ui-runtime] value not in enum:", value); return null; }
  return <Badge size={size} variant={TONE_VARIANT[found.tone]}>{found.label}</Badge>;
}

const moneyFormatter = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" });
const formatMoney = (cents) => moneyFormatter.format(cents / 100);
const formatPercent = (ratio) => (ratio === null ? "—" : (ratio * 100).toFixed(1) + "%");
const formatCount = (n) => new Intl.NumberFormat("en-US").format(n);
/* A counted noun reads "1 entry", never "1 entries". Irregular plurals pass
   their own second form; everything else gets an s. */
const formatPlural = (n, one, many) => formatCount(n) + " " + (n === 1 ? one : (many || one + "s"));

/* One format rule per column. Distance is absolute: three days ago and three
   days ahead format alike. Direction is never carried by the format. */
function formatWhen(ts, now) {
  if (!ts) return "Never";
  const d = Math.abs(ts - now);
  const date = new Date(ts);
  if (d < 24 * 3600e3) return date.toLocaleTimeString("en-GB", { hour: "2-digit", minute: "2-digit" });
  if (d < 7 * 24 * 3600e3) return date.toLocaleDateString("en-GB", { weekday: "short" }) + " " + date.toLocaleTimeString("en-GB", { hour: "2-digit", minute: "2-digit" });
  return date.toLocaleDateString("en-GB", { day: "numeric", month: "short" });
}
const formatAbsolute = (ts) => (ts ? new Date(ts).toLocaleString("en-GB", { day: "numeric", month: "short", year: "numeric", hour: "2-digit", minute: "2-digit" }) : "");

/* One spelling for every countdown in the product. Direction is in the words. */
function formatCountdown(ts, now) {
  if (!ts) return "No deadline";
  const diff = ts - now;
  const abs = Math.abs(diff);
  const unit = abs >= 36 * 3600e3 ? Math.round(abs / 864e5) : Math.round(abs / 3600e3);
  const noun = abs >= 36 * 3600e3 ? (unit === 1 ? "day" : "days") : (unit === 1 ? "hour" : "hours");
  return unit + " " + noun + (diff >= 0 ? " left" : " over");
}

function Icon({ name, size = 16 }) {
  const host = React.useRef(null);
  React.useEffect(() => {
    if (!host.current || !window.lucide) return;
    host.current.innerHTML = "";
    const el = document.createElement("i");
    el.setAttribute("data-lucide", name);
    host.current.appendChild(el);
    window.lucide.createIcons({ attrs: { width: size, height: size } });
  }, [name, size]);
  return <span className="fd-icon" ref={host} aria-hidden="true" style={{ width: size, height: size }} />;
}

function useFloating(open, anchorRef, panelRef, align, side) {
  const [pos, setPos] = React.useState({ top: -9999, left: -9999 });
  const place = React.useCallback(() => {
    const host = anchorRef.current, panel = panelRef.current;
    if (!host || !panel) return;
    /* Measure the TRIGGER, not the wrapper. The wrapper is inline-flex and static,
       so an absolutely-positioned trigger inside it — a chart bar, a pinned badge —
       does not sit where the wrapper does, and measuring the wrapper puts every
       overlay in the same wrong place. */
    const anchor = host.firstElementChild || host;
    const a = anchor.getBoundingClientRect(), p = panel.getBoundingClientRect();
    const m = 8;
    let top, left;
    if (side === "right") {
      left = a.right + 6;
      top = a.top + (a.height - p.height) / 2;
    } else {
      top = a.bottom + 4;
      if (top + p.height > window.innerHeight - m) {
        const above = a.top - 4 - p.height;
        top = above > m ? above : Math.max(m, window.innerHeight - m - p.height);
      }
      left = align === "end" ? a.right - p.width : align === "center" ? a.left + (a.width - p.width) / 2 : a.left;
    }
    left = Math.min(Math.max(m, left), Math.max(m, window.innerWidth - m - p.width));
    top = Math.min(Math.max(m, top), Math.max(m, window.innerHeight - m - p.height));
    setPos({ top: top, left: left });
  }, [align, side, anchorRef, panelRef]);
  React.useLayoutEffect(() => { if (open) place(); }, [open, place]);
  React.useEffect(() => {
    if (!open) return undefined;
    const onMove = () => place();
    window.addEventListener("scroll", onMove, true);
    window.addEventListener("resize", onMove);
    return () => { window.removeEventListener("scroll", onMove, true); window.removeEventListener("resize", onMove); };
  }, [open, place]);
  return pos;
}

function Floating({ trigger, children, align = "end", width, role = "menu", panelClass = "o-menu", label }) {
  const [open, setOpen] = React.useState(false);
  const anchorRef = React.useRef(null), panelRef = React.useRef(null);
  const pos = useFloating(open, anchorRef, panelRef, align, "bottom");
  React.useEffect(() => {
    if (!open) return undefined;
    const away = (e) => {
      if (anchorRef.current && anchorRef.current.contains(e.target)) return;
      if (panelRef.current && panelRef.current.contains(e.target)) return;
      setOpen(false);
    };
    const onKey = (e) => { if (e.key === "Escape") setOpen(false); };
    document.addEventListener("mousedown", away);
    document.addEventListener("keydown", onKey);
    return () => { document.removeEventListener("mousedown", away); document.removeEventListener("keydown", onKey); };
  }, [open]);
  const close = () => setOpen(false);
  return (
    <span ref={anchorRef} className="fd-floating-anchor">
      {React.cloneElement(trigger, { onClick: () => setOpen(!open), "aria-expanded": open, "aria-haspopup": role === "menu" ? "menu" : "dialog" })}
      {open ? ReactDOM.createPortal(
        <div ref={panelRef} role={role} aria-label={label} className={"o-popup " + panelClass} style={{ position: "fixed", top: pos.top, left: pos.left, zIndex: 60, minWidth: width || "12rem" }}>
          {typeof children === "function" ? children(close) : children}
        </div>, document.body) : null}
    </span>
  );
}

function FloatingTip({ label, side = "right", children }) {
  const [open, setOpen] = React.useState(false);
  const anchorRef = React.useRef(null), panelRef = React.useRef(null);
  const pos = useFloating(open, anchorRef, panelRef, "center", side);
  return (
    <span ref={anchorRef} className="fd-floating-anchor" onMouseEnter={() => setOpen(true)} onMouseLeave={() => setOpen(false)} onFocus={() => setOpen(true)} onBlur={() => setOpen(false)}>
      {children}
      {open ? ReactDOM.createPortal(
        <span ref={panelRef} role="tooltip" className="o-tooltip" style={{ position: "fixed", top: pos.top, left: pos.left, zIndex: 70, whiteSpace: "nowrap" }}>{label}</span>,
        document.body) : null}
    </span>
  );
}

Object.assign(window, { StatusBadge, TONE_VARIANT, formatMoney, formatPercent, formatCount, formatPlural, formatWhen, formatAbsolute, formatCountdown, Icon, Floating, FloatingTip });
