// Shared UI primitives + icons for Hitcents ERP
// Avatar, Badge, Button, Icon, Drawer, Modal, KBD, Section header

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

// ============ Icons (lightweight, monoline) ============
const ICONS = {
  home:     'M3 11.5 12 4l9 7.5V20a1 1 0 0 1-1 1h-5v-6h-6v6H4a1 1 0 0 1-1-1Z',
  users:    'M16 14a4 4 0 1 0-4-4 4 4 0 0 0 4 4Zm-8 0a4 4 0 1 0-4-4 4 4 0 0 0 4 4Zm0 2c-3.3 0-8 1.7-8 5v3h10v-3a5.7 5.7 0 0 1 2.3-4.6A11 11 0 0 0 8 16Zm8 0a11 11 0 0 0-2.6.3A5.7 5.7 0 0 1 16 21v3h8v-3c0-3.3-4.7-5-8-5Z',
  folder:   'M3 6a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2Z',
  spark:    'M12 2 14 9l7 2-7 2-2 7-2-7-7-2 7-2Z',
  briefcase:'M9 4h6a2 2 0 0 1 2 2v2h4a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2v-9a2 2 0 0 1 2-2h4V6a2 2 0 0 1 2-2Zm0 4h6V6H9Z',
  megaphone:'M3 11v2a2 2 0 0 0 2 2h1l4 5v-3l9 3V4l-9 3H5a2 2 0 0 0-2 2Z',
  bulb:     'M9 21h6m-3-19a7 7 0 0 0-4 12.7V18h8v-3.3A7 7 0 0 0 12 2Z',
  headset:  'M4 14v3a3 3 0 0 0 3 3h1v-7H7a3 3 0 0 0-3 1Zm16 0a3 3 0 0 0-3-1h-1v7h1a3 3 0 0 0 3-3Zm0-2a8 8 0 0 0-16 0',
  store:    'M3 7l2-4h14l2 4M3 7v2a3 3 0 0 0 6 0 3 3 0 0 0 6 0 3 3 0 0 0 6 0V7M5 11v9h14v-9',
  card:     'M3 7a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2Zm0 4h18',
  settings: 'M12 8a4 4 0 1 1-4 4 4 4 0 0 1 4-4Zm9 3-2.3-.4a7 7 0 0 0-.7-1.6l1.4-2-2.4-2.4-2 1.4a7 7 0 0 0-1.6-.7L13 3h-3l-.4 2.3a7 7 0 0 0-1.6.7l-2-1.4-2.4 2.4 1.4 2a7 7 0 0 0-.7 1.6L3 11v3l2.3.4a7 7 0 0 0 .7 1.6l-1.4 2 2.4 2.4 2-1.4a7 7 0 0 0 1.6.7l.4 2.3h3l.4-2.3a7 7 0 0 0 1.6-.7l2 1.4 2.4-2.4-1.4-2a7 7 0 0 0 .7-1.6L21 14Z',
  search:   'M11 4a7 7 0 1 0 4.3 12.5L20 21l1.5-1.5L17 15a7 7 0 0 0-6-11Zm0 2a5 5 0 1 1-5 5 5 5 0 0 1 5-5Z',
  bell:     'M12 3a6 6 0 0 0-6 6v4l-2 3v1h16v-1l-2-3V9a6 6 0 0 0-6-6Zm-2 17a2 2 0 0 0 4 0Z',
  plus:     'M12 5v14M5 12h14',
  x:        'M6 6l12 12M18 6 6 18',
  check:    'M5 12l5 5 9-11',
  chev_r:   'M9 6l6 6-6 6',
  chev_l:   'M15 6l-6 6 6 6',
  chev_d:   'M6 9l6 6 6-6',
  chev_u:   'M6 15l6-6 6 6',
  more:     'M5 12a2 2 0 1 1 4 0 2 2 0 0 1-4 0Zm6 0a2 2 0 1 1 4 0 2 2 0 0 1-4 0Zm6 0a2 2 0 1 1 4 0 2 2 0 0 1-4 0Z',
  filter:   'M4 5h16l-6 8v6l-4-2v-4Z',
  cal:      'M4 6a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v14H4Zm0 5h16M8 2v4m8-4v4',
  clock:    'M12 6v6l4 2M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z',
  mail:     'M3 5h18v14H3Zm0 0 9 8 9-8',
  chat:     'M4 5a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H9l-4 4v-4H4Z',
  phone:    'M5 4h4l2 5-3 2a12 12 0 0 0 5 5l2-3 5 2v4a2 2 0 0 1-2 2A16 16 0 0 1 3 6a2 2 0 0 1 2-2Z',
  flag:     'M5 21V4h10l-2 4 2 4H5',
  link:     'M10 14a4 4 0 0 0 5.7 0l3-3a4 4 0 0 0-5.7-5.7l-1 1m-2 8a4 4 0 0 1-5.7 0l-3-3a4 4 0 0 1 0-5.7l3-3a4 4 0 0 1 5.7 0',
  dots_v:   'M12 5v.01M12 12v.01M12 19v.01',
  star:     'M12 3l3 6 7 1-5 5 1 7-6-3-6 3 1-7-5-5 7-1Z',
  pencil:   'M14 4l6 6L9 21H3v-6Zm0 0 6 6',
  trash:    'M5 7h14m-2 0v12a2 2 0 0 1-2 2H9a2 2 0 0 1-2-2V7m3 0V4h4v3',
  upload:   'M12 4v12m0-12 -5 5m5-5 5 5M5 20h14',
  download: 'M12 4v12m0 0 5-5m-5 5-5-5M5 20h14',
  lock:     'M6 11h12v9H6Zm2 0V8a4 4 0 0 1 8 0v3',
  shield:   'M12 3 4 6v6a8 8 0 0 0 8 8 8 8 0 0 0 8-8V6Z',
  globe:    'M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18Zm0 0a13 13 0 0 0 0 18M3 12h18',
  send:     'M4 12 20 4l-3 16-5-6Zm8-2v8',
  pin:      'M12 21v-7m0 0 6-6-2-3H8L6 8Z',
  doc:      'M6 3h9l5 5v13H6Zm9 0v5h5',
  arrow_r:  'M5 12h14m0 0-6-6m6 6-6 6',
  arrow_u:  'M12 19V5m0 0-6 6m6-6 6 6',
  arrow_d:  'M12 5v14m0 0-6-6m6 6 6-6',
  drag:     'M9 5a1 1 0 1 1-2 0 1 1 0 0 1 2 0Zm0 7a1 1 0 1 1-2 0 1 1 0 0 1 2 0Zm0 7a1 1 0 1 1-2 0 1 1 0 0 1 2 0Zm8-14a1 1 0 1 1-2 0 1 1 0 0 1 2 0Zm0 7a1 1 0 1 1-2 0 1 1 0 0 1 2 0Zm0 7a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z',
  bolt:     'M13 2 4 14h7l-1 8 9-12h-7Z',
  key:      'M14 7a4 4 0 1 0-3.4 6.9L4 20.5V22h3v-2h2v-2h2l1.6-1.6A4 4 0 0 0 14 7Zm1.6 2.4a1 1 0 1 1-1.4-1.4 1 1 0 0 1 1.4 1.4Z',
  eye:      'M2 12s4-7 10-7 10 7 10 7-4 7-10 7S2 12 2 12Zm10 3a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z',
  eye_off:  'M3 3l18 18M10.6 10.6a3 3 0 0 0 4.2 4.2M9.4 5.2A10.6 10.6 0 0 1 12 5c6 0 10 7 10 7a17 17 0 0 1-3 3.6M6.2 6.2A17 17 0 0 0 2 12s4 7 10 7a10.6 10.6 0 0 0 2.6-.3',
  copy:     'M9 9h10v10H9Zm0 0V5h10v4M5 15H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v1',
  layers:   'M12 3 2 8l10 5 10-5Zm0 11L2 8m20 0-10 6m10 0L12 19 2 14',
  chart:    'M4 20V4m0 16h16M8 16V10m4 6V6m4 10v-8',
};

