// Inventory.jsx — master database of everything purchased for the store.
// Lives in Setup. Feeds the Truck Order par system (built next).
//
// Three tabs:
//   Items        — table list with sort + filter chips + search
//   Categories   — CRUD (like Training categories)
//   Storage      — CRUD storage-area labels
//
// Editor: full-form modal with vendor aliases (chip list), price history
// (auto-logged when price changes), conversion ratio between purchase & storage units.

const { useState: useInv, useMemo: useMemoInv } = React;

const INVENTORY_ITEMS_KEY      = 'portal_inventory_items_v2';   // bumped: vendor → vendorId
const INVENTORY_CATEGORIES_KEY = 'portal_inventory_categories_v1';
const STORAGE_AREAS_KEY        = 'portal_storage_areas_v1';
const INVENTORY_VENDORS_KEY    = 'portal_inventory_vendors_v1';

const seedInvItems      = (window.SAMPLE_INVENTORY_ITEMS || []).slice();
const seedInvCategories = (window.SAMPLE_INVENTORY_CATEGORIES || []).slice();
const seedStorageAreas  = (window.SAMPLE_STORAGE_AREAS || []).slice();
const seedVendors       = (window.SAMPLE_VENDORS || []).slice();

// Money formatter
const fmtMoney = (n) => {
  if (n === null || n === undefined || isNaN(n)) return '—';
  return '$' + Number(n).toFixed(2);
};
const fmtDateShort = (iso) => {
  if (!iso) return '';
  // 'YYYY-MM-DD' → 'May 1, 2026'
  const [y, m, d] = iso.split('-').map(Number);
  const dt = new Date(y, m - 1, d);
  return dt.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' });
};
const todayIso = () => {
  const d = new Date();
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
};

// Local form field helper
const InvField = ({ label, subtitle, children, span }) => (
  <div style={{ marginBottom: 16, gridColumn: span ? `span ${span}` : undefined }}>
    <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--fg-1)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 4 }}>{label}</div>
    {subtitle && <div style={{ fontSize: 12, color: 'var(--fg-3)', marginBottom: 8, lineHeight: 1.4 }}>{subtitle}</div>}
    {children}
  </div>
);

// Small colored pill for category, mirrors CategoryPill in Training.
const InvCategoryPill = ({ category, size = 'sm' }) => {
  if (!category) {
    return (
      <span style={{
        display: 'inline-flex', alignItems: 'center', gap: 6,
        padding: size === 'sm' ? '2px 8px' : '4px 10px',
        background: 'var(--bg-sunken)',
        borderRadius: 999,
        fontSize: size === 'sm' ? 11 : 12,
        color: 'var(--fg-3)',
        fontWeight: 500,
      }}>Uncategorized</span>
    );
  }
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 6,
      padding: size === 'sm' ? '2px 8px' : '4px 10px',
      background: category.color + '14',
      borderRadius: 999,
      fontSize: size === 'sm' ? 11 : 12,
      color: category.color,
      fontWeight: 600,
    }}>
      <span style={{ width: 6, height: 6, borderRadius: 999, background: category.color }} />
      {category.name}
    </span>
  );
};

const InvFilterChip = ({ active, onClick, children, color, count }) => (
  <button onClick={onClick} style={{
    display: 'inline-flex', alignItems: 'center', gap: 6,
    padding: '5px 11px',
    background: active ? 'var(--fg-1)' : '#FFFFFF',
    color: active ? '#FFFFFF' : 'var(--fg-2)',
    border: '1px solid ' + (active ? 'var(--fg-1)' : 'var(--border-2)'),
    borderRadius: 999,
    fontSize: 12, fontWeight: 500,
    cursor: 'pointer',
  }}>
    {color && <span style={{ width: 7, height: 7, borderRadius: 999, background: color }} />}
    {children}
    {typeof count === 'number' && (
      <span style={{
        fontSize: 11, fontWeight: 600,
        color: active ? 'rgba(255,255,255,0.7)' : 'var(--fg-3)',
        fontFamily: 'var(--font-num)',
      }}>{count}</span>
    )}
  </button>
);

const InvTh = ({ children, sortable, onClick, sort, k, w, align = 'left' }) => {
  const active = sortable && sort && sort.key === k;
  return (
    <th style={{
      textAlign: align,
      padding: '10px 14px',
      fontSize: 11, fontWeight: 600,
      color: active ? 'var(--fg-1)' : 'var(--fg-2)',
      textTransform: 'uppercase', letterSpacing: '0.06em',
      width: w, cursor: sortable ? 'pointer' : 'default',
      userSelect: 'none', whiteSpace: 'nowrap',
    }} onClick={onClick}>
      <span style={{ display: 'inline-flex', alignItems: 'center', gap: 4, justifyContent: align === 'right' ? 'flex-end' : 'flex-start' }}>
        {children}
        {sortable && (
          <span style={{ fontSize: 10, opacity: active ? 1 : 0.3, fontFamily: 'var(--font-num)' }}>
            {active ? (sort.dir === 'asc' ? '↑' : '↓') : '↕'}
          </span>
        )}
      </span>
    </th>
  );
};

