/* ============================================================
   IMTRACK — Shared UI components (React, Babel)
   Exposes components on window for module scripts.
   ============================================================ */
const { useState, useRef, useEffect, useMemo, createContext, useContext } = React;

/* ---------------- Icons (Lucide-style) ---------------- */
const ICON_PATHS = {
  'layout-dashboard': '<rect width="7" height="9" x="3" y="3" rx="1"/><rect width="7" height="5" x="14" y="3" rx="1"/><rect width="7" height="9" x="14" y="12" rx="1"/><rect width="7" height="5" x="3" y="16" rx="1"/>',
  'clipboard-list': '<rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/><path d="M12 11h4"/><path d="M12 16h4"/><path d="M8 11h.01"/><path d="M8 16h.01"/>',
  'file-text': '<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M10 9H8"/><path d="M16 13H8"/><path d="M16 17H8"/>',
  'receipt': '<path d="M4 2v20l2-1 2 1 2-1 2 1 2-1 2 1 2-1 2 1V2l-2 1-2-1-2 1-2-1-2 1-2-1-2 1Z"/><path d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8"/><path d="M12 17.5v-11"/>',
  'bar-chart-2': '<line x1="18" x2="18" y1="20" y2="10"/><line x1="12" x2="12" y1="20" y2="4"/><line x1="6" x2="6" y1="20" y2="14"/>',
  'bar-chart': '<line x1="12" x2="12" y1="20" y2="10"/><line x1="18" x2="18" y1="20" y2="4"/><line x1="6" x2="6" y1="20" y2="16"/>',
  'settings': '<path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/>',
  'users': '<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>',
  'plus': '<path d="M5 12h14"/><path d="M12 5v14"/>',
  'pencil': '<path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/><path d="m15 5 4 4"/>',
  'trash-2': '<path d="M3 6h18"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><line x1="10" x2="10" y1="11" y2="17"/><line x1="14" x2="14" y1="11" y2="17"/>',
  'copy': '<rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/>',
  'download': '<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" x2="12" y1="15" y2="3"/>',
  'upload': '<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" x2="12" y1="3" y2="15"/>',
  'search': '<circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/>',
  'filter': '<polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/>',
  'columns': '<rect width="18" height="18" x="3" y="3" rx="2"/><path d="M12 3v18"/>',
  'chevron-down': '<path d="m6 9 6 6 6-6"/>',
  'chevron-up': '<path d="m18 15-6-6-6 6"/>',
  'chevron-right': '<path d="m9 18 6-6-6-6"/>',
  'chevrons-up-down': '<path d="m7 15 5 5 5-5"/><path d="m7 9 5-5 5 5"/>',
  'external-link': '<path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>',
  'alert-circle': '<circle cx="12" cy="12" r="10"/><line x1="12" x2="12" y1="8" y2="12"/><line x1="12" x2="12.01" y1="16" y2="16"/>',
  'alert-triangle': '<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><line x1="12" x2="12" y1="9" y2="13"/><line x1="12" x2="12.01" y1="17" y2="17"/>',
  'flag': '<path d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z"/><line x1="4" x2="4" y1="22" y2="15"/>',
  'check-circle': '<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/>',
  'check': '<polyline points="20 6 9 17 4 12"/>',
  'info': '<circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/>',
  'x': '<path d="M18 6 6 18"/><path d="m6 6 12 12"/>',
  'calendar': '<rect width="18" height="18" x="3" y="4" rx="2" ry="2"/><line x1="16" x2="16" y1="2" y2="6"/><line x1="8" x2="8" y1="2" y2="6"/><line x1="3" x2="21" y1="10" y2="10"/>',
  'link-2': '<path d="M9 17H7A5 5 0 0 1 7 7h2"/><path d="M15 7h2a5 5 0 1 1 0 10h-2"/><line x1="8" x2="16" y1="12" y2="12"/>',
  'file-up': '<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M12 12v6"/><path d="m15 15-3-3-3 3"/>',
  'arrow-up': '<path d="m5 12 7-7 7 7"/><path d="M12 19V5"/>',
  'arrow-down': '<path d="M12 5v14"/><path d="m19 12-7 7-7-7"/>',
  'arrow-right': '<path d="M5 12h14"/><path d="m12 5 7 7-7 7"/>',
  'inbox': '<polyline points="22 12 16 12 14 15 10 15 8 12 2 12"/><path d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"/>',
  'sliders': '<line x1="4" x2="4" y1="21" y2="14"/><line x1="4" x2="4" y1="10" y2="3"/><line x1="12" x2="12" y1="21" y2="12"/><line x1="12" x2="12" y1="8" y2="3"/><line x1="20" x2="20" y1="21" y2="16"/><line x1="20" x2="20" y1="12" y2="3"/><line x1="2" x2="6" y1="14" y2="14"/><line x1="10" x2="14" y1="8" y2="8"/><line x1="18" x2="22" y1="16" y2="16"/>',
  'wallet': '<path d="M19 7V4a1 1 0 0 0-1-1H5a2 2 0 0 0 0 4h15a1 1 0 0 1 1 1v4h-3a2 2 0 0 0 0 4h3a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1"/><path d="M3 5v14a2 2 0 0 0 2 2h15a1 1 0 0 0 1-1v-4"/>',
  'building': '<rect width="16" height="20" x="4" y="2" rx="2" ry="2"/><path d="M9 22v-4h6v4"/><path d="M8 6h.01"/><path d="M16 6h.01"/><path d="M12 6h.01"/><path d="M12 10h.01"/><path d="M12 14h.01"/><path d="M16 10h.01"/><path d="M16 14h.01"/><path d="M8 10h.01"/><path d="M8 14h.01"/>',
  'lock': '<rect width="18" height="11" x="3" y="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>',
  'file-search': '<path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M4.268 21a2 2 0 0 0 1.727 1H18a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v3"/><path d="m9 18-1.5-1.5"/><circle cx="7" cy="16" r="3"/>',
  'rotate-ccw': '<path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/>',
};
function Icon({ name, size, color, style, className }) {
  const s = size || 18;
  return (
    <svg className={className} width={s} height={s} viewBox="0 0 24 24" fill="none"
      stroke={color || 'currentColor'} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"
      style={style} dangerouslySetInnerHTML={{ __html: ICON_PATHS[name] || '' }} />
  );
}