function Icon({ name, size = 16, stroke = 1.6, className, style }) {
  const path = ICONS[name];
  if (!path) return null;
  return (
    <svg
      width={size} height={size} viewBox="0 0 24 24"
      fill="none" stroke="currentColor" strokeWidth={stroke}
      strokeLinecap="round" strokeLinejoin="round"
      className={className} style={style} aria-hidden="true">
      <path d={path} />
    </svg>
  );
}

// ============ Avatar ============
// ---- Uploaded profile images (persisted, keyed by person id) ----
const AVATAR_IMG = (() => {
  try { return JSON.parse(localStorage.getItem('hc-avatar-img') || '{}'); } catch { return {}; }
})();
function getAvatarImage(person) {
  if (!person) return null;
  return person.img || AVATAR_IMG[person.id] || null;
}
function setAvatarImage(id, dataUrl) {
  if (!id) return;
  AVATAR_IMG[id] = dataUrl;
  try { localStorage.setItem('hc-avatar-img', JSON.stringify(AVATAR_IMG)); } catch {}
  const D = window.HitcentsData || {};
  const rec = (D.EMPLOYEES || []).find(e => e.id === id) || (D.CONTACTS || []).find(c => c.id === id);
  if (rec) rec.img = dataUrl;
  window.dispatchEvent(new CustomEvent('hitcents:avatar-changed', { detail: { id } }));
}
// Read a File, downscale to a small square, persist as data URL for `id`.
function uploadAvatarFile(id, file) {
  if (!file || !id) return;
  const reader = new FileReader();
  reader.onload = () => {
    const img = new Image();
    img.onload = () => {
      const S = 168;
      const canvas = document.createElement('canvas');
      canvas.width = S; canvas.height = S;
      const ctx = canvas.getContext('2d');
      const scale = Math.max(S / img.width, S / img.height);
      const w = img.width * scale, h = img.height * scale;
      ctx.drawImage(img, (S - w) / 2, (S - h) / 2, w, h);
      setAvatarImage(id, canvas.toDataURL('image/jpeg', 0.85));
    };
    img.src = reader.result;
  };
  reader.readAsDataURL(file);
}
// Opens a file picker and uploads the chosen image for `id`.
function pickAvatarImage(id) {
  const input = document.createElement('input');
  input.type = 'file';
  input.accept = 'image/*';
  input.onchange = () => { if (input.files && input.files[0]) uploadAvatarFile(id, input.files[0]); };
  input.click();
}

