// components.jsx — shared helpers + small UI pieces. Exports to window.
const { useState, useEffect, useRef } = React;

/* ---------------- formatting / math ---------------- */
function fmtNum(n) {
  if (n == null || n === '' || isNaN(n)) return null;
  return Number(n).toLocaleString('en-US', { maximumFractionDigits: 2 });
}
// returns { text, blank }
function fmtMoney(value, cur) {
  const num = fmtNum(value);
  if (num == null) return { text: 'note', blank: true };
  if (!cur) return { text: num, blank: false };
  if (cur.symbol === '🪙' || cur.fun) return { text: `${num} ${cur.symbol}`, blank: false };
  return { text: `${cur.symbol}${num}`, blank: false };
}
function toBase(value, cur) {
  if (value == null || isNaN(value)) return 0;
  const rate = cur ? (cur.rateToBase ?? 1) : 1;
  return Number(value) * rate;
}
function fmtBase(n) {
  return '฿' + Math.round(n).toLocaleString('en-US');
}
function relDate(iso) {
  if (!iso) return '';
  const d = new Date(iso + 'T00:00:00');
  const now = new Date(); now.setHours(0,0,0,0);
  const diff = Math.round((now - d) / 86400000);
  if (diff === 0) return 'Today';
  if (diff === 1) return 'Yesterday';
  if (diff > 1 && diff < 7) return diff + ' days ago';
  return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
}

/* ---------------- store hook ---------------- */
function useStore() {
  const [, force] = useState(0);
  useEffect(() => Store.onChange(() => force(n => n + 1)), []);
  return Store;
}

/* ---------------- tiny pieces ---------------- */
function Avatar({ owner, size = 22 }) {
  if (!owner) return null;
  return (
    <div className="avatar" style={{ width: size, height: size, fontSize: size * 0.46,
      background: `linear-gradient(150deg, ${owner.color}, ${shade(owner.color, -18)})` }}>
      {owner.name[0]}
    </div>
  );
}
function FlowChip({ flow }) {
  const map = { spent: ['chip-spent', 'Spent'], have: ['chip-have', 'Have'], received: ['chip-recv', 'Got'] };
  const [cls, label] = map[flow] || map.spent;
  return <span className={`chip ${cls}`}>{label}</span>;
}
function TypePill({ type }) {
  if (!type) return null;
  return (
    <span className="type-pill" style={{ background: tint(type.color, 0.15), color: shade(type.color, -8) }}>
      <Icon name={type.icon} size={13} stroke={2.1} /> {type.name}
    </span>
  );
}

/* ---------------- modal shell ---------------- */
function Modal({ title, icon, onClose, children, wide }) {
  useEffect(() => {
    const h = (e) => e.key === 'Escape' && onClose();
    window.addEventListener('keydown', h); return () => window.removeEventListener('keydown', h);
  }, [onClose]);
  return (
    <div className="scrim" onMouseDown={onClose}>
      <div className={`modal ${wide ? 'modal-wide' : ''}`} onMouseDown={e => e.stopPropagation()}>
        <div className="modal-head">
          {icon}
          <h2>{title}</h2>
          <button className="modal-x" onClick={onClose} aria-label="Close">
            <svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round"><path d="M6 6l12 12M18 6L6 18"/></svg>
          </button>
        </div>
        <div className="modal-body">{children}</div>
      </div>
    </div>
  );
}

/* ---------------- pickers ---------------- */
function IconPicker({ value, onChange }) {
  return (
    <div className="icon-grid">
      {ICON_NAMES.map(n => (
        <button key={n} className={`icon-cell ${value === n ? 'on' : ''}`} onClick={() => onChange(n)} type="button">
          <Icon name={n} size={20} color={value === n ? 'var(--rose)' : 'var(--ink-soft)'} />
        </button>
      ))}
    </div>
  );
}
const SWATCHES = ['#EC6F9E','#D9568A','#E05D7E','#E0883F','#E0A93F','#5BA88A','#3FA277','#5683C4','#7C6BD0','#C79BE0','#A9794F','#9AA0A6'];
function ColorPicker({ value, onChange }) {
  return (
    <div className="swatch-row">
      {SWATCHES.map(c => (
        <button key={c} type="button" className={`swatch ${value === c ? 'on' : ''}`} style={{ background: c }} onClick={() => onChange(c)} />
      ))}
    </div>
  );
}

/* ---------------- empty + toast ---------------- */
function Empty({ icon, title, sub, action }) {
  return (
    <div className="empty fade-in">
      <div className="empty-art">{icon}</div>
      <h3>{title}</h3>
      <p>{sub}</p>
      {action}
    </div>
  );
}
function Toast({ msg }) {
  if (!msg) return null;
  return <div className="toast"><Icon name="star" size={16} /> {msg}</div>;
}

/* ---------------- color utils ---------------- */
function shade(hex, amt) { // amt -100..100 (negative = darker)
  const c = hex.replace('#',''); const n = parseInt(c.length===3?c.split('').map(x=>x+x).join(''):c,16);
  let r=(n>>16)&255,g=(n>>8)&255,b=n&255; const f = amt/100;
  const adj = (v)=> Math.max(0,Math.min(255, Math.round(v + (f<0? v*f : (255-v)*f))));
  return `rgb(${adj(r)},${adj(g)},${adj(b)})`;
}

Object.assign(window, {
  fmtNum, fmtMoney, toBase, fmtBase, relDate, useStore,
  Avatar, FlowChip, TypePill, Modal, IconPicker, ColorPicker, SWATCHES, Empty, Toast, shade,
});
