// products.jsx — PRODUCTS & SERVICES (Sản phẩm & Dịch vụ) module + ProductStore + widget.
// Catalog of products/services, price history over time, per-product transaction history,
// and one-click "create quote from product" with latest-price autofill.
const SP_APP = APPS.find(a => a.id === 'products');

function prCatLabel(id, lang){ const list = (window.CatalogData && window.CatalogData.productCats) || PRODUCT_CATEGORIES; const c = list.find(x => x.id === id); return c ? c[lang] || c.vi : id; }
function prSlug(s){ return (s || '').normalize('NFD').replace(/[̀-ͯ]/g, '').replace(/đ/g, 'd').replace(/Đ/g, 'D').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 24) || 'nhom'; }
// Nhóm SP: chọn từ danh mục hoặc thêm trực tiếp nếu chưa có (lưu vào catalog dùng chung).
function CatPickAdd({ value, onChange }){
  const p = usePortal();
  const { lang } = useLang();
  const toast = useToast();
  const cats = p.catalogs.productCats || [];
  const [adding, setAdding] = React.useState(false);
  const [d, setD] = React.useState('');
  const add = () => {
    const vi = d.trim(); if (!vi){ toast(lang === 'vi' ? 'Nhập tên nhóm' : 'Enter group name', 'error'); return; }
    const ex = cats.find(c => (c.vi || '').toLowerCase() === vi.toLowerCase());
    if (ex){ onChange(ex.id); }
    else { let id = prSlug(vi); if (cats.some(c => c.id === id)) id = id + '-' + (cats.length + 1); p.addCatalogItem('productCats', { id, vi, en:vi }); onChange(id); }
    toast(lang === 'vi' ? 'Đã thêm nhóm' : 'Group added', 'success'); setD(''); setAdding(false);
  };
  if (adding) return (
    <div className="flex g8"><input className="input" autoFocus value={d} onChange={e => setD(e.target.value)} placeholder={lang === 'vi' ? 'Tên nhóm mới…' : 'New group…'} onKeyDown={e => { if (e.key === 'Enter') add(); }} />
      <button className="btn btn-primary btn-icon" onClick={add}><i className="bi bi-check-lg"></i></button>
      <button className="btn btn-icon" onClick={() => setAdding(false)}><i className="bi bi-x-lg"></i></button></div>
  );
  return (
    <div className="flex g8"><select className="select" value={value} onChange={e => onChange(e.target.value)}>{cats.map(c => <option key={c.id} value={c.id}>{c[lang] || c.vi}</option>)}</select>
      <button className="btn btn-icon" title={lang === 'vi' ? 'Thêm nhóm mới' : 'Add group'} onClick={() => setAdding(true)}><i className="bi bi-plus-lg"></i></button></div>
  );
}
// Đơn vị tính: chọn từ danh mục (Cài đặt) hoặc bấm + để thêm ĐVT mới (lưu vào danh mục dùng chung).
function UnitPickAdd({ value, onChange }){
  const p = usePortal();
  const { lang } = useLang();
  const toast = useToast();
  const units = p.catalogs.units || [];
  const [adding, setAdding] = React.useState(false);
  const [d, setD] = React.useState('');
  const add = () => {
    const u = d.trim(); if (!u){ toast(lang === 'vi' ? 'Nhập đơn vị tính' : 'Enter a unit', 'error'); return; }
    if (!units.some(x => (x || '').toLowerCase() === u.toLowerCase())) p.addCatalogItem('units', u);
    onChange(u); toast(lang === 'vi' ? 'Đã thêm đơn vị tính' : 'Unit added', 'success'); setD(''); setAdding(false);
  };
  if (adding) return (
    <div className="flex g8"><input className="input" autoFocus value={d} onChange={e => setD(e.target.value)} placeholder={lang === 'vi' ? 'ĐVT mới…' : 'New unit…'} onKeyDown={e => { if (e.key === 'Enter') add(); }} />
      <button className="btn btn-primary btn-icon" onClick={add}><i className="bi bi-check-lg"></i></button>
      <button className="btn btn-icon" onClick={() => setAdding(false)}><i className="bi bi-x-lg"></i></button></div>
  );
  return (
    <div className="flex g8"><select className="select" value={value} onChange={e => onChange(e.target.value)}>
        <option value="">{lang === 'vi' ? '— Chọn đơn vị —' : '— Select unit —'}</option>
        {value && !units.includes(value) && <option value={value}>{value}</option>}
        {units.map(u => <option key={u} value={u}>{u}</option>)}
      </select>
      <button className="btn btn-icon" title={lang === 'vi' ? 'Thêm ĐVT mới' : 'Add unit'} onClick={() => setAdding(true)}><i className="bi bi-plus-lg"></i></button></div>
  );
}
// icon cho từng nhóm sản phẩm/dịch vụ (bộ lọc chip)
const PR_CAT_ICON = { cloud:'bi-cloud', 'phan-mem':'bi-window-stack', 'dich-vu':'bi-gear-wide-connected', 'trien-khai':'bi-rocket-takeoff', 'phan-cung':'bi-cpu', khac:'bi-three-dots' };

function TrendBadge({ trend, lang }){
  if (!trend || trend.dir === 'flat') return <span className="badge badge-neutral"><i className="bi bi-dash"></i>{lang === 'vi' ? 'Ổn định' : 'Flat'}</span>;
  const up = trend.dir === 'up';
  return <span className={'badge ' + (up ? 'badge-error' : 'badge-success')}><i className={'bi ' + (up ? 'bi-arrow-up-right' : 'bi-arrow-down-right')}></i>{up ? '+' : '−'}{trend.pct}%</span>;
}

function PrStatusBadge({ s }){ const { lang } = useLang(); const st = PRODUCT_STATUS[s] || PRODUCT_STATUS.active; return <span className={'badge ' + st.cls}><i className="dot"></i>{st[lang] || st.vi}</span>; }