/* ---------------- Status / alert / traffic ---------------- */
const COLOR_CLASS = { green: 'badge-green', warning: 'badge-warning', critical: 'badge-critical', flag: 'badge-flag', blue: 'badge-blue', navy: 'badge-navy', neutral: 'badge-neutral' };
function StatusBadge({ store, list, id, dot }) {
  const E = window.IMTRACK_ENGINE;
  const v = E.lk(store, list, id);
  if (!v) return <span className="badge badge-neutral">{id}</span>;
  const color = v.color === 'amber' ? 'warning' : v.color === 'red' ? 'critical' : v.color;
  const cls = COLOR_CLASS[color] || 'badge-neutral';
  const dotColors = { green: '#16a34a', warning: '#d97706', critical: '#b45309', flag: '#7c3aed', blue: '#2563eb', navy: '#1e3a5f', neutral: '#64748b' };
  return (
    <span className={'badge ' + cls + (v.active ? '' : ' muted')} aria-label={v.label} title={v.active ? v.label : v.label + ' (deactivated)'}>
      {dot && <span className="dot" style={{ background: dotColors[color] || dotColors.neutral }} />}
      {v.label}
    </span>
  );
}
function AlertBadge({ sev }) {
  const normalized = sev === 'amber' ? 'warning' : sev === 'red' ? 'critical' : sev;
  const map = { critical: ['abadge-critical', '● CRITICAL'], warning: ['abadge-warning', '▲ WARNING'], flag: ['abadge-flag', '◆ FLAG'], green: ['abadge-green', '✓ OK'] };
  const [cls, label] = map[normalized] || map.flag;
  return <span className={'abadge ' + cls}>{label}</span>;
}
function SourceBadge({ source }) {
  const styles = {
    sap:  { background: '#e0f2fe', color: '#0369a1', label: 'SAP' },
    manual: { background: 'var(--color-slate-100)', color: 'var(--color-slate-700)', label: 'Manual' },
    derived: { background: '#ede9fe', color: '#7c3aed', label: 'Derived' },
    amendment: { background: '#fffbeb', color: '#b45309', label: 'Amendment' },
    import: { background: '#dcfce7', color: '#15803d', label: 'Import' },
  };
  const s = styles[(source || '').toLowerCase()] || styles.manual;
  return <span className="badge" style={{ fontSize: 10, padding: '0 6px', height: 18, background: s.background, color: s.color, fontWeight: 600, letterSpacing: '.03em', textTransform: 'uppercase' }}>{s.label}</span>;
}
function TrafficLight({ light, label, size }) {
  const normalized = light === 'amber' ? 'warning' : light === 'red' ? 'critical' : (light || 'neutral');
  const titles = { green: 'On Track', warning: 'Warning', critical: 'Overrun', neutral: 'No Data' };
  const cls = 'tl-' + normalized;
  if (size === 'sq') return <span className={'tl-square ' + cls} title={titles[normalized]} />;
  return (
    <span className="row" style={{ gap: 7 }} title={titles[normalized]}>
      <span className={'tl-dot ' + cls} />
      {label !== false && <span className="muted" style={{ fontSize: 11 }}>{titles[normalized]}</span>}
    </span>
  );
}

