// Shared UI atoms

function cx(...arr) { return arr.filter(Boolean).join(' '); }

function fmtDate(iso, opts = {}) {
  const d = new Date(iso);
  const months = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
  if (opts.time) {
    const hh = String(d.getHours()).padStart(2, '0');
    const mm = String(d.getMinutes()).padStart(2, '0');
    return `${months[d.getMonth()]} ${d.getDate()}, ${hh}:${mm}`;
  }
  return `${months[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`;
}

function fmtRelative(iso) {
  const d = new Date(iso);
  const diff = (Date.now() - d.getTime()) / 1000;
  if (diff < 60) return `${Math.floor(diff)}s ago`;
  if (diff < 3600) return `${Math.floor(diff/60)}m ago`;
  if (diff < 86400) return `${Math.floor(diff/3600)}h ago`;
  return `${Math.floor(diff/86400)}d ago`;
}

function fmtNum(n) {
  return n.toLocaleString('en-US');
}

function Icon({ name, size = 16 }) {
  const map = {
    arrow: <path d="M5 12h14M13 5l7 7-7 7"/>,
    close: <path d="M18 6L6 18M6 6l12 12"/>,
    check: <path d="M20 6L9 17l-5-5"/>,
    chev: <path d="M9 18l6-6-6-6"/>,
    copy: <><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></>,
    download: <><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><path d="M7 10l5 5 5-5"/><path d="M12 15V3"/></>,
    filter: <path d="M3 6h18M6 12h12M10 18h4"/>,
    search: <><circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/></>,
    plus: <path d="M12 5v14M5 12h14"/>,
    bolt: <path d="M13 2L3 14h7l-1 8 10-12h-7l1-8z"/>,
    wallet: <><path d="M20 12V8H6a2 2 0 010-4h12v4"/><path d="M4 6v12a2 2 0 002 2h14v-4"/><circle cx="17" cy="14" r="2"/></>,
  };
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round">
      {map[name]}
    </svg>
  );
}

function useReveal() {
  const ref = React.useRef(null);
  React.useEffect(() => {
    if (!ref.current) return;
    const el = ref.current;
    // Reveal-on-mount fallback: if it's already in view at mount, show it next frame.
    function checkInView() {
      const r = el.getBoundingClientRect();
      const vh = window.innerHeight || document.documentElement.clientHeight;
      if (r.top < vh - 40 && r.bottom > 0) el.classList.add('in');
    }
    requestAnimationFrame(checkInView);
    // Hard safety: ensure visible after 600ms no matter what.
    const safety = setTimeout(() => el.classList.add('in'), 600);
    let io;
    try {
      io = new IntersectionObserver((entries) => {
        entries.forEach((e) => { if (e.isIntersecting) el.classList.add('in'); });
      }, { threshold: 0.05, rootMargin: '0px 0px -40px 0px' });
      io.observe(el);
    } catch (e) { /* IO unsupported — safety covers it */ }
    const onScroll = () => checkInView();
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => { clearTimeout(safety); io && io.disconnect(); window.removeEventListener('scroll', onScroll); };
  }, []);
  return ref;
}

function Reveal({ children, as: As = "div", delay = 0, ...rest }) {
  const ref = useReveal();
  return <As ref={ref} className={cx("reveal", rest.className)} style={{ transitionDelay: `${delay}ms`, ...rest.style }}>{children}</As>;
}

function Toast({ messages }) {
  return (
    <div className="toast-wrap">
      {messages.map((m) => (
        <div key={m.id} className="toast">
          <span className="dot"></span>
          {m.text}
        </div>
      ))}
    </div>
  );
}

function Modal({ open, onClose, children, title }) {
  React.useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape' && open) onClose(); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [open, onClose]);

  if (!open) return null;
  return (
    <div className="modal-backdrop" onClick={onClose}>
      <div className="modal" onClick={(e) => e.stopPropagation()}>
        <div className="modal-h">
          <div className="modal-title">{title}</div>
          <button className="close-btn" onClick={onClose}><Icon name="close" /></button>
        </div>
        {children}
      </div>
    </div>
  );
}

function Confetti({ active, onDone }) {
  React.useEffect(() => {
    if (!active) return;
    const t = setTimeout(() => onDone && onDone(), 2600);
    return () => clearTimeout(t);
  }, [active, onDone]);

  if (!active) return null;

  const colors = ["#ff3366", "#fbbf24", "#34d399", "#60a5fa", "#a78bfa", "#ffffff"];
  const pieces = Array.from({ length: 80 }).map((_, i) => ({
    id: i,
    left: Math.random() * 100,
    color: colors[i % colors.length],
    delay: Math.random() * 0.4,
    dur: 1.8 + Math.random() * 1.2,
    rot: Math.random() * 360,
    w: 6 + Math.random() * 6,
    h: 10 + Math.random() * 10,
  }));

  return (
    <>
      {pieces.map((p) => (
        <div
          key={p.id}
          className="confetti-piece"
          style={{
            left: `${p.left}%`,
            background: p.color,
            transform: `rotate(${p.rot}deg)`,
            animationDelay: `${p.delay}s`,
            animationDuration: `${p.dur}s`,
            width: p.w,
            height: p.h,
          }}
        />
      ))}
    </>
  );
}

Object.assign(window, {
  cx, fmtDate, fmtRelative, fmtNum,
  Icon, Reveal, Toast, Modal, Confetti, useReveal,
});