function ProductModule(){
  const p = usePortal();
  const { t, lang } = useLang();
  const sub = p.route.sub || 'overview';
  // Mỗi Nhóm SP (danh mục động — user thêm nhóm là sidebar tự có mục mới) = 1 list riêng trên sidebar.
  const cats = p.catalogs.productCats || [];
  const items = [
    { id:'overview', icon:'bi-pie-chart', label:t('c.overview') },
    { id:'list', icon:'bi-list-ul', label:t('pr.catalog'), count:p.products.length },
    ...cats.map(c => ({ id:'cat-' + c.id, icon:PR_CAT_ICON[c.id] || 'bi-tag', label:c[lang] || c.vi, count:p.products.filter(x => x.category === c.id).length })),
    { id:'report', icon:'bi-bar-chart', label:t('c.report') },
    { id:'settings', icon:'bi-gear', label:t('c.settings') },
  ];
  const go = (id, extra) => p.navigate({ name:'module', module:'products', sub:id, ...extra });
  const catTab = sub.indexOf('cat-') === 0 ? sub.slice(4) : null;
  // Đang xem chi tiết → sáng đúng mục Nhóm của sản phẩm đó (không có nhóm khớp thì về Danh mục).
  const detailProd = sub === 'detail' ? p.products.find(x => x.code === p.route.id) : null;
  const active = sub === 'detail' ? (detailProd && cats.some(c => c.id === detailProd.category) ? 'cat-' + detailProd.category : 'list') : sub;
  return (<>
    <ModuleSidebar app={SP_APP} items={items} active={active} onNavigate={go} />
    <main className="main">
      {sub === 'overview' && <PrOverview go={go} />}
      {sub === 'list' && <PrList go={go} />}
      {catTab && <PrList key={catTab} go={go} fixedCat={catTab} />}
      {sub === 'detail' && <PrDetail code={p.route.id} go={go} />}
      {sub === 'report' && <PrReport go={go} />}
      {sub === 'help' && <ModuleHelp moduleId="products" app={SP_APP} />}
      {sub === 'settings' && <PrSettings />}
    </main>
  </>);
}

function PrOverview({ go }){
  const p = usePortal();
  const { t, lang } = useLang();
  const ps = p.products;
  const byCat = {}; ps.forEach(x => { byCat[x.category] = (byCat[x.category] || 0) + 1; });
  const colors = ['var(--primary)','var(--accent)','#7A5AF8','var(--warning)','var(--success)','var(--gray-400)'];
  const txCount = ps.reduce((s, x) => s + productTransactions(x, p.quotes, p.contracts).length, 0);
  const moved = ps.map(x => ({ x, tr:productTrend(x) })).filter(o => o.tr.dir !== 'flat')
    .sort((a, b) => Math.abs(b.tr.diff) - Math.abs(a.tr.diff)).slice(0, 5);
  const allTx = [];
  ps.forEach(x => productTransactions(x, p.quotes, p.contracts).forEach(tn => allTx.push({ x, tn })));
  const recent = allTx.map(({ x, tn }) => {
    const cust = CustomerStore.byCode(tn.customer) || {};
    const isC = tn.kind === 'contract';
    return { date:tn.date, icon:isC ? 'bi-file-earmark-check' : 'bi-receipt', color:isC ? 'var(--success)' : 'var(--warning)',
      title:x.name,
      sub:(isC ? (lang === 'vi' ? 'HĐ ' : 'Contract ') : (lang === 'vi' ? 'BG ' : 'Quote ')) + tn.code + ' · ' + (cust.shortName || cust.name || '') + ' · ' + (lang === 'vi' ? 'SL ' : 'Qty ') + tn.qty,
      tag:vndShort(tn.qty * tn.price),
      time:fmtAgo(tn.date, lang), onClick:() => go('detail', { id:x.code }) };
  });
  // Sự kiện tạo/cập nhật sản phẩm (theo created_at/updated_at).
  ps.forEach(x => { const ev = recordActivity(x, { create:(lang === 'vi' ? 'Tạo sản phẩm ' : 'New product ') + (x.sku || x.code), update:(lang === 'vi' ? 'Cập nhật sản phẩm ' : 'Updated ') + (x.sku || x.code), sub:x.name }, () => go('detail', { id:x.code })); if (ev) recent.push({ ...ev, time:fmtAgo(ev.date, lang) }); });
  recent.sort((a, b) => String((b.ts || b.date) || '').localeCompare(String((a.ts || a.date) || '')));
  return (
    <div className="page pagefade">
      <div className="page-head"><div><h1>{t('pr.title')}</h1><div className="sub">{lang === 'vi' ? 'Danh mục sản phẩm & dịch vụ, giá và lịch sử giao dịch.' : 'Product & service catalog, pricing and history.'}</div></div>
        <button className="btn btn-primary" onClick={() => p.quick('products')}><i className="bi bi-plus-lg"></i>{t('pr.new')}</button></div>
      <div className="kpi-strip">
        <Kpi label={t('pr.title')} value={ps.length} icon="bi-box-seam" />
        <Kpi label={PRODUCT_STATUS.active[lang]} value={ps.filter(x => x.status === 'active').length} icon="bi-check-circle" />
        <Kpi label={t('pr.txCount')} value={txCount} icon="bi-arrow-left-right" />
        <Kpi label={t('pr.priceChanges')} value={ps.filter(x => productTrend(x).dir !== 'flat').length} icon="bi-graph-up-arrow" />
      </div>
      <div className="dash-grid">
        <div className="card span2"><div className="card-head"><h3>{lang === 'vi' ? 'Biến động giá gần đây' : 'Recent price changes'}</h3></div><div className="card-body col g8">
          {moved.length === 0 && <EmptyState text={t('c.empty')} />}
          {moved.map(({ x, tr }) => (
            <div key={x.code} className="list-row" style={{ cursor:'pointer' }} onClick={() => go('detail', { id:x.code })}>
              <span className="mono" style={{ fontWeight:600, color:'var(--primary)', fontSize:'var(--fs-base)', width:72 }}>{x.code}</span>
              <span className="grow truncate">{x.name}</span>
              <span className="mono" style={{ fontWeight:600 }}>{vnd(productPrice(x))}</span>
              <TrendBadge trend={tr} lang={lang} />
            </div>
          ))}
        </div></div>
        <div className="card"><div className="card-head"><h3>{lang === 'vi' ? 'Theo nhóm' : 'By category'}</h3></div><div className="card-body">
          <DistribBars rows={Object.entries(byCat).sort((a, b) => b[1] - a[1]).map(([k, v], i) => ({ label:prCatLabel(k, lang), value:v, color:colors[i % colors.length] }))} />
        </div></div>
      </div>
      <RecentActivity items={recent} title={lang === 'vi' ? 'Hoạt động gần đây' : 'Recent activity'} />
    </div>
  );
}