/* ---------------- Money cell ---------------- */
function Money({ v, cur, dec, dash }) {
  const E = window.IMTRACK_ENGINE;
  if (v === null || v === undefined || (v === 0 && dash)) return <span className="dash">—</span>;
  const neg = v < 0;
  const fn = dec === 2 ? E.fmtUSD2 : (cur && cur !== 'USD' ? (x) => E.fmtMoney(x, cur) : E.fmtUSD0);
  const txt = fn(Math.abs(v));
  return <span className={'num' + (neg ? ' neg' : '')}>{neg ? '(' + txt + ')' : txt}</span>;
}

/* ---------------- Page header ---------------- */
function PageHeader({ title, subtitle, children }) {
  return (
    <div className="page-header">
      <div>
        <h1 className="page-title">{title}</h1>
        {subtitle && <div className="page-subtitle">{subtitle}</div>}
      </div>
      {children && <div className="page-actions">{children}</div>}
    </div>
  );
}

/* ---------------- Empty state ---------------- */
function EmptyState({ icon, title, sub, action }) {
  return (
    <div className="empty">
      <Icon name={icon || 'inbox'} size={44} />
      <div className="et">{title}</div>
      {sub && <div className="es">{sub}</div>}
      {action && <div className="mt16">{action}</div>}
    </div>
  );
}

/* ---------------- Modal ---------------- */
function Modal({ title, subtitle, size, onClose, children, footer }) {
  useEffect(() => {
    const h = (e) => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', h);
    return () => window.removeEventListener('keydown', h);
  }, [onClose]);
  return (
    <div className="modal-overlay" onClick={onClose}>
      <div className={'modal ' + (size || '')} onClick={(e) => e.stopPropagation()}>
        <div className="modal-head">
          <div>
            <h3>{title}</h3>
            {subtitle && <div className="sub">{subtitle}</div>}
          </div>
          <button className="icon-btn" onClick={onClose} aria-label="Close"><Icon name="x" size={18} /></button>
        </div>
        <div className="modal-body">{children}</div>
        {footer && <div className="modal-foot">{footer}</div>}
      </div>
    </div>
  );
}

/* ---------------- Confirm dialog ---------------- */
function ConfirmDialog({ title, message, confirmLabel, destructive, onConfirm, onClose }) {
  return (
    <Modal title={title} size="sm" onClose={onClose} footer={
      <>
        <button className="btn btn-secondary" onClick={onClose}>Cancel</button>
        <button className={'btn ' + (destructive ? 'btn-destructive' : 'btn-primary')} onClick={() => { onConfirm(); onClose(); }}>{confirmLabel || 'Confirm'}</button>
      </>
    }>
      <div className="row" style={{ alignItems: 'flex-start', gap: 12 }}>
        <Icon name={destructive ? 'alert-triangle' : 'info'} size={28} color={destructive ? '#dc2626' : '#2563eb'} />
        <div style={{ fontSize: 'var(--text-sm)', color: 'var(--text-secondary)', lineHeight: 1.55 }}>{message}</div>
      </div>
    </Modal>
  );
}