// ------------------------------------------------------------------
// Top-level
// ------------------------------------------------------------------
const Inventory = () => {
  const [tab, setTab] = useInv('items');
  const [items, setItems]               = usePersistent(INVENTORY_ITEMS_KEY, seedInvItems);
  const [categories, setCategories]     = usePersistent(INVENTORY_CATEGORIES_KEY, seedInvCategories);
  const [areas, setAreas]               = usePersistent(STORAGE_AREAS_KEY, seedStorageAreas);
  const [vendors, setVendors]           = usePersistent(INVENTORY_VENDORS_KEY, seedVendors);

  return (
    <div className="portal-page-wide" style={{ maxWidth: 1380 }}>
      <div className="portal-page-header" style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', gap: 24, flexWrap: 'wrap' }}>
        <div>
          <h1 className="portal-page-title">Inventory</h1>
          <div className="portal-page-sub">
            Master database of everything you purchase. Used by Truck Order to know what to count and reorder.
          </div>
        </div>
      </div>

      <div style={{
        display: 'flex', gap: 0,
        borderBottom: '1px solid var(--border-2)',
        marginTop: 18, marginBottom: 24,
        overflowX: 'auto',
      }}>
        {[
          { id: 'items',      label: 'Items',         count: items.length },
          { id: 'vendors',    label: 'Vendors',       count: vendors.length },
          { id: 'categories', label: 'Categories',    count: categories.length },
          { id: 'storage',    label: 'Storage Areas', count: areas.length },
        ].map(t => (
          <button key={t.id} onClick={() => setTab(t.id)} style={{
            padding: '10px 16px',
            background: 'transparent',
            border: 'none',
            borderBottom: '2px solid ' + (tab === t.id ? 'var(--fg-1)' : 'transparent'),
            marginBottom: -1,
            color: tab === t.id ? 'var(--fg-1)' : 'var(--fg-2)',
            fontSize: 14, fontWeight: tab === t.id ? 600 : 500,
            cursor: 'pointer',
            display: 'inline-flex', alignItems: 'center', gap: 8, whiteSpace: 'nowrap',
          }}>
            {t.label}
            <span style={{
              fontSize: 11, fontWeight: 600,
              color: 'var(--fg-3)',
              background: 'var(--bg-sunken)',
              padding: '1px 7px', borderRadius: 999,
              fontFamily: 'var(--font-num)',
            }}>{t.count}</span>
          </button>
        ))}
      </div>

      {tab === 'items'      && <InventoryItems  items={items} setItems={setItems} categories={categories} areas={areas} vendors={vendors} />}
      {tab === 'vendors'    && <Vendors          vendors={vendors} setVendors={setVendors} items={items} setItems={setItems} />}
      {tab === 'categories' && <InventoryCategories categories={categories} setCategories={setCategories} items={items} setItems={setItems} />}
      {tab === 'storage'    && <StorageAreas    areas={areas} setAreas={setAreas} items={items} setItems={setItems} />}
    </div>
  );
};

