// ATLAS Accounts Email Agent — ui.jsx
// Shared UI primitives

const { useState, useEffect, useRef, useMemo, useCallback } = React;

// ── Icons ────────────────────────────────────────────────────────────────────
const Icons = {
  inbox:    p => <svg {...p} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M22 12h-6l-2 3H10l-2-3H2"/><path d="M5.45 5.11L2 12v6a2 2 0 002 2h16a2 2 0 002-2v-6l-3.45-6.89A2 2 0 0016.76 4H7.24a2 2 0 00-1.79 1.11z"/></svg>,
  check:    p => <svg {...p} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12"/></svg>,
  money:    p => <svg {...p} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><rect x="2" y="5" width="20" height="14" rx="2"/><line x1="2" y1="10" x2="22" y2="10"/></svg>,
  chart:    p => <svg {...p} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/></svg>,
  settings: p => <svg {...p} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83-2.83l.06-.06A1.65 1.65 0 004.68 15a1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 012.83-2.83l.06.06A1.65 1.65 0 009 4.68a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 2.83l-.06.06A1.65 1.65 0 0019.4 9a1.65 1.65 0 001.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z"/></svg>,
  refresh:  p => <svg {...p} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 11-2.12-9.36L23 10"/></svg>,
  plus:     p => <svg {...p} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>,
  trash:    p => <svg {...p} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14H6L5 6"/><path d="M10 11v6M14 11v6"/><path d="M9 6V4h6v2"/></svg>,
  edit:     p => <svg {...p} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>,
  download: p => <svg {...p} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>,
  alert:    p => <svg {...p} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>,
  folder:   p => <svg {...p} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M22 19a2 2 0 01-2 2H4a2 2 0 01-2-2V5a2 2 0 012-2h5l2 3h9a2 2 0 012 2z"/></svg>,
  clip:     p => <svg {...p} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66l-9.2 9.19a2 2 0 01-2.83-2.83l8.49-8.48"/></svg>,
  spin:     p => <svg {...p} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" style={{animation:'spin 1s linear infinite'}}><path d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" strokeOpacity=".25"/><path d="M21 12a9 9 0 00-9-9"/></svg>,
  sparkle:  p => <svg {...p} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M12 2l2.4 7.4H22l-6.2 4.5 2.4 7.4L12 17l-6.2 4.3 2.4-7.4L2 9.4h7.6z"/></svg>,
  copy:     p => <svg {...p} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><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"/></svg>,
};

// ── Category colour map ───────────────────────────────────────────────────────
const CAT_COLORS = {
  invoice:         { bg: '#dbeafe', color: '#1d4ed8', border: '#bfdbfe' },
  statement:       { bg: '#e0e7ff', color: '#4338ca', border: '#c7d2fe' },
  docket:          { bg: '#d1fae5', color: '#065f46', border: '#a7f3d0' },
  'urgent-payment':{ bg: '#fee2e2', color: '#991b1b', border: '#fecaca' },
  'supplier-query':{ bg: '#fef3c7', color: '#92400e', border: '#fde68a' },
  'route-fleet':   { bg: '#cffafe', color: '#155e75', border: '#a5f3fc' },
  'route-estimator':{ bg:'#f3e8ff', color: '#6b21a8', border: '#e9d5ff' },
  'route-hr':      { bg: '#fce7f3', color: '#9d174d', border: '#fbcfe8' },
  'route-other':   { bg: '#f1f5f9', color: '#475569', border: '#e2e8f0' },
  noise:           { bg: '#f3f4f6', color: '#6b7280', border: '#e5e7eb' },
};

function catStyle(cat) {
  return CAT_COLORS[cat] || { bg: '#f3f4f6', color: '#374151', border: '#e5e7eb' };
}

// ── Badge ─────────────────────────────────────────────────────────────────────
function Badge({ children, cat, kind }) {
  const s = cat ? catStyle(cat) : { bg: '#f3f4f6', color: '#374151', border: '#e5e7eb' };
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 3,
      fontSize: 10.5, fontWeight: 700, padding: '2px 8px', borderRadius: 10,
      background: s.bg, color: s.color, border: `1px solid ${s.border}`,
      textTransform: 'uppercase', letterSpacing: '0.04em', whiteSpace: 'nowrap',
    }}>{children}</span>
  );
}

// ── Confidence pill ───────────────────────────────────────────────────────────
function ConfPill({ value }) {
  const color = value >= 85 ? '#16a34a' : value >= 65 ? '#d97706' : '#dc2626';
  const bg    = value >= 85 ? '#f0fdf4' : value >= 65 ? '#fffbeb' : '#fef2f2';
  return (
    <span style={{ fontSize: 11, fontWeight: 700, padding: '2px 7px', borderRadius: 8,
      background: bg, color, border: `1px solid ${color}30` }}>{value}%</span>
  );
}

