// ============ Reusable UI primitives ============

// Ripple hook — Material-ish
function attachRipple(e) {
  const target = e.currentTarget;
  if (!target) return;
  const rect = target.getBoundingClientRect();
  const size = Math.max(rect.width, rect.height);
  const x = e.clientX - rect.left - size / 2;
  const y = e.clientY - rect.top - size / 2;
  const span = document.createElement("span");
  span.className = "ripple";
  span.style.width = span.style.height = size + "px";
  span.style.left = x + "px";
  span.style.top = y + "px";
  target.appendChild(span);
  setTimeout(() => span.remove(), 600);
}

const Button = React.forwardRef(({ variant = "default", size, icon, children, onClick, style, className, ...rest }, ref) => {
  const cls = ["btn"];
  if (variant === "primary") cls.push("primary");
  if (variant === "ghost") cls.push("ghost");
  if (size === "sm") cls.push("sm");
  if (!children && icon) cls.push("icon");
  if (className) cls.push(className);
  return (
    <button ref={ref} className={cls.join(" ")} style={style}
      onClick={(e) => { attachRipple(e); onClick && onClick(e); }} {...rest}>
      {icon && <Icon name={icon} size={14} />}
      {children}
    </button>
  );
});

// Segment control (time period)
const Segment = ({ options, value, onChange }) => (
  <div className="segment">
    {options.map(o => (
      <button key={o.value}
        className={value === o.value ? "active" : ""}
        onClick={() => onChange(o.value)}>
        {o.label}
      </button>
    ))}
  </div>
);

// Card
const Card = ({ title, right, children, style, className }) => (
  <div className={"card " + (className || "")} style={style}>
    {(title || right) && (
      <div className="card-hd">
        {title && <h3>{title}</h3>}
        <div className="spacer" />
        {right}
      </div>
    )}
    <div className="card-bd">{children}</div>
  </div>
);

// Chip
const Chip = ({ tone = "default", dot, children }) => (
  <span className={"chip " + (tone !== "default" ? tone : "")}>
    {dot && <span className="dot" />}
    {children}
  </span>
);

// Modal
const Modal = ({ open, onClose, title, footer, children }) => {
  React.useEffect(() => {
    if (!open) return;
    const onKey = (e) => e.key === "Escape" && onClose && onClose();
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [open, onClose]);
  if (!open) return null;
  return (
    <div className="scrim" onClick={(e) => { if (e.target === e.currentTarget) onClose && onClose(); }}>
      <div className="modal" role="dialog">
        <div className="modal-hd">
          <h3>{title}</h3>
          <div style={{ flex: 1 }} />
          <Button variant="ghost" icon="x" onClick={onClose} />
        </div>
        <div className="modal-bd">{children}</div>
        {footer && <div className="modal-ft">{footer}</div>}
      </div>
    </div>
  );
};

// Field
const Field = ({ label, children }) => (
  <div className="field">
    <label>{label}</label>
    {children}
  </div>
);

// Count-up
const CountUp = ({ value, duration = 900, format = (v) => v.toLocaleString(), suffix = "" }) => {
  const [v, setV] = React.useState(0);
  const start = React.useRef(0);
  React.useEffect(() => {
    let raf;
    const t0 = performance.now();
    const from = start.current;
    const tick = (t) => {
      const p = Math.min(1, (t - t0) / duration);
      const eased = 1 - Math.pow(1 - p, 3);
      const cur = from + (value - from) * eased;
      setV(cur);
      if (p < 1) raf = requestAnimationFrame(tick);
      else start.current = value;
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [value, duration]);
  return <>{format(v)}{suffix}</>;
};

// Info tooltip — renders the bubble into document.body via a portal so it
// escapes any `overflow:hidden` / stacking-context ancestor (like .card).
const InfoTip = ({ text, size = 13 }) => {
  const ref = React.useRef(null);
  const [pos, setPos] = React.useState(null); // { top, left, placement }

  const compute = React.useCallback(() => {
    const el = ref.current;
    if (!el) return;
    const r = el.getBoundingClientRect();
    const vw = window.innerWidth, vh = window.innerHeight;
    // Default above; if not enough room, flip below
    const placement = r.top < 90 ? "bottom" : "top";
    const top = placement === "top" ? r.top : r.bottom;
    // Clamp horizontal to keep bubble on-screen (min 16px from edges)
    const left = Math.min(Math.max(r.left + r.width / 2, 160), vw - 160);
    setPos({ top, left, placement });
  }, []);

  const show = () => compute();
  const hide = () => setPos(null);

  React.useEffect(() => {
    if (!pos) return;
    const onMove = () => hide();
    window.addEventListener("scroll", onMove, true);
    window.addEventListener("resize", onMove);
    return () => {
      window.removeEventListener("scroll", onMove, true);
      window.removeEventListener("resize", onMove);
    };
  }, [pos]);

  return (
    <span
      ref={ref}
      className="info-tip"
      tabIndex={0}
      aria-label={typeof text === "string" ? text : "Info"}
      onMouseEnter={show}
      onMouseLeave={hide}
      onFocus={show}
      onBlur={hide}
    >
      <Icon name="info" size={size}/>
      {pos && ReactDOM.createPortal(
        <span
          className={"info-tip-bubble is-" + pos.placement}
          role="tooltip"
          style={{ top: pos.top, left: pos.left }}
        >
          {text}
        </span>,
        document.body
      )}
    </span>
  );
};

// Titled section header with icon + optional info tip
const SectionTitle = ({ icon, children, tip, color }) => (
  <span style={{ display: "inline-flex", alignItems: "center", gap: 8 }}>
    {icon && <span style={{ color: color || "var(--accent)", display: "inline-flex" }}><Icon name={icon} size={14}/></span>}
    {children}
    {tip && <InfoTip text={tip}/>}
  </span>
);

Object.assign(window, { Button, Segment, Card, Chip, Modal, Field, CountUp, attachRipple, InfoTip, SectionTitle });