function PrList({ go, fixedCat }){
  const p = usePortal();
  const showCost = canViewCost(p);
  const { t, lang } = useLang();
  const [q, setQ] = React.useState('');
  const [catState, setCat] = React.useState('all');
  const cat = fixedCat || catState;                 // fixedCat = list riêng của 1 Nhóm SP (mục sidebar)
  const srt = useTableSort();
  const list = srt.sortRows(p.products.filter(x =>
    (cat === 'all' || x.category === cat) &&
    (!q || (x.code + x.name).toLowerCase().includes(q.toLowerCase()))),
    { code:x => x.code, name:x => x.name, cat:x => prCatLabel(x.category, lang), unit:x => x.unit, price:x => productPrice(x),
      cost:x => { const m = productMargin(x); return m.has ? m.cost : null; }, margin:x => { const m = productMargin(x); return m.has ? m.pct : null; },
      vat:x => +x.vat || 0, vatAmt:x => Math.round(productPrice(x) * (+x.vat || 0) / 100), totalVat:x => Math.round(productPrice(x) * (1 + (+x.vat || 0) / 100)),
      trend:x => productTrend(x), tx:x => productTransactions(x, p.quotes, p.contracts).length });
  return (
    <div className="page pagefade">
      <div className="page-head"><div><h1>{fixedCat ? prCatLabel(fixedCat, lang) : t('pr.catalog')}</h1><div className="sub">{list.length} {lang === 'vi' ? 'sản phẩm / dịch vụ' : 'products / services'}</div></div>
        <button className="btn btn-primary" onClick={() => p.quick('products')}><i className="bi bi-plus-lg"></i>{t('pr.new')}</button></div>
      <div className="toolbar">
        <div className="topsearch" style={{ width:240, background:'var(--bg)', border:'1px solid var(--border-strong)' }}><i className="bi bi-search"></i>
          <input value={q} onChange={e => setQ(e.target.value)} placeholder={t('c.search')} style={{ border:'none', outline:'none', background:'transparent', flex:1 }} /></div>
      </div>
      {/* Bộ lọc nhóm SP/DV bằng chip có icon (ẩn khi đang ở list riêng của 1 nhóm) */}
      {!fixedCat && <div className="catbar">
        <button className={'catchip' + (cat === 'all' ? ' is-active' : '')} onClick={() => setCat('all')}>
          <i className="bi bi-grid"></i>{t('c.all')}<span className="cc">{p.products.length}</span>
        </button>
        {(p.catalogs.productCats || []).map(c => {
          const n = p.products.filter(x => x.category === c.id).length;
          return (
            <button key={c.id} className={'catchip' + (cat === c.id ? ' is-active' : '')} onClick={() => setCat(c.id)}>
              <i className={'bi ' + (PR_CAT_ICON[c.id] || 'bi-tag')}></i>{c[lang]}<span className="cc">{n}</span>
            </button>
          );
        })}
      </div>}
      <div className="card"><div style={{ overflow:'auto' }}>
        <table className="tbl">
          <thead><tr><ThSort k="code" sort={srt}>{t('pr.code')}</ThSort><ThSort k="name" sort={srt}>{t('pr.name')}</ThSort><ThSort k="cat" sort={srt}>{t('pr.category')}</ThSort><ThSort k="unit" sort={srt}>{t('hd.unit')}</ThSort><ThSort k="price" sort={srt} className="num">{lang === 'vi' ? 'Giá bán' : 'Sell'}</ThSort>{showCost && <ThSort k="cost" sort={srt} className="num">{t('pr.cost')}</ThSort>}{showCost && <ThSort k="margin" sort={srt} className="num">{lang === 'vi' ? 'Biên' : 'Margin'}</ThSort>}<ThSort k="vat" sort={srt} className="num">{t('pr.vat')}</ThSort><ThSort k="vatAmt" sort={srt} className="num">{t('pr.vatAmt')}</ThSort><ThSort k="totalVat" sort={srt} className="num">{t('pr.totalVat')}</ThSort><ThSort k="trend" sort={srt}>{t('pr.trend')}</ThSort><ThSort k="tx" sort={srt} className="num">{t('pr.txCount')}</ThSort><th></th></tr></thead>
          <tbody>
            {list.map(x => {
              const tr = productTrend(x);
              const n = productTransactions(x, p.quotes, p.contracts).length;
              const price = productPrice(x);
              const vatPct = +x.vat || 0;
              const vatAmt = Math.round(price * vatPct / 100);
              const mg = productMargin(x);
              return (
                <tr key={x.code} onClick={() => go('detail', { id:x.code })}>
                  <td style={{ whiteSpace:'nowrap' }}><span className="mono" style={{ fontWeight:600, color:'var(--primary)' }}>{x.code}</span></td>
                  <td style={{ fontWeight:500 }}>{x.name}{x.sku && <span className="mono muted" style={{ display:'block', fontSize:'var(--fs-sm)', fontWeight:400 }}>{x.sku}</span>}</td>
                  <td className="muted">{prCatLabel(x.category, lang)}</td>
                  <td className="muted">{x.unit}</td>
                  <td className="num mono" style={{ fontWeight:600 }}>{vnd(price)}</td>
                  {showCost && <td className="num mono" style={{ color:'var(--text-muted)' }}>{mg.has ? vnd(mg.cost) : '—'}</td>}
                  {showCost && <td className="num">{mg.has ? <span className="badge" style={{ background:mg.diff >= 0 ? 'var(--success-bg)' : 'var(--error-bg)', color:mg.diff >= 0 ? 'var(--success)' : 'var(--error)' }}>{mg.pct}%</span> : <span className="subtle">—</span>}</td>}
                  <td className="num mono">{vatPct}%</td>
                  <td className="num mono">{vnd(vatAmt)}</td>
                  <td className="num mono" style={{ fontWeight:600 }}>{vnd(price + vatAmt)}</td>
                  <td><TrendBadge trend={tr} lang={lang} /></td>
                  <td className="num">{n}</td>
                  <td style={{ textAlign:'right' }}><button className="btn btn-sm btn-primary" onClick={e => { e.stopPropagation(); p.startQuoteFrom(x); }}><i className="bi bi-receipt"></i>{t('pr.makeQuoteShort')}</button></td>
                </tr>
              );
            })}
          </tbody>
        </table>
        {!list.length && <EmptyState text={t('c.empty')} />}
      </div></div>
    </div>
  );
}