// ------------------------------------------------------------------
// Items — table list
// ------------------------------------------------------------------
const InventoryItems = ({ items, setItems, categories, areas, vendors }) => {
  const [editing, setEditing] = useInv(null);
  const [search, setSearch]   = useInv('');
  const [catFilter, setCatFilter] = useInv('all');
  const [sort, setSort]       = useInv({ key: 'name', dir: 'asc' });

  const catById    = useMemoInv(() => Object.fromEntries(categories.map(c => [c.id, c])), [categories]);
  const areaById   = useMemoInv(() => Object.fromEntries(areas.map(a => [a.id, a])), [areas]);
  const vendorById = useMemoInv(() => Object.fromEntries(vendors.map(v => [v.id, v])), [vendors]);

  const filtered = useMemoInv(() => {
    const q = search.trim().toLowerCase();
    let out = items.filter(i => {
      if (catFilter !== 'all' && i.categoryId !== catFilter) return false;
      if (!q) return true;
      const vendorName = vendorById[i.vendorId]?.name || '';
      const hay = [
        i.name, vendorName, i.vendorSku,
        ...(i.vendorAliases || []),
      ].filter(Boolean).join(' ').toLowerCase();
      return hay.includes(q);
    });

    out = out.map(it => ({
      ...it,
      _cat:    catById[it.categoryId],
      _area:   areaById[it.storageAreaId],
      _vendor: vendorById[it.vendorId],
    }));

    const dir = sort.dir === 'asc' ? 1 : -1;
    out.sort((a, b) => {
      switch (sort.key) {
        case 'name':     return a.name.localeCompare(b.name) * dir;
        case 'vendor':   return ((a._vendor?.name || 'zzz').localeCompare(b._vendor?.name || 'zzz')) * dir;
        case 'category': return ((a._cat?.name || 'zzz').localeCompare(b._cat?.name || 'zzz')) * dir;
        case 'area':     return ((a._area?.name || 'zzz').localeCompare(b._area?.name || 'zzz')) * dir;
        case 'price':    return ((a.currentPrice || 0) - (b.currentPrice || 0)) * dir;
        default: return 0;
      }
    });
    return out;
  }, [items, search, catFilter, sort, catById, areaById]);

  const addItem = () => {
    setEditing({
      id: 'new',
      name: '',
      categoryId: categories[0]?.id || null,
      storageAreaId: areas[0]?.id || null,
      vendorId: vendors[0]?.id || null,
      vendorAliases: [],
      vendorSku: '',
      purchaseUnit: 'case',
      storageUnit: '',
      conversion: 1,
      currentPrice: 0,
      priceHistory: [],
      notes: '',
    });
  };

  const saveItem = (it) => {
    if (it.id === 'new') {
      const newId = 'inv-' + Date.now().toString(36);
      setItems(prev => [...prev, { ...it, id: newId }]);
    } else {
      setItems(prev => prev.map(p => p.id === it.id ? it : p));
    }
    setEditing(null);
  };

  const deleteItem = (id) => {
    if (!confirm('Delete this item? This cannot be undone.')) return;
    setItems(prev => prev.filter(p => p.id !== id));
    setEditing(null);
  };

  const catCounts = useMemoInv(() => {
    const map = { all: items.length };
    categories.forEach(c => map[c.id] = 0);
    items.forEach(i => { if (map.hasOwnProperty(i.categoryId)) map[i.categoryId] += 1; });
    return map;
  }, [items, categories]);

  const setSortKey = (key) => {
    setSort(s => s.key === key ? { key, dir: s.dir === 'asc' ? 'desc' : 'asc' } : { key, dir: 'asc' });
  };

  return (
    <div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 12, flexWrap: 'wrap' }}>
        <div style={{
          flex: '0 0 auto', width: 300,
          display: 'flex', alignItems: 'center', gap: 8,
          padding: '8px 12px',
          background: 'var(--bg-sunken)',
          borderRadius: 8,
        }}>
          <PIcon name="search" size={14} color="var(--fg-3)" />
          <input value={search} onChange={e => setSearch(e.target.value)}
            placeholder="Search name, vendor, SKU, alias"
            style={{ flex: 1, border: 'none', background: 'transparent', outline: 'none', fontSize: 13 }} />
        </div>
        <div style={{ flex: 1 }} />
        <PBtn variant="primary" onClick={addItem}>
          <PIcon name="plus" size={14} />
          New item
        </PBtn>
      </div>

      <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', marginBottom: 14 }}>
        <InvFilterChip active={catFilter === 'all'} onClick={() => setCatFilter('all')} count={catCounts.all}>All</InvFilterChip>
        {categories.map(c => (
          <InvFilterChip key={c.id} active={catFilter === c.id} onClick={() => setCatFilter(c.id)} color={c.color} count={catCounts[c.id] || 0}>
            {c.name}
          </InvFilterChip>
        ))}
      </div>

      {filtered.length === 0 ? (
        <div className="portal-empty">
          <div style={{ fontSize: 14, color: 'var(--fg-2)' }}>
            {search || catFilter !== 'all' ? 'No items match.' : 'No inventory items yet.'}
          </div>
          {!search && catFilter === 'all' && (
            <PBtn variant="ghost" onClick={addItem} style={{ marginTop: 10 }}>
              <PIcon name="plus" size={14} />
              Add the first one
            </PBtn>
          )}
        </div>
      ) : (
        <div style={{
          background: '#FFFFFF',
          border: '1px solid var(--border-2)',
          borderRadius: 10,
          overflow: 'hidden',
        }}>
          <table style={{ borderCollapse: 'collapse', width: '100%' }}>
            <thead>
              <tr style={{ borderBottom: '1px solid var(--border-2)', background: '#FAFAF9' }}>
                <InvTh sortable onClick={() => setSortKey('name')}     sort={sort} k="name">Item</InvTh>
                <InvTh sortable onClick={() => setSortKey('vendor')}   sort={sort} k="vendor" w={170}>Vendor</InvTh>
                <InvTh sortable onClick={() => setSortKey('category')} sort={sort} k="category" w={130}>Category</InvTh>
                <InvTh sortable onClick={() => setSortKey('area')}     sort={sort} k="area" w={140}>Storage</InvTh>
                <InvTh                                                                          w={220}>Conversion</InvTh>
                <InvTh sortable onClick={() => setSortKey('price')}    sort={sort} k="price" w={100} align="right">Price</InvTh>
                <InvTh w={36} />
              </tr>
            </thead>
            <tbody>
              {filtered.map(item => (
                <tr key={item.id}
                  onClick={() => setEditing(item)}
                  style={{ borderBottom: '1px solid var(--border-2)', cursor: 'pointer' }}
                  onMouseEnter={e => e.currentTarget.style.background = '#FBFAF8'}
                  onMouseLeave={e => e.currentTarget.style.background = 'transparent'}
                >
                  <td style={{ padding: '12px 14px' }}>
                    <div style={{ fontSize: 13.5, fontWeight: 600, color: 'var(--fg-1)', lineHeight: 1.3 }}>
                      {item.name || <span style={{ color: 'var(--fg-3)', fontStyle: 'italic', fontWeight: 500 }}>Untitled</span>}
                    </div>
                    {item.vendorSku && (
                      <div style={{ fontSize: 11, color: 'var(--fg-3)', marginTop: 2, fontFamily: 'var(--font-num)' }}>
                        SKU {item.vendorSku}
                      </div>
                    )}
                  </td>
                  <td style={{ padding: '12px 14px' }}>
                    <div style={{ fontSize: 12.5, color: 'var(--fg-1)', fontWeight: 500 }}>
                      {item._vendor?.name || <span style={{ color: 'var(--fg-3)' }}>—</span>}
                    </div>
                    {(item.vendorAliases || []).length > 0 && (
                      <div style={{ fontSize: 11, color: 'var(--fg-3)', marginTop: 2, lineHeight: 1.3, maxWidth: 160, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                        {(item.vendorAliases || []).join(' · ')}
                      </div>
                    )}
                  </td>
                  <td style={{ padding: '12px 14px' }}>
                    <InvCategoryPill category={item._cat} />
                  </td>
                  <td style={{ padding: '12px 14px', fontSize: 12.5, color: 'var(--fg-2)' }}>
                    {item._area?.name || <span style={{ color: 'var(--fg-3)' }}>—</span>}
                  </td>
                  <td style={{ padding: '12px 14px' }}>
                    <ConversionDisplay item={item} />
                  </td>
                  <td style={{ padding: '12px 14px', textAlign: 'right' }}>
                    <div style={{ fontSize: 13.5, fontWeight: 600, color: 'var(--fg-1)', fontFamily: 'var(--font-num)' }}>
                      {fmtMoney(item.currentPrice)}
                    </div>
                    {(item.priceHistory || []).length > 1 && (
                      <PriceTrend history={item.priceHistory} />
                    )}
                  </td>
                  <td style={{ padding: '12px 6px', textAlign: 'center' }}>
                    <PIcon name="chevronR" size={14} color="var(--fg-3)" />
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}

      {editing && (
        <InventoryEditor
          item={editing}
          categories={categories}
          areas={areas}
          vendors={vendors}
          onCancel={() => setEditing(null)}
          onSave={saveItem}
          onDelete={editing.id !== 'new' ? () => deleteItem(editing.id) : null}
        />
      )}
    </div>
  );
};

// Inline conversion display: "1 case → 2 lg prep pan"
const ConversionDisplay = ({ item }) => {
  const purchase = item.purchaseUnit || '—';
  const storage  = item.storageUnit  || '—';
  const ratio    = item.conversion;

  if (!ratio || (storage === purchase)) {
    return (
      <span style={{ fontSize: 12.5, color: 'var(--fg-2)' }}>
        <span style={{ fontWeight: 600, fontFamily: 'var(--font-num)' }}>1</span> {purchase}
      </span>
    );
  }

  return (
    <span style={{ fontSize: 12.5, color: 'var(--fg-2)', display: 'inline-flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
      <span><span style={{ fontWeight: 600, fontFamily: 'var(--font-num)' }}>1</span> {purchase}</span>
      <span style={{ color: 'var(--fg-3)' }}>→</span>
      <span><span style={{ fontWeight: 600, fontFamily: 'var(--font-num)' }}>{ratio}</span> {storage}</span>
    </span>
  );
};

// Tiny arrow indicator for price movement
const PriceTrend = ({ history }) => {
  const sorted = [...history].sort((a, b) => b.date.localeCompare(a.date));
  if (sorted.length < 2) return null;
  const cur = sorted[0].price;
  const prev = sorted[1].price;
  const diff = cur - prev;
  if (Math.abs(diff) < 0.01) return null;
  const up = diff > 0;
  const pct = Math.abs((diff / prev) * 100);
  return (
    <div style={{ fontSize: 10.5, color: up ? '#B45309' : '#2D6A2A', fontFamily: 'var(--font-num)', marginTop: 1, fontWeight: 500 }}>
      {up ? '↑' : '↓'} {pct.toFixed(1)}% vs prev
    </div>
  );
};

// ------------------------------------------------------------------
// Editor modal
// ------------------------------------------------------------------
const InventoryEditor = ({ item, categories, areas, vendors, onCancel, onSave, onDelete }) => {
  const [draft, setDraft] = useInv(item);
  const [aliasInput, setAliasInput] = useInv('');
  const update = (patch) => setDraft(d => ({ ...d, ...patch }));

  const addAlias = () => {
    const v = aliasInput.trim();
    if (!v) return;
    update({ vendorAliases: [...(draft.vendorAliases || []), v] });
    setAliasInput('');
  };
  const removeAlias = (i) => {
    update({ vendorAliases: draft.vendorAliases.filter((_, j) => j !== i) });
  };

  const onSubmit = () => {
    // If currentPrice differs from latest history entry, append a history row.
    let history = draft.priceHistory || [];
    const sorted = [...history].sort((a, b) => b.date.localeCompare(a.date));
    const latest = sorted[0];
    const cur = Number(draft.currentPrice);
    if (!latest || Math.abs(latest.price - cur) > 0.001) {
      history = [{ date: todayIso(), price: cur }, ...history];
    }
    onSave({ ...draft, currentPrice: cur, conversion: Number(draft.conversion) || 1, priceHistory: history });
  };

  const sortedHistory = [...(draft.priceHistory || [])].sort((a, b) => b.date.localeCompare(a.date));

  return (
    <PModal open={true} onClose={onCancel} width={820}>
      <div style={{ display: 'flex', flexDirection: 'column', maxHeight: '88vh' }}>
        <div style={{
          padding: '18px 24px',
          borderBottom: '1px solid var(--border-2)',
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          flexShrink: 0,
        }}>
          <div style={{ fontSize: 16, fontWeight: 600, letterSpacing: '-0.01em' }}>
            {item.id === 'new' ? 'New inventory item' : 'Edit inventory item'}
          </div>
          <button onClick={onCancel} style={{
            width: 28, height: 28, borderRadius: 6, background: 'transparent', border: 'none',
            display: 'inline-flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer',
          }}>
            <PIcon name="x" size={16} color="var(--fg-2)" />
          </button>
        </div>

        <div style={{ flex: 1, overflow: 'auto', padding: '20px 24px' }}>
          {/* Basics */}
          <div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr 1fr', gap: 14 }}>
            <InvField label="Display name">
              <input value={draft.name} onChange={e => update({ name: e.target.value })}
                placeholder="e.g. Broccoli" className="portal-input" autoFocus />
            </InvField>
            <InvField label="Category">
              <select value={draft.categoryId || ''} onChange={e => update({ categoryId: e.target.value || null })} className="portal-input">
                <option value="">Uncategorized</option>
                {categories.map(c => <option key={c.id} value={c.id}>{c.name}</option>)}
              </select>
            </InvField>
            <InvField label="Storage area">
              <select value={draft.storageAreaId || ''} onChange={e => update({ storageAreaId: e.target.value || null })} className="portal-input">
                <option value="">—</option>
                {areas.map(a => <option key={a.id} value={a.id}>{a.name}</option>)}
              </select>
            </InvField>
          </div>

          {/* Vendor */}
          <div style={{
            background: '#FBFAF8',
            borderRadius: 10,
            padding: 16,
            marginTop: 4, marginBottom: 16,
            border: '1px solid var(--border-2)',
          }}>
            <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--fg-2)', textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: 12 }}>Vendor</div>
            <div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr', gap: 14 }}>
              <InvField label="Vendor" subtitle={vendors.length === 0 ? 'Add vendors in the Vendors tab first.' : null}>
                <select value={draft.vendorId || ''} onChange={e => update({ vendorId: e.target.value || null })}
                  className="portal-input">
                  <option value="">— select vendor —</option>
                  {vendors.map(v => <option key={v.id} value={v.id}>{v.name}</option>)}
                </select>
              </InvField>
              <InvField label="Vendor SKU">
                <input value={draft.vendorSku} onChange={e => update({ vendorSku: e.target.value })}
                  placeholder="e.g. 4561234" className="portal-input"
                  style={{ fontFamily: 'var(--font-num)' }} />
              </InvField>
            </div>
            <InvField label="Vendor aliases" subtitle="Any names the vendor uses on invoices — English, 中文, abbreviations. Used for searching.">
              <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginBottom: 8 }}>
                {(draft.vendorAliases || []).map((a, i) => (
                  <span key={i} style={{
                    display: 'inline-flex', alignItems: 'center', gap: 6,
                    padding: '4px 6px 4px 10px',
                    background: '#FFFFFF',
                    border: '1px solid var(--border-2)',
                    borderRadius: 999,
                    fontSize: 12, color: 'var(--fg-1)',
                  }}>
                    {a}
                    <button onClick={() => removeAlias(i)} style={{
                      width: 18, height: 18, borderRadius: 999,
                      background: 'transparent', border: 'none', cursor: 'pointer',
                      display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                    }}>
                      <PIcon name="x" size={11} color="var(--fg-3)" />
                    </button>
                  </span>
                ))}
              </div>
              <div style={{ display: 'flex', gap: 8 }}>
                <input value={aliasInput} onChange={e => setAliasInput(e.target.value)}
                  onKeyDown={e => { if (e.key === 'Enter') { e.preventDefault(); addAlias(); } }}
                  placeholder="Type an alias and press Enter"
                  className="portal-input" style={{ flex: 1 }} />
                <PBtn variant="ghost" onClick={addAlias} disabled={!aliasInput.trim()}>
                  <PIcon name="plus" size={13} /> Add
                </PBtn>
              </div>
            </InvField>
          </div>

          {/* Storage / units */}
          <div style={{
            background: '#FBFAF8',
            borderRadius: 10,
            padding: 16,
            marginBottom: 16,
            border: '1px solid var(--border-2)',
          }}>
            <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--fg-2)', textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: 4 }}>Storage calculation</div>
            <div style={{ fontSize: 12, color: 'var(--fg-3)', lineHeight: 1.4, marginBottom: 12 }}>
              How a purchase unit converts into what you actually store on the line.
            </div>

            <div style={{ display: 'grid', gridTemplateColumns: '1fr auto 100px auto 1fr', gap: 10, alignItems: 'end' }}>
              <InvField label="Purchase unit">
                <input value={draft.purchaseUnit} onChange={e => update({ purchaseUnit: e.target.value })}
                  placeholder="case" className="portal-input" />
              </InvField>
              <div style={{ paddingBottom: 22, fontSize: 18, color: 'var(--fg-3)', fontWeight: 300 }}>=</div>
              <InvField label="Quantity">
                <input type="number" step="0.01" min="0"
                  value={draft.conversion}
                  onChange={e => update({ conversion: e.target.value })}
                  className="portal-input"
                  style={{ textAlign: 'center', fontFamily: 'var(--font-num)', fontWeight: 600 }} />
              </InvField>
              <div style={{ paddingBottom: 22, fontSize: 18, color: 'var(--fg-3)', fontWeight: 300 }}>×</div>
              <InvField label="Storage unit">
                <input value={draft.storageUnit} onChange={e => update({ storageUnit: e.target.value })}
                  placeholder="lg prep pan" className="portal-input" />
              </InvField>
            </div>

            {draft.purchaseUnit && draft.storageUnit && draft.conversion > 0 && (
              <div style={{
                marginTop: 4,
                padding: '8px 12px',
                background: '#FFFFFF',
                border: '1px solid var(--border-2)',
                borderRadius: 6,
                fontSize: 12.5, color: 'var(--fg-2)',
                fontStyle: 'italic',
              }}>
                Preview:&nbsp;
                <span style={{ fontFamily: 'var(--font-num)', fontWeight: 600, color: 'var(--fg-1)', fontStyle: 'normal' }}>1</span> {draft.purchaseUnit}
                &nbsp;→&nbsp;
                <span style={{ fontFamily: 'var(--font-num)', fontWeight: 600, color: 'var(--fg-1)', fontStyle: 'normal' }}>{draft.conversion}</span> {draft.storageUnit}
                {Number(draft.conversion) !== 1 && Number(draft.conversion) !== 0 && (
                  <span style={{ color: 'var(--fg-3)', marginLeft: 8 }}>
                    ({(1 / Number(draft.conversion)).toFixed(2)} {draft.purchaseUnit} per {draft.storageUnit})
                  </span>
                )}
              </div>
            )}
          </div>

          {/* Price */}
          <div style={{ display: 'grid', gridTemplateColumns: '180px 1fr', gap: 14 }}>
            <InvField label="Current price">
              <div style={{ position: 'relative' }}>
                <span style={{ position: 'absolute', left: 12, top: '50%', transform: 'translateY(-50%)', fontSize: 13, color: 'var(--fg-3)' }}>$</span>
                <input type="number" step="0.01" min="0"
                  value={draft.currentPrice}
                  onChange={e => update({ currentPrice: e.target.value })}
                  className="portal-input"
                  style={{ paddingLeft: 24, fontFamily: 'var(--font-num)', fontWeight: 600 }} />
              </div>
            </InvField>
            <InvField label="Price history">
              <PriceHistoryList history={sortedHistory} currentPrice={Number(draft.currentPrice)} />
            </InvField>
          </div>

          <InvField label="Notes">
            <textarea value={draft.notes} onChange={e => update({ notes: e.target.value })}
              rows={3} className="portal-input"
              placeholder="e.g. Order Mon & Thu. Inspect before accepting." />
          </InvField>
        </div>

        <div style={{
          padding: '14px 24px',
          borderTop: '1px solid var(--border-2)',
          display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10,
          flexShrink: 0,
          background: '#FFFFFF',
        }}>
          {onDelete ? (
            <PBtn variant="ghost" onClick={onDelete} style={{ color: 'var(--danger)' }}>
              <PIcon name="trash" size={13} /> Delete
            </PBtn>
          ) : <div />}
          <div style={{ display: 'flex', gap: 8 }}>
            <PBtn variant="ghost" onClick={onCancel}>Cancel</PBtn>
            <PBtn variant="primary" onClick={onSubmit} disabled={!draft.name.trim()}>
              {item.id === 'new' ? 'Create' : 'Save'}
            </PBtn>
          </div>
        </div>
      </div>
    </PModal>
  );
};

const PriceHistoryList = ({ history, currentPrice }) => {
  // Find the max for the inline bar viz.
  const all = [...history.map(h => h.price), currentPrice].filter(p => !isNaN(p));
  const max = Math.max(...all, 1);

  // Latest entry's price doesn't match currentPrice if the user has typed a new value.
  const latest = history[0];
  const showPending = latest && Math.abs(latest.price - currentPrice) > 0.001;

  if (history.length === 0 && !showPending) {
    return <div style={{ fontSize: 12.5, color: 'var(--fg-3)', padding: '8px 12px', background: 'var(--bg-sunken)', borderRadius: 6 }}>No history yet. First save creates an entry.</div>;
  }

  return (
    <div style={{
      maxHeight: 140, overflow: 'auto',
      border: '1px solid var(--border-2)',
      borderRadius: 6,
      background: '#FFFFFF',
    }}>
      {showPending && (
        <div style={{
          display: 'flex', alignItems: 'center', gap: 10,
          padding: '8px 12px',
          borderBottom: '1px solid var(--border-2)',
          background: '#FFF8EC',
          fontSize: 12.5,
        }}>
          <span style={{
            fontSize: 10, fontWeight: 700, color: '#92400E',
            background: '#FDE68A', padding: '1px 6px', borderRadius: 4,
            letterSpacing: '0.05em',
          }}>PENDING</span>
          <span style={{ color: 'var(--fg-2)' }}>Today</span>
          <span style={{ flex: 1 }} />
          <span style={{ fontFamily: 'var(--font-num)', fontWeight: 600 }}>{fmtMoney(currentPrice)}</span>
        </div>
      )}
      {history.map((h, i) => {
        const pct = max ? Math.round((h.price / max) * 100) : 0;
        return (
          <div key={i} style={{
            display: 'flex', alignItems: 'center', gap: 10,
            padding: '8px 12px',
            borderBottom: i === history.length - 1 ? 'none' : '1px solid var(--border-2)',
            fontSize: 12.5,
            background: i === 0 && !showPending ? '#FBFAF8' : '#FFFFFF',
          }}>
            <span style={{ color: 'var(--fg-2)', fontFamily: 'var(--font-num)', minWidth: 90 }}>{fmtDateShort(h.date)}</span>
            <div style={{ flex: 1, height: 4, background: 'var(--bg-sunken)', borderRadius: 999, overflow: 'hidden' }}>
              <div style={{ width: pct + '%', height: '100%', background: i === 0 ? 'var(--fg-1)' : '#94A3B8' }} />
            </div>
            <span style={{ fontFamily: 'var(--font-num)', fontWeight: i === 0 ? 600 : 500, minWidth: 60, textAlign: 'right' }}>
              {fmtMoney(h.price)}
            </span>
          </div>
        );
      })}
    </div>
  );
};

// ------------------------------------------------------------------
// Vendors CRUD
// ------------------------------------------------------------------
const Vendors = ({ vendors, setVendors, items, setItems }) => {
  const [draft, setDraft] = useInv(null);

  const addVendor = () => setDraft({ id: 'new', name: '', notes: '' });

  const saveVendor = () => {
    if (!draft.name.trim()) return;
    if (draft.id === 'new') {
      const newId = 'v-' + Date.now().toString(36);
      setVendors(prev => [...prev, { ...draft, id: newId }]);
    } else {
      setVendors(prev => prev.map(v => v.id === draft.id ? draft : v));
    }
    setDraft(null);
  };

  const deleteVendor = (id) => {
    const count = items.filter(i => i.vendorId === id).length;
    if (count > 0) {
      if (!confirm(`This vendor is used by ${count} item${count === 1 ? '' : 's'}. They will be left without a vendor. Continue?`)) return;
      setItems(prev => prev.map(i => i.vendorId === id ? { ...i, vendorId: null } : i));
    }
    setVendors(prev => prev.filter(v => v.id !== id));
    if (draft && draft.id === id) setDraft(null);
  };

  const itemCount = (id) => items.filter(i => i.vendorId === id).length;

  return (
    <div style={{ maxWidth: 720 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 14 }}>
        <div style={{ fontSize: 13, color: 'var(--fg-2)', lineHeight: 1.5 }}>
          Suppliers you order from. Each inventory item is tied to one vendor.
        </div>
        <PBtn variant="primary" onClick={addVendor}>
          <PIcon name="plus" size={14} />
          New vendor
        </PBtn>
      </div>

      <CrudList
        rows={vendors}
        emptyText="No vendors yet."
        renderRow={(v) => (
          <>
            <span style={{
              width: 32, height: 32, borderRadius: 8,
              background: '#FFFFFF',
              border: '1px solid var(--border-2)',
              display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
              flexShrink: 0,
              fontSize: 13, fontWeight: 700, color: 'var(--fg-2)',
              letterSpacing: '-0.01em',
            }}>
              {v.name.slice(0, 2).toUpperCase()}
            </span>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontSize: 14, fontWeight: 500, color: 'var(--fg-1)' }}>{v.name}</div>
              {v.notes && (
                <div style={{ fontSize: 11.5, color: 'var(--fg-3)', marginTop: 2, lineHeight: 1.35, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: 480 }}>
                  {v.notes}
                </div>
              )}
            </div>
            <div style={{ fontSize: 12, color: 'var(--fg-3)', fontFamily: 'var(--font-num)' }}>
              {itemCount(v.id)} item{itemCount(v.id) === 1 ? '' : 's'}
            </div>
          </>
        )}
        onEdit={(v) => setDraft({ ...v })}
        onDelete={(v) => deleteVendor(v.id)}
      />

      {draft && (
        <PModal open={true} onClose={() => setDraft(null)} width={460}
          title={draft.id === 'new' ? 'New vendor' : 'Edit vendor'}>
          <div style={{ padding: '8px 24px 20px' }}>
            <InvField label="Name">
              <input value={draft.name} onChange={e => setDraft(d => ({ ...d, name: e.target.value }))}
                placeholder="e.g. Sysco" className="portal-input" autoFocus />
            </InvField>
            <InvField label="Notes" subtitle="Delivery days, ordering quirks, contact info.">
              <textarea value={draft.notes || ''} onChange={e => setDraft(d => ({ ...d, notes: e.target.value }))}
                rows={3} className="portal-input"
                placeholder="e.g. Tue/Fri delivery. Min order $200." />
            </InvField>
            <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 10 }}>
              <PBtn variant="ghost" onClick={() => setDraft(null)}>Cancel</PBtn>
              <PBtn variant="primary" onClick={saveVendor} disabled={!draft.name.trim()}>
                {draft.id === 'new' ? 'Create' : 'Save'}
              </PBtn>
            </div>
          </div>
        </PModal>
      )}
    </div>
  );
};

