// components.jsx — shared UI primitives for Haven
const { useState, useRef, useEffect } = React;

// ── Card ────────────────────────────────────────────────────────────
function Card({ children, style, pad = 22, className = '', hover, onClick, ...rest }) {
  const [h, setH] = useState(false);
  return (
    <div
      className={className}
      onClick={onClick}
      onMouseEnter={() => setH(true)}
      onMouseLeave={() => setH(false)}
      style={{
        background: 'var(--surface)', border: '1px solid var(--line)',
        borderRadius: 'var(--radius)', padding: pad,
        boxShadow: hover && h ? 'var(--shadow)' : 'var(--shadow-sm)',
        transition: 'box-shadow .2s, transform .2s, border-color .2s',
        transform: hover && h ? 'translateY(-2px)' : 'none',
        cursor: onClick ? 'pointer' : 'default',
        ...style,
      }}
      {...rest}
    >
      {children}
    </div>
  );
}

// ── Button ──────────────────────────────────────────────────────────
function Button({ children, variant = 'primary', size = 'md', icon: Icon, iconRight, full, style, ...rest }) {
  const [h, setH] = useState(false);
  const sizes = {
    sm: { padding: '7px 13px', fontSize: 13.5, radius: 10, gap: 6, ic: 16 },
    md: { padding: '11px 18px', fontSize: 15, radius: 12, gap: 8, ic: 18 },
    lg: { padding: '14px 24px', fontSize: 16, radius: 14, gap: 9, ic: 20 },
  }[size];
  const variants = {
    primary: { bg: 'var(--primary)', color: '#fff', border: 'transparent', bgH: 'var(--primary-ink)' },
    amber: { bg: 'var(--amber)', color: '#fff', border: 'transparent', bgH: 'var(--amber-ink)' },
    soft: { bg: 'var(--primary-soft)', color: 'var(--primary-ink)', border: 'transparent', bgH: '#f3d9e4' },
    ghost: { bg: 'transparent', color: 'var(--ink-2)', border: 'var(--line-2)', bgH: 'var(--surface-2)' },
    white: { bg: 'var(--surface)', color: 'var(--ink)', border: 'var(--line-2)', bgH: 'var(--surface-2)' },
  }[variant];
  return (
    <button
      onMouseEnter={() => setH(true)} onMouseLeave={() => setH(false)}
      style={{
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: sizes.gap,
        padding: sizes.padding, fontSize: sizes.fontSize, fontWeight: 600, fontFamily: 'var(--sans)',
        color: variants.color, background: h ? variants.bgH : variants.bg,
        border: `1px solid ${variants.border === 'transparent' ? (h ? variants.bgH : variants.bg) : variants.border}`,
        borderRadius: sizes.radius, width: full ? '100%' : 'auto',
        transition: 'all .16s', whiteSpace: 'nowrap', lineHeight: 1.1, ...style,
      }}
      {...rest}
    >
      {Icon && !iconRight && <Icon size={sizes.ic} />}
      {children}
      {Icon && iconRight && <Icon size={sizes.ic} />}
    </button>
  );
}

// ── Pill / Badge ────────────────────────────────────────────────────
function Pill({ children, color = 'var(--ink-2)', bg = 'var(--surface-2)', style }) {
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 5, padding: '4px 10px',
      borderRadius: 99, fontSize: 12.5, fontWeight: 600, color, background: bg,
      letterSpacing: '.01em', whiteSpace: 'nowrap', ...style,
    }}>{children}</span>
  );
}

const STATUS = {
  draft: { label: 'Draft', color: 'var(--ink-2)', bg: 'var(--surface-2)', icon: 'doc' },
  submitted: { label: 'Submitted', color: 'var(--amber-ink)', bg: 'var(--amber-soft)', icon: 'clock' },
  paid: { label: 'Paid', color: 'var(--good)', bg: 'var(--good-soft)', icon: 'checkCircle' },
  rejected: { label: 'Needs a fix', color: 'var(--alert)', bg: 'var(--alert-soft)', icon: 'alert' },
  pending: { label: 'To approve', color: 'var(--amber-ink)', bg: 'var(--amber-soft)', icon: 'clock' },
};
function StatusPill({ status }) {
  const s = STATUS[status] || STATUS.draft;
  const I = Icons[s.icon];
  return <Pill color={s.color} bg={s.bg}><I size={13} />{s.label}</Pill>;
}