function PriceHistoryChart({ history, lang }){
  if (!history || !history.length) return <EmptyState text={lang === 'vi' ? 'Chưa có dữ liệu giá' : 'No price data'} />;
  const max = Math.max(...history.map(h => h.price), 1);
  return (
    <div className="col g8">
      {history.map((h, i) => {
        const prev = i > 0 ? history[i - 1].price : null;
        const diff = prev == null ? 0 : h.price - prev;
        const pct = prev ? Math.round(diff / prev * 100) : 0;
        return (
          <div key={i} className="flex g12 items-center">
            <span className="mono subtle" style={{ width:84, fontSize:'var(--fs-base)' }}>{fmtDate(h.date)}</span>
            <div className="grow" style={{ background:'var(--bg-sunken)', borderRadius:'var(--r-pill)', height:22, position:'relative', overflow:'hidden' }}>
              <div style={{ width:Math.max(6, h.price / max * 100) + '%', height:'100%', background:i === history.length - 1 ? 'var(--primary)' : 'var(--gray-400)', borderRadius:'var(--r-pill)', transition:'width .3s' }}></div>
            </div>
            <span className="mono" style={{ width:140, textAlign:'right', fontWeight:600 }}>{vnd(h.price)}</span>
            <span style={{ width:72, textAlign:'right' }}>{diff === 0 ? <span className="subtle">—</span> : <span className="badge" style={{ background:diff > 0 ? 'var(--error-bg)' : 'var(--success-bg)', color:diff > 0 ? 'var(--error)' : 'var(--success)' }}>{diff > 0 ? '+' : '−'}{Math.abs(pct)}%</span>}</span>
          </div>
        );
      })}
    </div>
  );
}