// ------------------------------------------------------------------
// Categories CRUD (mirrors Training categories pattern)
// ------------------------------------------------------------------
const INV_PALETTE = ['#059669', '#0891B2', '#CA8A04', '#BE123C', '#475569', '#2563EB', '#EA580C', '#7C3AED', '#DB2777'];

const InventoryCategories = ({ categories, setCategories, items, setItems }) => {
  const [draft, setDraft] = useInv(null);

  const addCategory = () => setDraft({ id: 'new', name: '', color: INV_PALETTE[categories.length % INV_PALETTE.length] });

  const saveCategory = () => {
    if (!draft.name.trim()) return;
    if (draft.id === 'new') {
      const newId = 'ic-' + Date.now().toString(36);
      setCategories(prev => [...prev, { ...draft, id: newId }]);
    } else {
      setCategories(prev => prev.map(c => c.id === draft.id ? draft : c));
    }
    setDraft(null);
  };

  const deleteCategory = (id) => {
    const count = items.filter(i => i.categoryId === id).length;
    if (count > 0) {
      if (!confirm(`This category has ${count} item${count === 1 ? '' : 's'}. They will be set to uncategorized. Continue?`)) return;
      setItems(prev => prev.map(i => i.categoryId === id ? { ...i, categoryId: null } : i));
    }
    setCategories(prev => prev.filter(c => c.id !== id));
    if (draft && draft.id === id) setDraft(null);
  };

  const itemCount = (id) => items.filter(i => i.categoryId === id).length;

  return (
    <div style={{ maxWidth: 720 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 14 }}>
        <div style={{ fontSize: 13, color: 'var(--fg-2)', lineHeight: 1.5 }}>
          Categories group purchases by type — Grocery, Beverage, Paper, Chemicals, etc.
        </div>
        <PBtn variant="primary" onClick={addCategory}>
          <PIcon name="plus" size={14} />
          New category
        </PBtn>
      </div>

      <CrudList
        rows={categories}
        emptyText="No categories yet."
        renderRow={(c) => (
          <>
            <span style={{ width: 14, height: 14, borderRadius: 4, background: c.color, flexShrink: 0 }} />
            <div style={{ flex: 1, fontSize: 14, fontWeight: 500, color: 'var(--fg-1)' }}>{c.name}</div>
            <div style={{ fontSize: 12, color: 'var(--fg-3)', fontFamily: 'var(--font-num)' }}>
              {itemCount(c.id)} item{itemCount(c.id) === 1 ? '' : 's'}
            </div>
          </>
        )}
        onEdit={(c) => setDraft({ ...c })}
        onDelete={(c) => deleteCategory(c.id)}
      />

      {draft && (
        <PModal open={true} onClose={() => setDraft(null)} width={420}
          title={draft.id === 'new' ? 'New category' : 'Edit category'}>
          <div style={{ padding: '8px 24px 20px' }}>
            <InvField label="Name">
              <input value={draft.name} onChange={e => setDraft(d => ({ ...d, name: e.target.value }))}
                placeholder="e.g. Grocery" className="portal-input" autoFocus />
            </InvField>
            <InvField label="Color">
              <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
                {INV_PALETTE.map(col => (
                  <button key={col} onClick={() => setDraft(d => ({ ...d, color: col }))} style={{
                    width: 30, height: 30, borderRadius: 8,
                    background: col, border: '2px solid ' + (draft.color === col ? 'var(--fg-1)' : 'transparent'),
                    cursor: 'pointer',
                  }} />
                ))}
              </div>
            </InvField>
            <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 10 }}>
              <PBtn variant="ghost" onClick={() => setDraft(null)}>Cancel</PBtn>
              <PBtn variant="primary" onClick={saveCategory} disabled={!draft.name.trim()}>
                {draft.id === 'new' ? 'Create' : 'Save'}
              </PBtn>
            </div>
          </div>
        </PModal>
      )}
    </div>
  );
};