function Avatar({ person, size = 28, ring }) {
  if (!person) return <div className="avatar" style={{ width: size, height: size, background: '#E8E2DA' }} />;
  const imgSrc = getAvatarImage(person);
  const shadow = ring ? '0 0 0 2px #fff, 0 0 0 4px var(--accent)' : undefined;
  if (imgSrc) {
    return (
      <div className="avatar" title={person.name}
        style={{ width: size, height: size, padding: 0, overflow: 'hidden', boxShadow: shadow }}>
        <img src={imgSrc} alt={person.name} style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
      </div>
    );
  }
  const { initials, hue } = person.avatar || { initials: '?', hue: 30 };
  const bg = `oklch(72% 0.09 ${hue})`;
  const fg = `oklch(28% 0.06 ${hue})`;
  return (
    <div className="avatar" title={person.name}
      style={{
        width: size, height: size, background: bg, color: fg,
        fontSize: Math.max(10, size * 0.38),
        boxShadow: shadow
      }}>
      {initials}
    </div>
  );
}

// ============ Badge / Pill ============
function Badge({ children, tone = 'neutral', size, style }) {
  return <span className={`badge badge-${tone}`} style={{ ...(size === 'lg' ? { padding: '4px 10px', fontSize: 12 } : null), ...style }}>{children}</span>;
}

// ============ Button ============
function Button({ children, variant = 'ghost', size = 'md', icon, iconRight, onClick, type, disabled, className, style, title }) {
  return (
    <button
      type={type || 'button'}
      onClick={onClick}
      disabled={disabled}
      title={title}
      className={`btn btn-${variant} btn-${size} ${className || ''}`}
      style={style}>
      {icon && <Icon name={icon} size={size === 'sm' ? 13 : 15} />}
      {children}
      {iconRight && <Icon name={iconRight} size={size === 'sm' ? 13 : 15} />}
    </button>
  );
}

// ============ KBD ============
function Kbd({ children }) { return <kbd className="kbd">{children}</kbd>; }

// ============ Drawer → renders as a centered MODAL ============
// (Project rule: no side-drawers anywhere except Internal Comms/Chat.
//  Kept the name `Drawer` so every existing call site becomes a modal.)
function Drawer({ open, onClose, children, width = 520, title, actions }) {
  if (!open) return null;
  return (
    <div className="modal-scrim" onClick={onClose}>
      <div className="modal-dialog" style={{ width }} onClick={e => e.stopPropagation()}>
        <div className="modal-head">
          <div style={{ fontWeight: 600, fontSize: 14 }}>{title}</div>
          <div className="row" style={{ gap: 6 }}>
            {actions}
            <button className="icon-btn" onClick={onClose} aria-label="Close"><Icon name="x" size={16} /></button>
          </div>
        </div>
        <div className="modal-body">{children}</div>
      </div>
    </div>
  );
}

// ============ Helpers ============
const fmtMoney = (n) => {
  if (n == null) return '—';
  const a = Math.abs(n);
  if (a >= 1_000_000) return `$${(n/1_000_000).toFixed(a >= 10_000_000 ? 1 : 2)}M`;
  if (a >= 1_000)     return `$${(n/1_000).toFixed(a >= 10_000 ? 0 : 1)}k`;
  return `$${n}`;
};
const fmtMoneyFull = (n) => n == null ? '—' : `$${n.toLocaleString('en-US')}`;
const fmtDate = (d) => d ? new Date(d).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) : '—';
const fmtDateShort = (d) => d ? new Date(d).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '—';