function PrDetail({ code, go }){
  const p = usePortal();
  const showCost = canViewCost(p);
  const { t, lang } = useLang();
  const toast = useToast();
  const confirm = useConfirm();
  const [editing, setEditing] = React.useState(false);
  const x = p.products.find(y => y.code === code);
  if (!x) return <div className="page"><EmptyState text={t('c.empty')} /></div>;
  const remove = async () => {
    if (!(await confirm({ message:(lang === 'vi' ? 'Xóa sản phẩm ' : 'Delete ') + x.name + '?', confirmLabel:lang === 'vi' ? 'Xóa' : 'Delete' }))) return;
    p.deleteProduct(x.code);
    toast((lang === 'vi' ? 'Đã xóa ' : 'Deleted ') + x.code, 'success');
    go('list');
  };
  const tr = productTrend(x);
  const txns = productTransactions(x, p.quotes, p.contracts);
  const custs = [...new Set(txns.map(tn => tn.customer))];
  const spread = productSpread(x, p.contracts);
  const mg = productMargin(x);                                   // markup dự kiến từ giá vốn (master)
  const ven = (p.vendors || []).find(v => v.code === x.vendor);  // NCC mặc định
  const noteForCode = (h) => h.note;
  return (
    <div className="page pagefade">
      <div className="page-head">
        <div className="flex g12 items-center">
          <button className="btn btn-ghost btn-icon" onClick={() => go('list')}><i className="bi bi-arrow-left"></i></button>
          <Avatar name={x.name} size={40} square color="var(--accent)" />
          <div><h1 style={{ fontSize:'var(--fs-2xl)' }}>{x.name}</h1>
            <div className="sub flex g8 items-center"><span className="mono" style={{ color:'var(--primary)', fontWeight:600 }}>{x.code}</span> · {prCatLabel(x.category, lang)} · <PrStatusBadge s={x.status} /></div></div>
        </div>
        <div className="flex g8">
          <button className="btn btn-primary" onClick={() => p.startQuoteFrom(x)}><i className="bi bi-receipt"></i>{t('pr.makeQuote')}</button>
          <button className="btn" onClick={() => setEditing(true)}><i className="bi bi-pencil"></i>{t('c.edit')}</button>
          <button className="btn btn-icon" title={t('c.delete')} onClick={remove}><i className="bi bi-trash"></i></button>
        </div>
      </div>
      {editing && <NewProductModal editing={x} onClose={() => setEditing(false)} />}

      <div className="kpi-strip" style={{ marginBottom:14 }}>
        <Kpi label={t('pr.price')} value={vnd(productPrice(x))} icon="bi-tag" />
        <Kpi label={t('pr.trend')} value={tr.dir === 'flat' ? (lang === 'vi' ? 'Ổn định' : 'Flat') : (tr.dir === 'up' ? '+' : '−') + tr.pct + '%'} icon={tr.dir === 'down' ? 'bi-arrow-down-right' : 'bi-arrow-up-right'} />
        <Kpi label={t('pr.txCount')} value={txns.length} icon="bi-arrow-left-right" />
        <Kpi label={t('pr.customers')} value={custs.length} icon="bi-building" />
      </div>

      {showCost && (spread.buy.length || spread.sell.length) ? (
        <div className="card" style={{ marginBottom:14 }}>
          <div className="card-head"><h3>{lang === 'vi' ? 'Chênh lệch mua / bán' : 'Buy/Sell spread'}</h3><span className="subtle" style={{ fontSize:'var(--fs-sm)' }}>{lang === 'vi' ? 'Tổng hợp từ hợp đồng' : 'Aggregated from contracts'}</span></div>
          <div className="card-body">
            <div className="kpi-strip" style={{ gridTemplateColumns:'repeat(4, 1fr)' }}>
              <Kpi label={lang === 'vi' ? 'Giá mua TB (đầu vào)' : 'Avg buy'} value={vnd(spread.avgBuy)} icon="bi-arrow-up-circle" />
              <Kpi label={lang === 'vi' ? 'Giá bán TB (đầu ra)' : 'Avg sell'} value={vnd(spread.avgSell)} icon="bi-arrow-down-circle" />
              <Kpi label={lang === 'vi' ? 'Chênh lệch' : 'Spread'} value={vnd(spread.diff)} icon="bi-graph-up-arrow" />
              <Kpi label={lang === 'vi' ? 'Biên' : 'Margin'} value={spread.marginPct + '%'} icon="bi-percent" />
            </div>
            <div className="split" style={{ marginTop:12 }}>
              <div><div className="subtle" style={{ fontSize:'var(--fs-sm)', marginBottom:6 }}>{lang === 'vi' ? 'HĐ mua (đầu vào)' : 'Purchases'}</div>
                {spread.buy.length ? spread.buy.map((b, i) => (<div key={i} className="list-row" style={{ cursor:'pointer' }} onClick={() => p.navigate({ name:'module', module:'contracts', sub:'detail', id:b.code })}><span className="mono" style={{ fontWeight:600, color:'var(--primary)', width:120 }}>{b.code}</span><span className="grow truncate subtle">{b.itemName}</span><span className="mono" style={{ color:'var(--error)' }}>{vnd(b.price)}</span></div>)) : <div className="subtle" style={{ fontSize:'var(--fs-base)' }}>—</div>}</div>
              <div><div className="subtle" style={{ fontSize:'var(--fs-sm)', marginBottom:6 }}>{lang === 'vi' ? 'HĐ bán (đầu ra)' : 'Sales'}</div>
                {spread.sell.length ? spread.sell.map((s, i) => (<div key={i} className="list-row" style={{ cursor:'pointer' }} onClick={() => p.navigate({ name:'module', module:'contracts', sub:'detail', id:s.code })}><span className="mono" style={{ fontWeight:600, color:'var(--primary)', width:120 }}>{s.code}</span><span className="grow truncate subtle">{s.itemName}</span><span className="mono" style={{ color:'var(--success)' }}>{vnd(s.price)}</span></div>)) : <div className="subtle" style={{ fontSize:'var(--fs-base)' }}>—</div>}</div>
            </div>
          </div>
        </div>
      ) : null}

      <div className="split" style={{ marginBottom:14 }}>
        <div className="card"><div className="card-head"><h3>{lang === 'vi' ? 'Thông tin' : 'Details'}</h3></div><div className="card-body">
          <div className="det-row"><span className="dk">{t('pr.name')}</span><span className="dv">{x.name}</span></div>
          {x.sku && <div className="det-row"><span className="dk">{t('pr.sku')}</span><span className="dv mono" style={{ fontWeight:600 }}>{x.sku}</span></div>}
          <div className="det-row"><span className="dk">{t('pr.category')}</span><span className="dv">{prCatLabel(x.category, lang)}</span></div>
          <div className="det-row"><span className="dk">{t('hd.unit')}</span><span className="dv">{x.unit}</span></div>
          <div className="det-row"><span className="dk">{t('hd.vat')}</span><span className="dv">{x.vat}%</span></div>
          {showCost && mg.has && <div className="det-row"><span className="dk">{t('pr.cost')}</span><span className="dv mono">{vnd(mg.cost)}</span></div>}
          {x.vendor && <div className="det-row"><span className="dk">{t('pr.vendor')}</span><span className="dv">{ven ? (ven.shortName || ven.name) : x.vendor}</span></div>}
          {showCost && mg.has && <div className="det-row"><span className="dk">{lang === 'vi' ? 'Tăng giá' : 'Markup'}</span><span className="dv"><span className="mono" style={{ fontWeight:600, color:mg.diff >= 0 ? 'var(--success)' : 'var(--error)' }}>{mg.diff >= 0 ? '+' : '−'}{vnd(Math.abs(mg.diff))}</span> <span className="subtle">· {lang === 'vi' ? 'Biên' : 'Margin'} {mg.pct}%</span></span></div>}
          <div className="det-row"><span className="dk">{t('c.status')}</span><span className="dv"><PrStatusBadge s={x.status} /></span></div>
          {x.desc && <div className="det-row"><span className="dk">{t('pr.desc')}</span><span className="dv" style={{ maxWidth:600 }}>{x.desc}</span></div>}
        </div></div>
        <div className="card"><div className="card-head"><h3 style={{ fontSize:'var(--fs-md)' }}>{t('pr.priceHistory')}</h3>{tr.dir !== 'flat' && <TrendBadge trend={tr} lang={lang} />}</div><div className="card-body">
          <PriceHistoryChart history={x.priceHistory} lang={lang} />
          {x.priceHistory.length > 0 && x.priceHistory[x.priceHistory.length - 1].note &&
            <div className="subtle" style={{ fontSize:'var(--fs-sm)', marginTop:10 }}><i className="bi bi-info-circle" style={{ marginRight:4 }}></i>{x.priceHistory[x.priceHistory.length - 1].note}</div>}
        </div></div>
      </div>

      <div className="card">
        <div className="card-head"><h3>{t('pr.txHistory')}</h3><span className="badge badge-neutral">{txns.length} {lang === 'vi' ? 'giao dịch' : 'records'}</span></div>
        <div style={{ overflow:'auto' }}>
          <table className="tbl">
            <thead><tr><th>{t('bg.issueDate') || 'Ngày'}</th><th>{t('ct.customer')}</th><th>{lang === 'vi' ? 'Chứng từ' : 'Document'}</th><th>{t('c.status')}</th><th className="num">{t('hd.qty')}</th><th className="num">{t('hd.price')}</th></tr></thead>
            <tbody>
              {txns.map((tn, i) => {
                const cust = CustomerStore.byCode(tn.customer) || {};
                return (
                  <tr key={i} onClick={() => p.navigate({ name:'module', module:tn.kind === 'contract' ? 'contracts' : 'quotes', sub:'detail', id:tn.code })}>
                    <td className="mono" style={{ fontSize:'var(--fs-base)' }}>{fmtDate(tn.date)}</td>
                    <td><span className="flex g8 items-center"><Avatar name={cust.shortName || tn.customer} size={22} />{cust.name || tn.customer}</span></td>
                    <td><span className="mono" style={{ fontWeight:600, color:'var(--primary)' }}>{tn.code}</span></td>
                    <td>{tn.kind === 'contract' ? <span className="badge badge-info"><i className="dot"></i>{lang === 'vi' ? 'Hợp đồng' : 'Contract'}</span> : <span className="badge badge-warning"><i className="dot"></i>{lang === 'vi' ? 'Báo giá' : 'Quote'}</span>}</td>
                    <td className="num">{tn.qty}</td>
                    <td className="num mono" style={{ fontWeight:600 }}>{vnd(tn.price)}</td>
                  </tr>
                );
              })}
            </tbody>
          </table>
          {!txns.length && <EmptyState text={lang === 'vi' ? 'Chưa có giao dịch nào' : 'No transactions yet'} />}
        </div>
      </div>
    </div>
  );
}