// ── Modal ─────────────────────────────────────────────────────────────────────
function Modal({ onClose, children, title, wide }) {
  useEffect(() => {
    const h = e => e.key === 'Escape' && onClose();
    document.addEventListener('keydown', h);
    return () => document.removeEventListener('keydown', h);
  }, [onClose]);
  return (
    <div style={{ position:'fixed', inset:0, background:'rgba(0,0,0,.45)', zIndex:200,
      display:'flex', alignItems:'center', justifyContent:'center', padding:24 }}
      onClick={e => e.target === e.currentTarget && onClose()}>
      <div style={{ background:'#fff', borderRadius:12, width:'100%',
        maxWidth: wide ? 760 : 520, maxHeight:'90vh', overflow:'auto',
        boxShadow:'0 20px 60px rgba(0,0,0,.2)' }}>
        {title && (
          <div style={{ padding:'18px 24px 0', display:'flex', alignItems:'center', justifyContent:'space-between' }}>
            <h2 style={{ margin:0, fontSize:16, fontWeight:700, color:'var(--navy)' }}>{title}</h2>
            <button onClick={onClose} style={{ all:'unset', cursor:'pointer', fontSize:20, color:'#9ca3af', lineHeight:1 }}>×</button>
          </div>
        )}
        {children}
      </div>
    </div>
  );
}

// ── ConfirmDialog ─────────────────────────────────────────────────────────────
function ConfirmDialog({ title, body, confirmLabel = 'Confirm', danger, onCancel, onConfirm }) {
  return (
    <Modal onClose={onCancel} title={title}>
      <div style={{ padding:'16px 24px' }}>
        <p style={{ margin:'0 0 20px', color:'#374151', fontSize:14 }}>{body}</p>
        <div style={{ display:'flex', gap:8, justifyContent:'flex-end' }}>
          <button onClick={onCancel} style={{ padding:'8px 16px', borderRadius:6, border:'1px solid #d1d5db', background:'#fff', cursor:'pointer', fontSize:13 }}>Cancel</button>
          <button onClick={onConfirm} style={{ padding:'8px 16px', borderRadius:6, border:'none',
            background: danger ? '#dc2626' : 'var(--navy)', color:'#fff', cursor:'pointer', fontSize:13, fontWeight:600 }}>{confirmLabel}</button>
        </div>
      </div>
    </Modal>
  );
}

// ── Toast ─────────────────────────────────────────────────────────────────────
function Toast({ msg, onRemove }) {
  useEffect(() => { const t = setTimeout(onRemove, 3500); return () => clearTimeout(t); }, []);
  return (
    <div style={{ background:'#1a1a18', color:'#fff', padding:'10px 18px', borderRadius:8,
      fontSize:13, fontWeight:500, boxShadow:'0 4px 20px rgba(0,0,0,.25)',
      animation:'slideUp .2s ease', marginBottom:8 }}>{msg}</div>
  );
}
function ToastHost({ toasts, remove }) {
  return (
    <div style={{ position:'fixed', bottom:24, right:24, zIndex:300 }}>
      {toasts.map(t => <Toast key={t.id} msg={t.msg} onRemove={() => remove(t.id)} />)}
    </div>
  );
}

// ── api helper ────────────────────────────────────────────────────────────────
const api = {
  async get(path) {
    const r = await fetch(path);
    if (!r.ok) throw new Error(await r.text());
    return r.json();
  },
  async _send(method, path, body) {
    const r = await fetch(path, {
      method,
      headers: { 'Content-Type': 'application/json' },
      body: body !== undefined ? JSON.stringify(body) : undefined,
    });
    if (!r.ok) { const t = await r.text(); throw new Error(t); }
    if (r.status === 204) return { ok: true };
    const text = await r.text();
    if (!text) return { ok: true };
    try { return JSON.parse(text); } catch { return { ok: true }; }
  },
  post:   (p, b) => api._send('POST',   p, b),
  patch:  (p, b) => api._send('PATCH',  p, b),
  put:    (p, b) => api._send('PUT',    p, b),
  delete: (p)    => api._send('DELETE', p),
};

// ── formatters ────────────────────────────────────────────────────────────────
function fmtDate(d) {
  if (!d) return '—';
  return new Date(d).toLocaleDateString('en-AU', { day:'numeric', month:'short', year:'numeric' });
}
function fmtAmt(n) {
  if (n == null || n === '') return '—';
  return '$' + Number(n).toLocaleString('en-AU', { minimumFractionDigits:2, maximumFractionDigits:2 });
}
function fmtCatLabel(cat) {
  const map = {
    invoice:'Invoice', statement:'Statement', docket:'Docket',
    'urgent-payment':'Urgent Payment', 'supplier-query':'Supplier Query',
    'route-fleet':'→ Fleet', 'route-estimator':'→ Estimator',
    'route-hr':'→ HR', 'route-other':'→ Other', noise:'Noise',
  };
  return map[cat] || cat;
}

// Expose everything
Object.assign(window, {
  Icons, Badge, ConfPill, Modal, ConfirmDialog, Toast, ToastHost, api,
  catStyle, fmtDate, fmtAmt, fmtCatLabel, CAT_COLORS,
  useState, useEffect, useRef, useMemo, useCallback,
});