/* ---------------- Field helpers ---------------- */
function Field({ label, req, hint, children, span }) {
  return (
    <div className={'field' + (span ? ' span2' : '')}>
      {label && <label className="field-label">{label}{req && <span className="req">*</span>}</label>}
      {children}
      {hint && <div className="field-hint">{hint}</div>}
    </div>
  );
}
function Select({ value, onChange, options, placeholder }) {
  return (
    <select className="select" value={value} onChange={(e) => onChange(e.target.value)}>
      {placeholder && <option value="">{placeholder}</option>}
      {options.map((o) => <option key={o.value} value={o.value}>{o.label}</option>)}
    </select>
  );
}

/* ---------------- Sortable table head ---------------- */
function SortHead({ label, col, sort, setSort, numeric }) {
  const active = sort && sort.col === col;
  return (
    <th className={'sortable' + (numeric ? ' num-col' : '')} onClick={() => setSort({ col, dir: active && sort.dir === 'asc' ? 'desc' : 'asc' })}>
      <span className="th-inner" style={{ flexDirection: numeric ? 'row-reverse' : 'row' }}>
        {label}
        {active
          ? <Icon name={sort.dir === 'asc' ? 'arrow-up' : 'arrow-down'} size={13} color="#2563eb" />
          : <Icon name="chevrons-up-down" size={13} color="#cbd5e1" />}
      </span>
    </th>
  );
}
function sortRows(rows, sort, accessors) {
  if (!sort) return rows;
  const acc = accessors[sort.col] || ((r) => r[sort.col]);
  const dir = sort.dir === 'asc' ? 1 : -1;
  return [...rows].sort((a, b) => {
    let va = acc(a), vb = acc(b);
    if (va === null || va === undefined) va = '';
    if (vb === null || vb === undefined) vb = '';
    if (typeof va === 'number' && typeof vb === 'number') return (va - vb) * dir;
    return String(va).localeCompare(String(vb)) * dir;
  });
}

/* ---------------- Toast ---------------- */
const ToastCtx = createContext(null);
function ToastProvider({ children }) {
  const [toasts, setToasts] = useState([]);
  const push = (msg, kind) => {
    const id = Math.random();
    setToasts((t) => [...t, { id, msg, kind }]);
    setTimeout(() => setToasts((t) => t.filter((x) => x.id !== id)), 3200);
  };
  return (
    <ToastCtx.Provider value={push}>
      {children}
      <div className="toast-wrap">
        {toasts.map((t) => (
          <div key={t.id} className={'toast ' + (t.kind || '')}>
            <Icon name={t.kind === 'good' ? 'check-circle' : t.kind === 'warn' ? 'alert-triangle' : 'info'} size={18}
              color={t.kind === 'good' ? '#16a34a' : t.kind === 'warn' ? '#d97706' : '#2563eb'} />
            <span>{t.msg}</span>
          </div>
        ))}
      </div>
    </ToastCtx.Provider>
  );
}
const useToast = () => useContext(ToastCtx) || (() => {});

/* ---------------- CSV export ---------------- */
function exportCsv(filename, headers, rows) {
  const esc = (v) => {
    if (v === null || v === undefined) return '';
    const s = String(v);
    return /[",\n]/.test(s) ? '"' + s.replace(/"/g, '""') + '"' : s;
  };
  const csv = [headers.map(esc).join(','), ...rows.map((r) => r.map(esc).join(','))].join('\n');
  const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url; a.download = filename; a.click();
  URL.revokeObjectURL(url);
}

/* progress bar */
function PBar({ pct, light }) {
  const colors = { green: '#16a34a', warning: '#d97706', critical: '#b45309', neutral: '#cbd5e1' };
  const w = Math.max(0, Math.min(100, pct));
  return <div className="pbar"><span style={{ width: w + '%', background: colors[light] || '#2563eb' }} /></div>;
}

Object.assign(window, {
  Icon, StatusBadge, AlertBadge, TrafficLight, Money, PageHeader, EmptyState,
  Modal, ConfirmDialog, Field, Select, SortHead, sortRows, ToastProvider, useToast,
  exportCsv, PBar,
});