function PrReport({ go }){
  const p = usePortal();
  const { t, lang } = useLang();
  const toast = useToast();
  const period = useReportPeriod('thisyear');
  const months = monthsInPeriod(period);
  const perProduct = p.products.map(x => {
    const txns = productTransactions(x, p.quotes, p.contracts).filter(tn => tn.kind === 'contract' && inPeriod(tn.date, period));
    const revenue = txns.reduce((s, tn) => s + tn.qty * tn.price, 0);
    return { x, txns, revenue, count:txns.length, custs:new Set(txns.map(tn => tn.customer)).size };
  });
  const active = perProduct.filter(r => r.count > 0).sort((a, b) => b.revenue - a.revenue);
  const totalRev = perProduct.reduce((s, r) => s + r.revenue, 0);
  const revByMonth = {}, cntByMonth = {};
  perProduct.forEach(r => r.txns.forEach(tn => { const k = rpMonthKey(tn.date); revByMonth[k] = (revByMonth[k] || 0) + tn.qty * tn.price; cntByMonth[k] = (cntByMonth[k] || 0) + 1; }));
  const byCat = {}; perProduct.forEach(r => { if (r.revenue) byCat[r.x.category] = (byCat[r.x.category] || 0) + r.revenue; });
  const catSlices = Object.entries(byCat).sort((a, b) => b[1] - a[1]).map(([k, v], i) => ({ label:prCatLabel(k, lang), value:v, color:REPORT_COLORS[i % REPORT_COLORS.length] }));
  const rankRows = active.slice(0, 8).map(r => ({ label:r.x.name, value:r.revenue }));
  const excel = () => exportReportXLSX([{
    name:lang === 'vi' ? 'Sản phẩm' : 'Products',
    header:[lang === 'vi' ? 'Mã SP' : 'Code', lang === 'vi' ? 'Tên' : 'Name', lang === 'vi' ? 'Nhóm' : 'Category', lang === 'vi' ? 'Giá hiện tại' : 'Current price', lang === 'vi' ? 'Số KH' : 'Customers', lang === 'vi' ? 'Lượt bán' : 'Deals', lang === 'vi' ? 'Doanh thu HĐ (VND)' : 'Revenue (VND)'],
    widths:[12, 34, 18, 16, 10, 10, 18],
    rows:active.map(r => [r.x.code, r.x.name, prCatLabel(r.x.category, lang), productPrice(r.x), r.custs, r.count, r.revenue]),
  }], 'bao-cao-san-pham_' + period.from + '_' + period.to, toast, lang);

  return (
    <ReportShell title={t('c.report') + ' · ' + t('pr.title')} subtitle={lang === 'vi' ? 'Hiệu quả sản phẩm theo hợp đồng ký trong kỳ.' : 'Product performance by contracts in period.'} period={period} onExcel={excel}>
      <div className="kpi-strip">
        <Kpi label={lang === 'vi' ? 'Doanh thu HĐ' : 'Contract revenue'} value={vndShort(totalRev)} icon="bi-cash-stack" />
        <Kpi label={lang === 'vi' ? 'SP có giao dịch' : 'With activity'} value={active.length} icon="bi-box-seam" />
        <Kpi label={lang === 'vi' ? 'Lượt bán' : 'Deals'} value={perProduct.reduce((s, r) => s + r.count, 0)} icon="bi-arrow-left-right" />
        <Kpi label={t('pr.priceChanges')} value={p.products.filter(x => productTrend(x).dir !== 'flat').length} icon="bi-graph-up-arrow" />
      </div>
      {active.length === 0 ? <div className="card"><div className="card-body"><EmptyState icon="bi-calendar-x" text={lang === 'vi' ? 'Không có giao dịch sản phẩm trong kỳ này' : 'No product activity in this period'} /></div></div> : <>
      <div className="dash-grid" style={{ marginBottom:14 }}>
        <div className="card span2"><div className="card-head"><h3>{lang === 'vi' ? 'Doanh thu theo tháng' : 'Revenue by month'}</h3></div><div className="card-body"><ReportBars months={months} series={revByMonth} money /></div></div>
        <div className="card"><div className="card-head"><h3>{lang === 'vi' ? 'Cơ cấu theo nhóm' : 'By category'}</h3></div><div className="card-body"><ReportDonut slices={catSlices} money /></div></div>
      </div>
      <div className="dash-grid" style={{ marginBottom:14 }}>
        <div className="card span2"><div className="card-head"><h3>{lang === 'vi' ? 'Xu hướng lượt bán' : 'Deals — trend'}</h3></div><div className="card-body"><ReportTrend months={months} series={cntByMonth} /></div></div>
        <div className="card"><div className="card-head"><h3>{lang === 'vi' ? 'Top sản phẩm theo doanh thu' : 'Top products by revenue'}</h3></div><div className="card-body"><ReportRank rows={rankRows} money /></div></div>
      </div>
      <div className="card"><div className="card-head"><h3>{lang === 'vi' ? 'Chi tiết sản phẩm' : 'Product detail'}</h3><span className="badge badge-neutral">{active.length}</span></div><div style={{ overflow:'auto' }}>
        <table className="tbl"><thead><tr><th>{t('pr.name')}</th><th className="num">{t('pr.price')}</th><th>{t('pr.trend')}</th><th className="num">{t('pr.customers')}</th><th className="num">{lang === 'vi' ? 'Lượt bán' : 'Deals'}</th><th className="num">{lang === 'vi' ? 'Doanh thu HĐ' : 'Revenue'}</th></tr></thead>
          <tbody>{active.map(({ x, count, custs, revenue }) => (
            <tr key={x.code} onClick={() => go('detail', { id:x.code })}>
              <td><span className="flex g8 items-center"><span className="mono" style={{ fontWeight:600, color:'var(--primary)', fontSize:'var(--fs-base)' }}>{x.code}</span>{x.name}</span></td>
              <td className="num mono" style={{ fontWeight:600 }}>{vnd(productPrice(x))}</td>
              <td><TrendBadge trend={productTrend(x)} lang={lang} /></td>
              <td className="num">{custs}</td><td className="num">{count}</td>
              <td className="num mono" style={{ fontWeight:600 }}>{revenue ? vnd(revenue) : '—'}</td>
            </tr>
          ))}</tbody></table>
      </div></div>
      </>}
    </ReportShell>
  );
}