function timeAgo(iso) {
  const t = new Date(iso).getTime();
  const diff = (Date.now() - t) / 1000;
  if (diff < 60) return 'now';
  if (diff < 3600) return Math.floor(diff/60) + 'm';
  if (diff < 86400) return Math.floor(diff/3600) + 'h';
  return Math.floor(diff/86400) + 'd';
}

function getEmployee(id) { return window.HitcentsData.EMPLOYEES.find(e => e.id === id); }
function getDept(id) { return window.HitcentsData.DEPTS.find(d => d.id === id); }
function getCustomer(id) { return window.HitcentsData.CUSTOMERS.find(c => c.id === id); }
function getContact(id) { return (window.HitcentsData.CONTACTS || []).find(c => c.id === id); }
function getContactsForCompany(companyId) { return (window.HitcentsData.CONTACTS || []).filter(c => c.companyId === companyId); }
function getVendor(id) { return (window.HitcentsData.VENDORS || []).find(v => v.id === id); }
function getVendorContacts(vendorId) { return (window.HitcentsData.VENDOR_CONTACTS || []).filter(c => c.vendorId === vendorId); }
function getLicensesForVendor(vendor) {
  if (!vendor) return [];
  return (vendor.licenses || []).map(id => (window.HitcentsData.LICENSES || []).find(l => l.id === id)).filter(Boolean);
}
function getVendorForLicense(licId) {
  return (window.HitcentsData.VENDORS || []).find(v => (v.licenses || []).includes(licId));
}
function getContractsForVendor(vendor) {
  if (!vendor) return [];
  const aliases = (vendor.aliases || [vendor.name]).map(a => a.toLowerCase());
  return (window.HitcentsData.CONTRACTS || []).filter(c => c.type === 'vendor' && aliases.includes((c.counterparty || '').toLowerCase()));
}
function getVendorForContract(contract) {
  if (!contract) return null;
  const cp = (contract.counterparty || '').toLowerCase();
  return (window.HitcentsData.VENDORS || []).find(v => (v.aliases || [v.name]).map(a => a.toLowerCase()).includes(cp));
}

// Pop-out window behaviour shared by the Cents + Chat overlays.
// When popped, the shell becomes a floating, draggable window and the
// scrim stops blocking the app behind it.
function usePopout(defaultPos) {
  const [popped, setPopped] = useState(false);
  const [pos, setPos] = useState(defaultPos || { x: 160, y: 84 });
  const drag = useRef(null);
  function onHeaderPointerDown(e) {
    if (!popped) return;
    if (e.target.closest('button, input, textarea, select, a')) return;
    drag.current = { sx: e.clientX, sy: e.clientY, ox: pos.x, oy: pos.y };
    function move(ev) {
      if (!drag.current) return;
      setPos({
        x: Math.max(0, Math.min(window.innerWidth - 120, drag.current.ox + ev.clientX - drag.current.sx)),
        y: Math.max(0, Math.min(window.innerHeight - 60, drag.current.oy + ev.clientY - drag.current.sy)),
      });
    }
    function up() { drag.current = null; window.removeEventListener('pointermove', move); window.removeEventListener('pointerup', up); }
    window.addEventListener('pointermove', move);
    window.addEventListener('pointerup', up);
  }
  return { popped, setPopped, pos, onHeaderPointerDown };
}

// File extension → icon color
const FILE_KINDS = {
  pdf: { color: '#C2452C', label: 'PDF' },
  docx:{ color: '#3E73C7', label: 'DOC' },
  xlsx:{ color: '#2F8B5A', label: 'XLS' },
  pptx:{ color: '#D18A1F', label: 'PPT' },
  md:  { color: '#5C6470', label: 'MD' },
  txt: { color: '#5C6470', label: 'TXT' },
  fig: { color: '#A24FB8', label: 'FIG' },
  svg: { color: '#0E8A95', label: 'SVG' },
  zip: { color: '#5C6470', label: 'ZIP' },
};

function fileBytes(n) {
  if (!n) return '—';
  if (n < 1024) return n + ' B';
  if (n < 1024*1024) return Math.round(n/1024) + ' KB';
  return (n / (1024*1024)).toFixed(1) + ' MB';
}

Object.assign(window, {
  Icon, Avatar, Badge, Button, Kbd, Drawer,
  getAvatarImage, setAvatarImage, uploadAvatarFile, pickAvatarImage,
  fmtMoney, fmtMoneyFull, fmtDate, fmtDateShort, timeAgo,
  getEmployee, getDept, getCustomer,
  getContact, getContactsForCompany,
  getVendor, getVendorContacts, getLicensesForVendor, getVendorForLicense, getContractsForVendor, getVendorForContract,
  usePopout,
  FILE_KINDS, fileBytes,
});