// ------------------------------------------------------------------
// Storage Areas CRUD
// ------------------------------------------------------------------
const StorageAreas = ({ areas, setAreas, items, setItems }) => {
  const [draft, setDraft] = useInv(null);

  const addArea = () => setDraft({ id: 'new', name: '' });

  const saveArea = () => {
    if (!draft.name.trim()) return;
    if (draft.id === 'new') {
      const newId = 'sa-' + Date.now().toString(36);
      setAreas(prev => [...prev, { ...draft, id: newId }]);
    } else {
      setAreas(prev => prev.map(a => a.id === draft.id ? draft : a));
    }
    setDraft(null);
  };

  const deleteArea = (id) => {
    const count = items.filter(i => i.storageAreaId === id).length;
    if (count > 0) {
      if (!confirm(`This area has ${count} item${count === 1 ? '' : 's'} stored in it. They will be left without a storage area. Continue?`)) return;
      setItems(prev => prev.map(i => i.storageAreaId === id ? { ...i, storageAreaId: null } : i));
    }
    setAreas(prev => prev.filter(a => a.id !== id));
    if (draft && draft.id === id) setDraft(null);
  };

  const itemCount = (id) => items.filter(i => i.storageAreaId === id).length;

  return (
    <div style={{ maxWidth: 720 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 14 }}>
        <div style={{ fontSize: 13, color: 'var(--fg-2)', lineHeight: 1.5 }}>
          Physical locations where inventory is kept. Helps staff find things during count.
        </div>
        <PBtn variant="primary" onClick={addArea}>
          <PIcon name="plus" size={14} />
          New area
        </PBtn>
      </div>

      <CrudList
        rows={areas}
        emptyText="No storage areas yet."
        renderRow={(a) => (
          <>
            <span style={{
              width: 28, height: 28, borderRadius: 6,
              background: 'var(--bg-sunken)',
              display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
              flexShrink: 0,
            }}>
              <PIcon name="box" size={14} color="var(--fg-2)" />
            </span>
            <div style={{ flex: 1, fontSize: 14, fontWeight: 500, color: 'var(--fg-1)' }}>{a.name}</div>
            <div style={{ fontSize: 12, color: 'var(--fg-3)', fontFamily: 'var(--font-num)' }}>
              {itemCount(a.id)} item{itemCount(a.id) === 1 ? '' : 's'}
            </div>
          </>
        )}
        onEdit={(a) => setDraft({ ...a })}
        onDelete={(a) => deleteArea(a.id)}
      />

      {draft && (
        <PModal open={true} onClose={() => setDraft(null)} width={420}
          title={draft.id === 'new' ? 'New storage area' : 'Edit storage area'}>
          <div style={{ padding: '8px 24px 20px' }}>
            <InvField label="Name">
              <input value={draft.name} onChange={e => setDraft(d => ({ ...d, name: e.target.value }))}
                placeholder="e.g. Walk-in Cooler" className="portal-input" autoFocus />
            </InvField>
            <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 10 }}>
              <PBtn variant="ghost" onClick={() => setDraft(null)}>Cancel</PBtn>
              <PBtn variant="primary" onClick={saveArea} disabled={!draft.name.trim()}>
                {draft.id === 'new' ? 'Create' : 'Save'}
              </PBtn>
            </div>
          </div>
        </PModal>
      )}
    </div>
  );
};