function PrSettings(){
  const { t, lang } = useLang();
  return (
    <div className="page pagefade">
      <div className="page-head"><div><h1>{t('c.settings')}</h1><div className="sub">{lang === 'vi' ? 'Nhóm sản phẩm & dịch vụ.' : 'Product & service categories.'}</div></div></div>
      <div className="settings-grid">
        <CatalogEditor catalogKey="productCats" title={lang === 'vi' ? 'Nhóm sản phẩm' : 'Product categories'} />
        <CatalogEditor catalogKey="units" title={lang === 'vi' ? 'Đơn vị tính' : 'Units of measure'} stringMode={true} />
      </div>
    </div>
  );
}

/* New/Edit-product modal (quick action target; truyền `editing` để sửa) */
function NewProductModal({ onClose, editing, onCreated }){
  const p = usePortal();
  const { t, lang } = useLang();
  const toast = useToast();
  const showCost = canViewCost(p);
  const code = editing ? editing.code : genProductCode(p.products);
  const [f, setF] = React.useState(editing
    ? { name:editing.name, sku:editing.sku || '', category:editing.category, unit:editing.unit, vat:editing.vat, cost:productCost(editing), vendor:editing.vendor || '', price:productPrice(editing), desc:editing.desc || '', itemType:editing.itemType || 'product', stockTracked:!!editing.stockTracked, minStock:editing.minStock || 0 }
    : { name:'', sku:'', category:'dich-vu', unit:'', vat:8, cost:0, vendor:'', price:0, desc:'', itemType:'product', stockTracked:false, minStock:0 });
  const set = (k, v) => setF(s => ({ ...s, [k]:v }));
  // Mã hàng tự điền từ tên (chừng nào user chưa tự sửa) — sửa được, có gợi ý mã đã dùng.
  const [skuAuto, setSkuAuto] = React.useState(!editing || !editing.sku);
  React.useEffect(() => { if (skuAuto) setF(s => ({ ...s, sku:genProductSku(s.name) })); }, [f.name, skuAuto]);
  const skuSuggestions = [...new Set((p.products || []).map(x => x.sku).filter(Boolean))];
  // Tăng giá (markup) xem trước trong form: chênh = giá bán − giá vốn; biên = chênh / giá bán.
  const mkAmt = (+f.price || 0) - (+f.cost || 0);
  const mkPct = (+f.price || 0) ? Math.round(mkAmt / (+f.price || 0) * 100) : 0;
  const create = () => {
    if (!f.name.trim()) { toast(lang === 'vi' ? 'Nhập tên sản phẩm' : 'Enter a name', 'error'); return; }
    if (editing) {
      const patch = { name:f.name.trim(), sku:(f.sku || '').trim(), category:f.category, unit:f.unit, vat:+f.vat || 0, cost:+f.cost || 0, vendor:f.vendor || '', desc:f.desc, itemType:f.itemType, stockTracked:!!f.stockTracked, minStock:+f.minStock || 0 };
      const newPrice = +f.price || 0;
      if (newPrice !== productPrice(editing)) patch.priceHistory = [...(editing.priceHistory || []), ph(TODAY, newPrice, lang === 'vi' ? 'Cập nhật giá' : 'Price update')];
      p.updateProduct(editing.code, patch);
      toast(lang === 'vi' ? 'Đã lưu sản phẩm' : 'Saved', 'success');
      onClose(); return;
    }
    const np = { code, sku:(f.sku || '').trim(), name:f.name.trim(), category:f.category, unit:f.unit, vat:+f.vat || 0, cost:+f.cost || 0, vendor:f.vendor || '', status:'active', desc:f.desc, match:[f.name.trim()], itemType:f.itemType, stockTracked:!!f.stockTracked, minStock:+f.minStock || 0, priceHistory:[ph(TODAY, +f.price || 0, lang === 'vi' ? 'Giá khởi tạo' : 'Initial price')] };
    p.addProduct(np);
    toast((lang === 'vi' ? 'Đã thêm ' : 'Added ') + code, 'success');
    onClose();
    // Mở từ nơi khác (vd form Hợp đồng) → trả SP vừa tạo qua onCreated, KHÔNG rời màn hiện tại.
    if (onCreated) { onCreated(np); return; }
    p.navigate({ name:'module', module:'products', sub:'detail', id:code });
  };
  return (
    <Modal title={editing ? (lang === 'vi' ? 'Sửa sản phẩm' : 'Edit product') : t('pr.new')} width={560} onClose={onClose}
      footer={<><button className="btn" onClick={onClose}>{t('c.cancel')}</button><button className="btn btn-primary" onClick={create}>{editing ? t('c.save') : t('c.create')}</button></>}>
      <div className="col g16">
        <div className="flex g12 items-center" style={{ fontSize:'var(--fs-base)' }}><span className="subtle">{t('pr.code')}:</span><span className="mono" style={{ fontWeight:600, color:'var(--primary)' }}>{code}</span>{!editing && <span className="badge badge-neutral">{lang === 'vi' ? 'tự sinh' : 'auto'}</span>}</div>
        <Field label={t('pr.name')}><input className="input" autoFocus value={f.name} onChange={e => set('name', e.target.value)} placeholder={lang === 'vi' ? 'VD: Dịch vụ giám sát hệ thống' : 'e.g. System monitoring service'} /></Field>
        <Field label={t('pr.sku')} hint={lang === 'vi' ? 'Tự điền từ tên — sửa được' : 'Auto from name — editable'}>
          <input className="input" value={f.sku} list="pr-skus" onChange={e => { setSkuAuto(false); set('sku', e.target.value); }} placeholder={lang === 'vi' ? 'VD: DVGSHT' : 'e.g. SKU'} />
          <datalist id="pr-skus">{skuSuggestions.map(s => <option key={s} value={s} />)}</datalist>
        </Field>
        <div className="flex g12">
          <div style={{ flex:1 }}><Field label={t('pr.category')}><CatPickAdd value={f.category} onChange={v => set('category', v)} /></Field></div>
          <div style={{ flex:1 }}><Field label={t('hd.unit')} hint={lang === 'vi' ? 'Chọn từ danh mục hoặc thêm mới' : 'Pick from catalog or add new'}><UnitPickAdd value={f.unit} onChange={v => set('unit', v)} /></Field></div>
        </div>
        <div className="flex g12 wrap items-start">
          <div style={{ flex:'1 1 150px' }}><Field label={lang === 'vi' ? 'Loại hàng' : 'Item type'}><select className="select" value={f.itemType} onChange={e => set('itemType', e.target.value)}>
            {Object.entries(ITEM_TYPES).map(([k, v]) => <option key={k} value={k}>{v[lang]}</option>)}
          </select></Field></div>
          <div style={{ flex:'1 1 150px' }}><Field label={lang === 'vi' ? 'Tồn kho' : 'Stock'}>
            <label className="flex g8 items-center" style={{ height:'var(--control-h)', cursor:'pointer' }}>
              <input type="checkbox" checked={f.stockTracked} onChange={e => set('stockTracked', e.target.checked)} />
              <span style={{ fontWeight:500 }}>{lang === 'vi' ? 'Quản lý tồn kho' : 'Track stock'}</span>
            </label>
          </Field></div>
          {f.stockTracked && <div style={{ flex:'1 1 130px' }}><Field label={lang === 'vi' ? 'Định mức tối thiểu' : 'Min stock'}><input type="number" className="input" min="0" value={f.minStock} onChange={e => set('minStock', e.target.value)} /></Field></div>}
        </div>
        <div className="flex g12">
          <div style={{ flex:1 }}><Field label={lang === 'vi' ? 'Giá bán' : 'Sell price'}><MoneyInput value={f.price} onChange={v => set('price', v)} /></Field></div>
          <div style={{ flex:1 }}><Field label={t('hd.vat') + ' (%)'}><input type="number" className="input" value={f.vat} onChange={e => set('vat', e.target.value)} /></Field></div>
        </div>
        <div className="flex g12">
          {showCost && <div style={{ flex:1 }}><Field label={t('pr.cost')} hint={lang === 'vi' ? 'Giá nhập từ NCC — chỉ nội bộ, không in cho khách' : 'Buy price — internal only'}><MoneyInput value={f.cost} onChange={v => set('cost', v)} /></Field></div>}
          <div style={{ flex:1 }}><Field label={t('pr.vendor')}>
            <select className="select" value={f.vendor} onChange={e => set('vendor', e.target.value)}>
              <option value="">{lang === 'vi' ? '— Không chọn —' : '— None —'}</option>
              {(p.vendors || []).map(v => <option key={v.code} value={v.code}>{v.code} · {v.shortName || v.name}</option>)}
            </select>
          </Field></div>
        </div>
        {showCost && (+f.cost > 0) && (
          <div className="flex g8 items-center" style={{ fontSize:'var(--fs-base)', background:'var(--bg-subtle)', padding:'6px 10px', borderRadius:'var(--r-md)', marginTop:-6 }}>
            <i className="bi bi-graph-up-arrow" style={{ color:'var(--accent)' }}></i>
            <span className="subtle">{lang === 'vi' ? 'Tăng giá:' : 'Markup:'}</span>
            <span className="mono" style={{ fontWeight:700, color:mkAmt >= 0 ? 'var(--success)' : 'var(--error)' }}>{mkAmt >= 0 ? '+' : '−'}{vnd(Math.abs(mkAmt))}</span>
            <span className="subtle">· {lang === 'vi' ? 'Biên' : 'Margin'} {mkPct}%</span>
          </div>
        )}
        <Field label={t('pr.desc')}><textarea className="textarea" rows={2} value={f.desc} onChange={e => set('desc', e.target.value)} /></Field>
      </div>
    </Modal>
  );
}