// ── Progress bar ────────────────────────────────────────────────────
function Bar({ pct, color = 'var(--primary)', track = 'var(--surface-2)', height = 9, marker }) {
  const p = Math.min(1, Math.max(0, pct));
  return (
    <div style={{ position: 'relative', height, background: track, borderRadius: 99, overflow: 'visible' }}>
      <div style={{
        position: 'absolute', inset: 0, width: `${p * 100}%`, background: color,
        borderRadius: 99, transition: 'width .6s cubic-bezier(.4,0,.1,1)',
      }} />
      {marker != null && (
        <div title={`${Math.round(marker * 100)}% of the plan period`} style={{
          position: 'absolute', top: -3, bottom: -3, left: `${Math.min(1, marker) * 100}%`,
          width: 2, background: 'var(--ink)', opacity: .32, borderRadius: 2,
        }} />
      )}
    </div>
  );
}

// ── Donut ring ──────────────────────────────────────────────────────
function Ring({ pct, size = 120, stroke = 12, color = 'var(--primary)', track = 'var(--surface-2)', children }) {
  const r = (size - stroke) / 2;
  const c = 2 * Math.PI * r;
  const p = Math.min(1, Math.max(0, pct));
  return (
    <div style={{ position: 'relative', width: size, height: size }}>
      <svg width={size} height={size} style={{ transform: 'rotate(-90deg)' }}>
        <circle cx={size / 2} cy={size / 2} r={r} fill="none" stroke={track} strokeWidth={stroke} />
        <circle cx={size / 2} cy={size / 2} r={r} fill="none" stroke={color} strokeWidth={stroke}
          strokeLinecap="round" strokeDasharray={c} strokeDashoffset={c * (1 - p)}
          style={{ transition: 'stroke-dashoffset .8s cubic-bezier(.4,0,.1,1)' }} />
      </svg>
      <div style={{ position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', textAlign: 'center' }}>
        {children}
      </div>
    </div>
  );
}

// ── Avatar ──────────────────────────────────────────────────────────
function Avatar({ initials, size = 38, bg = 'var(--primary-soft)', color = 'var(--primary-ink)' }) {
  return (
    <div style={{
      width: size, height: size, borderRadius: '50%', background: bg, color,
      display: 'grid', placeItems: 'center', fontWeight: 700, fontSize: size * 0.36, flexShrink: 0,
    }}>{initials}</div>
  );
}

// ── Plain-English helper (jargon tooltip) ───────────────────────────
let PLAIN_ON = true;
function PlainTip({ term, children }) {
  const [open, setOpen] = useState(false);
  const text = GLOSSARY[term] || children;
  if (!PLAIN_ON) return <span>{term}</span>;
  return (
    <span style={{ position: 'relative', whiteSpace: 'nowrap' }}
      onMouseEnter={() => setOpen(true)} onMouseLeave={() => setOpen(false)}>
      <span style={{ borderBottom: '1.5px dotted var(--ink-3)', cursor: 'help' }}>{term}</span>
      <span style={{ color: 'var(--primary)', cursor: 'help', display: 'inline-block', verticalAlign: 'middle', marginLeft: 3, position: 'relative', top: -1 }}><Icons.help size={14} /></span>
      {open && (
        <span style={{
          position: 'absolute', bottom: '135%', left: 0, zIndex: 50, width: 280, whiteSpace: 'normal',
          background: 'var(--ink)', color: '#fff', padding: '11px 13px', borderRadius: 12,
          fontSize: 13, lineHeight: 1.5, fontWeight: 400, boxShadow: 'var(--shadow-lg)',
          animation: 'pop .14s ease both',
        }}>
          <span style={{ display: 'block', fontWeight: 700, marginBottom: 3, color: 'var(--amber)' }}>In plain English</span>
          {text}
        </span>
      )}
    </span>
  );
}

// ── Section heading ─────────────────────────────────────────────────
function SectionTitle({ children, sub, action }) {
  return (
    <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', marginBottom: 14, gap: 16 }}>
      <div>
        <h2 style={{ margin: 0, fontFamily: 'var(--serif)', fontWeight: 500, fontSize: 21, color: 'var(--ink)', letterSpacing: '-.01em' }}>{children}</h2>
        {sub && <p style={{ margin: '3px 0 0', fontSize: 13.5, color: 'var(--ink-2)' }}>{sub}</p>}
      </div>
      {action}
    </div>
  );
}

// ── Modal ───────────────────────────────────────────────────────────
function Modal({ open, onClose, children, width = 560, title, sub }) {
  useEffect(() => {
    if (!open) return;
    const h = (e) => e.key === 'Escape' && onClose();
    window.addEventListener('keydown', h);
    return () => window.removeEventListener('keydown', h);
  }, [open, onClose]);
  if (!open) return null;
  return (
    <div onClick={onClose} style={{
      position: 'fixed', inset: 0, zIndex: 200, background: 'rgba(44,37,33,.34)',
      backdropFilter: 'blur(3px)', display: 'grid', placeItems: 'center', padding: 24,
      animation: 'fadeIn .18s ease both',
    }}>
      <div onClick={(e) => e.stopPropagation()} style={{
        background: 'var(--surface)', borderRadius: 'var(--radius-lg)', width, maxWidth: '100%',
        maxHeight: '90vh', overflow: 'auto', boxShadow: 'var(--shadow-lg)', animation: 'pop .2s ease both',
      }}>
        {title && (
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', padding: '24px 26px 0', gap: 16 }}>
            <div>
              <h3 style={{ margin: 0, fontFamily: 'var(--serif)', fontWeight: 500, fontSize: 23, letterSpacing: '-.01em' }}>{title}</h3>
              {sub && <p style={{ margin: '4px 0 0', fontSize: 14, color: 'var(--ink-2)' }}>{sub}</p>}
            </div>
            <button onClick={onClose} style={{ background: 'var(--surface-2)', border: 'none', borderRadius: 10, padding: 8, color: 'var(--ink-2)', display: 'grid', placeItems: 'center' }}><Icons.x size={18} /></button>
          </div>
        )}
        <div style={{ padding: '20px 26px 26px' }}>{children}</div>
      </div>
    </div>
  );
}

// ── Empty / info note ───────────────────────────────────────────────
function Note({ children, tone = 'info', icon }) {
  const tones = {
    info: { bg: 'var(--surface-2)', color: 'var(--ink-2)', ic: 'var(--ink-3)' },
    good: { bg: 'var(--good-soft)', color: '#3a7264', ic: 'var(--good)' },
    amber: { bg: 'var(--amber-soft)', color: 'var(--amber-ink)', ic: 'var(--amber-ink)' },
    primary: { bg: 'var(--primary-soft)', color: 'var(--primary-ink)', ic: 'var(--primary)' },
  }[tone];
  const I = Icons[icon || 'info'];
  return (
    <div style={{ display: 'flex', gap: 10, alignItems: 'flex-start', background: tones.bg, borderRadius: 14, padding: '13px 15px', fontSize: 13.5, lineHeight: 1.5, color: tones.color }}>
      <span style={{ color: tones.ic, flexShrink: 0, marginTop: 1 }}><I size={18} /></span>
      <span>{children}</span>
    </div>
  );
}

// number ticker
function useCountUp(target, dur = 700) {
  const [v, setV] = useState(0);
  useEffect(() => {
    let raf, start;
    const tick = (t) => {
      if (!start) start = t;
      const p = Math.min(1, (t - start) / dur);
      const e = 1 - Math.pow(1 - p, 3);
      setV(target * e);
      if (p < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [target, dur]);
  return v;
}

function setPlain(on) { PLAIN_ON = on; }

Object.assign(window, {
  Card, Button, Pill, StatusPill, STATUS, Bar, Ring, Avatar, PlainTip, SectionTitle, Modal, Note, useCountUp, setPlain,
});