// Reusable list-with-row-actions used by both CRUD pages
const CrudList = ({ rows, renderRow, onEdit, onDelete, emptyText }) => (
  <div style={{
    background: '#FFFFFF',
    border: '1px solid var(--border-2)',
    borderRadius: 10,
    overflow: 'hidden',
  }}>
    {rows.length === 0 ? (
      <div style={{ padding: 24, textAlign: 'center', fontSize: 13, color: 'var(--fg-3)' }}>{emptyText}</div>
    ) : rows.map((row, i) => (
      <div key={row.id} style={{
        display: 'flex', alignItems: 'center', gap: 12,
        padding: '12px 16px',
        borderTop: i === 0 ? 'none' : '1px solid var(--border-2)',
      }}>
        {renderRow(row)}
        <div style={{ display: 'flex', gap: 4 }}>
          <button onClick={() => onEdit(row)} style={{
            width: 28, height: 28, borderRadius: 6, background: 'transparent', border: 'none', cursor: 'pointer',
            display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
          }}>
            <PIcon name="edit" size={13} color="var(--fg-2)" />
          </button>
          <button onClick={() => onDelete(row)} style={{
            width: 28, height: 28, borderRadius: 6, background: 'transparent', border: 'none', cursor: 'pointer',
            display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
          }}>
            <PIcon name="trash" size={13} color="var(--fg-3)" />
          </button>
        </div>
      </div>
    ))}
  </div>
);

Object.assign(window, { Inventory });