/* Dashboard widget */
function ProductWidget(){
  const p = usePortal();
  const { t, lang } = useLang();
  const ps = p.products;
  const moved = ps.map(x => ({ x, tr:productTrend(x) })).filter(o => o.tr.dir !== 'flat').slice(0, 3);
  return (
    <WidgetShell wid="products" icon="bi-box-seam" iconColor="#0FB0AB" title={t('pr.title')}
      onViewAll={() => p.openApp(SP_APP)} onNew={() => p.quick('products')} newLabel={t('pr.new')}>
        <div className="flex g16" style={{ marginBottom:8 }}>
          <div><div className="k-val" style={{ fontSize:'var(--fs-2xl)', fontWeight:700 }}>{ps.length}</div><div className="subtle" style={{ fontSize:'var(--fs-sm)' }}>{lang === 'vi' ? 'sản phẩm' : 'products'}</div></div>
          <div><div className="k-val" style={{ fontSize:'var(--fs-2xl)', fontWeight:700, color:'var(--success)' }}>{ps.filter(x => x.status === 'active').length}</div><div className="subtle" style={{ fontSize:'var(--fs-sm)' }}>{PRODUCT_STATUS.active[lang]}</div></div>
        </div>
        {(moved.length ? moved.map(o => o.x) : ps.slice(0, 3)).map(x => (
          <div key={x.code} className="list-row" style={{ cursor:'pointer' }} onClick={() => p.navigate({ name:'module', module:'products', sub:'detail', id:x.code })}>
            <span className="mono" style={{ fontWeight:600, color:'var(--primary)', fontSize:'var(--fs-base)', width:64 }}>{x.code}</span><span className="grow truncate">{x.name}</span><TrendBadge trend={productTrend(x)} lang={lang} />
          </div>
        ))}
    </WidgetShell>
  );
}

Object.assign(window, { ProductModule, NewProductModal, ProductWidget, prCatLabel, TrendBadge, PrStatusBadge });
