// customers.jsx — CUSTOMERS master-data module + CustomerStore + widget.
const CUST_APP = APPS.find(a => a.id === 'customers');

function deburr(s){ return (s || '').normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/đ/g, 'd').replace(/Đ/g, 'D'); }
const CODE_STOPWORDS = new Set(['cong','ty','tnhh','co','ltd','jsc','corp','corporation','company','jv','group','technology','tech','digital','solution','solutions','vietnam','viet','nam','vn','gmbh','inc','nghe','the','and']);
function genCustomerCode(name) {
  const words = deburr(name).split(/[^A-Za-z0-9]+/).filter(Boolean);
  const meaningful = words.filter(w => !CODE_STOPWORDS.has(w.toLowerCase()));
  const base = (meaningful.length ? meaningful : words).join('').toUpperCase();
  return base.slice(0, 10) || 'KH';
}
function flagBadge(cc){ return <span className="flag mono" style={{ fontWeight:600 }}>{cc}</span>; }

function CustomerModule() {
  const p = usePortal();
  const { t, tt, lang } = useLang();
  const toast = useToast();
  const customers = p.customers;
  const sub = p.route.sub || 'overview';
  const items = [
    { id:'overview', icon:'bi-pie-chart', label:t('c.overview') },
    { id:'list', icon:'bi-list-ul', label:t('c.list'), count:customers.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:'customers', sub:id, ...extra });

  return (<>
    <ModuleSidebar app={CUST_APP} items={items} active={sub === 'detail' ? 'list' : sub} onNavigate={go} />
    <main className="main">
      {sub === 'overview' && <CustOverview go={go} />}
      {sub === 'list' && <CustList go={go} />}
      {sub === 'detail' && <CustDetail code={p.route.id} go={go} />}
      {sub === 'report' && <CustReport />}
      {sub === 'help' && <ModuleHelp moduleId="customers" app={CUST_APP} />}
      {sub === 'settings' && <CustSettings />}
    </main>
  </>);
}

function countBy(arr, key) {
  const m = {}; arr.forEach(x => { const k = typeof key === 'function' ? key(x) : x[key]; m[k] = (m[k] || 0) + 1; }); return m;
}

function CustOverview({ go }) {
  const p = usePortal();
  const { t, lang } = useLang();
  const cs = p.customers;
  const byInd = countBy(cs, 'industry');
  const byCountry = countBy(cs, 'country');
  const colors = ['var(--primary)','var(--accent)','#7A5AF8','var(--warning)','var(--success)','#D14343','var(--gray-400)'];
  // tương tác mới nhất: tổng hợp hoạt động thật theo khách hàng (HĐ ký + báo giá + timeline liên hệ)
  const recent = [];
  (p.contracts || []).forEach(c => {
    const cust = cs.find(x => x.code === c.customer); if (!cust) return;   // bỏ HĐ mua (đối tác = NCC)
    recent.push({ date:c.signDate, icon:'bi-file-earmark-check', color:'#0B4FBF', title:(lang === 'vi' ? 'Ký hợp đồng ' : 'Signed ') + c.code, sub:cust.shortName || cust.name, onClick:() => go('detail', { id:cust.code }) });
  });
  (p.quotes || []).forEach(q => {
    const cust = cs.find(x => x.code === q.customer); if (!cust) return;
    recent.push({ date:q.issueDate, icon:'bi-receipt', color:'#D14343', title:(lang === 'vi' ? 'Báo giá ' : 'Quote ') + q.code, sub:cust.shortName || cust.name, onClick:() => go('detail', { id:cust.code }) });
  });
  p.contacts.forEach(ct => {
    const cust = cs.find(c => c.code === ct.customer);
    if (!cust) return;
    (ct.tx || []).forEach(x => {
      const ty = TX_TYPES[x.type] || {};
      recent.push({ date:x.date, icon:ty.icon, color:ty.color, title:x.title, sub:(cust.shortName || cust.name) + ' · ' + ct.name, onClick:() => go('detail', { id:cust.code }) });
    });
  });
  // Sự kiện tạo/cập nhật khách hàng (theo created_at/updated_at).
  cs.forEach(c => { const ev = recordActivity(c, { create:(lang === 'vi' ? 'Tạo khách hàng ' : 'New customer ') + (c.shortName || c.name), update:(lang === 'vi' ? 'Cập nhật khách hàng ' : 'Updated ') + (c.shortName || c.name), sub:c.code }, () => go('detail', { id:c.code })); if (ev) recent.push(ev); });
  recent.sort((a, b) => ((a.ts || a.date) < (b.ts || b.date) ? 1 : -1));
  return (
    <div className="page pagefade">
      <div className="page-head"><div><h1>{t('cust.title')}</h1><div className="sub">{lang === 'vi' ? 'Tổng quan dữ liệu chủ khách hàng.' : 'Customer master-data overview.'}</div></div>
        <button className="btn btn-primary" onClick={() => p.quick('customers')}><i className="bi bi-plus-lg"></i>{t('cust.new')}</button></div>
      <div className="kpi-strip">
        <Kpi label={t('cust.title')} value={cs.length} icon="bi-building" />
        <Kpi label={t('st.active')} value={cs.filter(c => c.status === 'active').length} icon="bi-check-circle" delta="+3" deltaDir="up" />
        <Kpi label={t('st.prospect')} value={cs.filter(c => c.status === 'prospect').length} icon="bi-hourglass-split" />
        <Kpi label={lang === 'vi' ? 'Quốc gia' : 'Countries'} value={Object.keys(byCountry).length} icon="bi-globe2" />
      </div>
      <div className="dash-grid">
        <div className="card span2"><div className="card-head"><h3>{lang === 'vi' ? 'Phân bố theo ngành' : 'By industry'}</h3></div><div className="card-body">
          <DistribBars rows={Object.entries(byInd).sort((a, b) => b[1] - a[1]).map(([label, value], i) => ({ label, value, color:colors[i % colors.length] }))} />
        </div></div>
        <div className="card"><div className="card-head"><h3>{lang === 'vi' ? 'Theo quốc gia' : 'By country'}</h3></div><div className="card-body">
          <DistribBars rows={Object.entries(byCountry).sort((a, b) => b[1] - a[1]).map(([label, value], i) => ({ label:COUNTRIES[label] || label, value, color:colors[i % colors.length] }))} />
        </div></div>
      </div>
      <RecentInteractions items={recent} onViewAll={() => go('list')} />
    </div>
  );
}

function CustList({ go }) {
  const p = usePortal();
  const { t, tt, lang } = useLang();
  const [q, setQ] = React.useState('');
  const [status, setStatus] = React.useState('all');
  const srt = useTableSort();
  const list = srt.sortRows(p.customers.filter(c =>
    (status === 'all' || c.status === status) &&
    (!q || (c.name + c.code + c.shortName).toLowerCase().includes(q.toLowerCase()))),
    { code:c => c.code, name:c => c.name, industry:c => c.industry, country:c => COUNTRIES[c.country] || c.country, mst:c => c.mst, owner:c => c.owner, status:c => c.status });
  return (
    <div className="page pagefade">
      <div className="page-head"><div><h1>{t('c.list')}</h1><div className="sub">{list.length} {lang === 'vi' ? 'khách hàng' : 'customers'}</div></div>
        <button className="btn btn-primary" onClick={() => p.quick('customers')}><i className="bi bi-plus-lg"></i>{t('cust.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 className="seg">
          {['all','active','prospect','inactive'].map(s => <button key={s} className={status === s ? 'is-active' : ''} onClick={() => setStatus(s)}><i className={'bi ' + ({ all:'bi-grid-1x2', active:'bi-check-circle', prospect:'bi-hourglass-split', inactive:'bi-slash-circle' }[s])}></i>{s === 'all' ? t('c.all') : t('st.' + s)}</button>)}
        </div>
      </div>
      <div className="card"><div style={{ overflow:'auto' }}>
        <table className="tbl">
          <thead><tr><ThSort k="code" sort={srt}>{t('cust.code')}</ThSort><ThSort k="name" sort={srt}>{t('cust.name')}</ThSort><ThSort k="industry" sort={srt}>{t('cust.industry')}</ThSort><ThSort k="country" sort={srt}>{t('cust.country')}</ThSort><ThSort k="mst" sort={srt}>{t('cust.mst')}</ThSort><ThSort k="owner" sort={srt}>{t('c.owner')}</ThSort><ThSort k="status" sort={srt}>{t('c.status')}</ThSort></tr></thead>
          <tbody>
            {list.map(c => (
              <tr key={c.code} onClick={() => go('detail', { id:c.code })}>
                <td><span className="mono" style={{ fontWeight:600, color:'var(--primary)' }}>{c.code}</span></td>
                <td><span style={{ fontWeight:500 }}>{c.name}</span> <span className="subtle">· {c.shortName}</span></td>
                <td className="muted">{c.industry}</td>
                <td>{flagBadge(c.country)} <span className="muted">{COUNTRIES[c.country]}</span></td>
                <td className="mono" style={{ fontSize:'var(--fs-base)' }}>{c.mst}</td>
                <td>{c.owner}</td>
                <td><StatusBadge status={c.status} /></td>
              </tr>
            ))}
          </tbody>
        </table>
        {list.length === 0 && <EmptyState text={t('c.empty')} />}
      </div></div>
    </div>
  );
}

function CustDetail({ code, go }) {
  const p = usePortal();
  const { t, lang } = useLang();
  const toast = useToast();
  const confirm = useConfirm();
  const [editing, setEditing] = React.useState(false);
  const c = p.customers.find(x => x.code === code);
  if (!c) return <div className="page"><EmptyState text={t('c.empty')} /></div>;
  const remove = async () => {
    if (!(await confirm({ message:(lang === 'vi' ? 'Xóa khách hàng ' : 'Delete customer ') + c.name + '?', confirmLabel:lang === 'vi' ? 'Xóa' : 'Delete' }))) return;
    p.deleteCustomer(c.code);
    toast((lang === 'vi' ? 'Đã xóa ' : 'Deleted ') + c.code, 'success');
    go('list');
  };
  const linkedContacts = p.contacts.filter(ct => ct.customer === c.code);
  const custQuotes = (p.quotes || []).filter(q => q.customer === c.code);
  const custContracts = (p.contracts || []).filter(x => x.customer === c.code && contractDir(x) === 'sell');
  const emailCount = p.sentEmails.filter(e => linkedContacts.some(lc => lc.email && e.to.includes(lc.email))).length;
  const linkedTickets = (p.tickets || []).filter(tk => tk.customer === c.code);
  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={c.shortName} size={40} square color="var(--primary)" />
          <div><h1 style={{ fontSize:'var(--fs-2xl)' }}>{c.name}</h1><div className="sub flex g8 items-center"><span className="mono" style={{ color:'var(--primary)', fontWeight:600 }}>{c.code}</span> · {c.shortName} · <StatusBadge status={c.status} /></div></div>
        </div>
        <div className="flex g8 wrap">
          <button className="btn" onClick={() => p.quick('contacts', { customer:c.code, type:'customer' })}><i className="bi bi-person-plus"></i>{lang === 'vi' ? 'Thêm liên hệ' : 'Add contact'}</button>
          <button className="btn" onClick={() => p.quick('quotes', { customer:c.code })}><i className="bi bi-receipt"></i>{lang === 'vi' ? 'Tạo báo giá' : 'New quote'}</button>
          <button className="btn" onClick={() => p.quick('contracts', { customer:c.code, direction:'sell' })}><i className="bi bi-file-earmark-text"></i>{lang === 'vi' ? 'Tạo hợp đồng' : 'New contract'}</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 && <NewCustomerModal editing={c} onClose={() => setEditing(false)} />}
      <div className="split">
        <div className="col g12">
          <div className="card"><div className="card-head"><h3>{lang === 'vi' ? 'Thông tin chi tiết' : 'Details'}</h3></div><div className="card-body">
            <div className="det-row"><span className="dk">{t('cust.name')}</span><span className="dv">{c.name}</span></div>
            <div className="det-row"><span className="dk">{t('cust.short')}</span><span className="dv">{c.shortName}</span></div>
            <div className="det-row"><span className="dk">{t('cust.mst')}</span><span className="dv mono">{c.mst}</span></div>
            {c.address && <div className="det-row"><span className="dk">{t('cust.address')}</span><span className="dv">{c.address}</span></div>}
            <div className="det-row"><span className="dk">{t('cust.industry')}</span><span className="dv">{c.industry}</span></div>
            <div className="det-row"><span className="dk">{t('cust.country')}</span><span className="dv">{flagBadge(c.country)} {COUNTRIES[c.country]}</span></div>
            <div className="det-row"><span className="dk">{t('cust.lang')}</span><span className="dv">{LANGS[c.lang]}</span></div>
            <div className="det-row"><span className="dk">{t('c.owner')}</span><span className="dv">{c.owner}</span></div>
          </div></div>
          {custQuotes.length > 0 && (
            <div className="card"><div className="card-head"><h3 style={{ fontSize:'var(--fs-md)' }}>{lang === 'vi' ? 'Báo giá liên quan' : 'Linked quotes'}</h3><span className="subtle" style={{ fontSize:'var(--fs-sm)' }}>{vndShort(custQuotes.reduce((s, q) => s + quoteTotals(q).total, 0))}</span></div><div className="card-body col g4">
              {custQuotes.map(lq => { const expired = quoteStatus(lq) === 'expired'; return (
                <div key={lq.code} className="col g4" style={{ cursor:'pointer', padding:'8px 6px', borderRadius:'var(--r-sm)' }} onClick={() => p.navigate({ name:'module', module:'quotes', sub:'detail', id:lq.code })}>
                  <div className="flex g8 items-center">
                    <span className="mono" style={{ fontWeight:600, color:'var(--primary)', fontSize:'var(--fs-base)' }}>{lq.code}</span>
                    <span className="grow"></span>
                    <span className="mono" style={{ fontWeight:600 }}>{vndShort(quoteTotals(lq).total)}</span>
                    <BgStatusBadge q={lq} />
                  </div>
                  <div className="flex g10 items-center subtle wrap" style={{ fontSize:'var(--fs-sm)' }}>
                    <span><i className="bi bi-calendar3" style={{ marginRight:4 }}></i>{fmtDate(lq.issueDate)}</span>
                    <span><i className="bi bi-list-ul" style={{ marginRight:4 }}></i>{(lq.items || []).length} {lang === 'vi' ? 'hạng mục' : 'items'}</span>
                    <span style={{ color:expired ? 'var(--error)' : 'inherit' }}><i className="bi bi-clock" style={{ marginRight:4 }}></i>{lang === 'vi' ? 'HL đến ' : 'valid to '}{fmtDate(lq.validUntil)}</span>
                    {lq.convertedTo && <span style={{ color:'var(--success)' }}><i className="bi bi-arrow-right-circle" style={{ marginRight:4 }}></i>{lq.convertedTo}</span>}
                  </div>
                  {lq.notes && <div className="truncate" style={{ fontSize:'var(--fs-sm)', color:'var(--text-muted)' }}>{lq.notes}</div>}
                </div>
              ); })}
            </div></div>
          )}
          {custContracts.length > 0 && (
            <div className="card"><div className="card-head"><h3 style={{ fontSize:'var(--fs-md)' }}>{lang === 'vi' ? 'Hợp đồng liên quan' : 'Linked contracts'}</h3><span className="subtle" style={{ fontSize:'var(--fs-sm)' }}>{vndShort(custContracts.reduce((s, x) => s + contractTotals(x).total, 0))}</span></div><div className="card-body col g4">
              {custContracts.map(lc => {
                const sch = lc.schedule || []; const paidN = sch.filter(s => s.paid).length;
                const term = lc.termType === 'term' && lc.endDate ? (fmtDate(lc.startDate) + ' → ' + fmtDate(lc.endDate)) : (lang === 'vi' ? 'Một lần' : 'One-time');
                return (
                <div key={lc.code} className="col g4" style={{ cursor:'pointer', padding:'8px 6px', borderRadius:'var(--r-sm)' }} onClick={() => p.navigate({ name:'module', module:'contracts', sub:'detail', id:lc.code })}>
                  <div className="flex g8 items-center">
                    <span className="mono" style={{ fontWeight:600, color:'var(--primary)', fontSize:'var(--fs-base)' }}>{lc.code}</span>
                    <span className="grow"></span>
                    <span className="mono" style={{ fontWeight:600 }}>{vndShort(contractTotals(lc).total)}</span>
                    <HdStatusBadge c={lc} />
                  </div>
                  <div className="flex g10 items-center subtle wrap" style={{ fontSize:'var(--fs-sm)' }}>
                    <span><i className="bi bi-folder2" style={{ marginRight:4 }}></i>{catLabel(lc.category, lang)}</span>
                    <span><i className="bi bi-pen" style={{ marginRight:4 }}></i>{lang === 'vi' ? 'Ký ' : 'Signed '}{fmtDate(lc.signDate)}</span>
                    {sch.length > 0 && <span style={{ color:paidN === sch.length ? 'var(--success)' : 'inherit' }}><i className="bi bi-cash-coin" style={{ marginRight:4 }}></i>{paidN}/{sch.length} {lang === 'vi' ? 'đợt' : 'paid'}</span>}
                  </div>
                  <div className="truncate" style={{ fontSize:'var(--fs-sm)', color:'var(--text-muted)' }}><i className="bi bi-calendar-range" style={{ marginRight:4 }}></i>{term}</div>
                </div>
              ); })}
            </div></div>
          )}
        </div>
        <div className="col g12">
          <div className="card"><div className="card-head"><h3 style={{ fontSize:'var(--fs-md)' }}>{t('cust.links')}</h3></div><div className="card-body col g8">
            <div className="flex g8 wrap">
              <span className="linkcount"><i className="bi bi-person-vcard"></i><b>{linkedContacts.length}</b> {lang === 'vi' ? 'liên hệ' : 'contacts'}</span>
              <span className="linkcount"><i className="bi bi-receipt"></i><b>{custQuotes.length}</b> {lang === 'vi' ? 'báo giá' : 'quotes'}</span>
              <span className="linkcount"><i className="bi bi-file-earmark-text"></i><b>{custContracts.length}</b> {lang === 'vi' ? 'hợp đồng' : 'contracts'}</span>
              <span className="linkcount"><i className="bi bi-headset"></i><b>{linkedTickets.length}</b> {lang === 'vi' ? 'phiếu hỗ trợ' : 'tickets'}</span>
              <span className="linkcount"><i className="bi bi-envelope"></i><b>{emailCount}</b> email</span>
            </div>
          </div></div>
          {linkedContacts.length > 0 && (
            <div className="card"><div className="card-head"><h3 style={{ fontSize:'var(--fs-md)' }}>{lang === 'vi' ? 'Liên hệ liên quan' : 'Linked contacts'}</h3></div><div className="card-body">
              {linkedContacts.map(lc => (
                <div key={lc.code} className="list-row" style={{ cursor:'pointer' }} onClick={() => p.navigate({ name:'module', module:'contacts', sub:'detail', id:lc.code })}>
                  <Avatar name={lc.name} size={24} color="var(--gray-400)" /><span className="grow" style={{ fontWeight:500 }}>{lc.name}</span><span className="subtle">{lc.title}</span>
                </div>
              ))}
            </div></div>
          )}
          {linkedTickets.length > 0 && (
            <div className="card"><div className="card-head"><h3 style={{ fontSize:'var(--fs-md)' }}><i className="bi bi-headset" style={{ marginRight:6, color:'var(--accent)' }}></i>{lang === 'vi' ? 'Phiếu hỗ trợ liên quan' : 'Linked support tickets'}</h3>
              <button className="btn btn-ghost btn-sm" onClick={() => p.quick('support', { customer:c.code })}>{lang === 'vi' ? 'Tạo phiếu' : 'New ticket'}</button></div><div className="card-body">
              {linkedTickets.map(tk => (
                <div key={tk.code} className="list-row" style={{ cursor:'pointer' }} onClick={() => p.navigate({ name:'module', module:'support', sub:'detail', id:tk.code })}>
                  <span className="mono" style={{ fontWeight:600, color:'var(--accent)', fontSize:'var(--fs-base)', width:104 }}>{tk.code}</span><span className="grow truncate">{tk.title}</span><TicketStatusBadge status={tk.status} />
                </div>
              ))}
            </div></div>
          )}
        </div>
      </div>
    </div>
  );
}

function CustReport() {
  const p = usePortal();
  const { t, lang } = useLang();
  const cs = p.customers;
  const byOwner = countBy(cs, 'owner');
  const byStatus = countBy(cs, 'status');
  return (
    <div className="page pagefade">
      <div className="page-head"><div><h1>{t('c.report')}</h1><div className="sub">{lang === 'vi' ? 'Thống kê danh mục khách hàng.' : 'Customer portfolio statistics.'}</div></div>
        <button className="btn"><i className="bi bi-download"></i>{lang === 'vi' ? 'Xuất Excel' : 'Export'}</button></div>
      <div className="dash-grid">
        <div className="card"><div className="card-head"><h3>{lang === 'vi' ? 'Theo trạng thái' : 'By status'}</h3></div><div className="card-body">
          <DistribBars rows={['active','prospect','inactive'].map(s => ({ label:t('st.' + s), value:byStatus[s] || 0, color:s === 'active' ? 'var(--success)' : s === 'prospect' ? 'var(--warning)' : 'var(--gray-400)' }))} />
        </div></div>
        <div className="card span2"><div className="card-head"><h3>{lang === 'vi' ? 'Theo người phụ trách' : 'By owner'}</h3></div><div style={{ overflow:'auto' }}>
          <table className="tbl"><thead><tr><th>{t('c.owner')}</th><th className="num">{lang === 'vi' ? 'Số KH' : 'Customers'}</th><th className="num">{lang === 'vi' ? 'Tỷ lệ' : 'Share'}</th></tr></thead>
            <tbody>{Object.entries(byOwner).sort((a, b) => b[1] - a[1]).map(([o, n]) => (
              <tr key={o} style={{ cursor:'default' }}><td><span className="flex g8 items-center"><Avatar name={o} size={22} />{o}</span></td><td className="num">{n}</td><td className="num muted">{Math.round(n / cs.length * 100)}%</td></tr>
            ))}</tbody></table>
        </div></div>
      </div>
    </div>
  );
}

function CustSettings() {
  const { t, lang } = useLang();
  return (
    <div className="page pagefade">
      <div className="page-head"><div><h1>{t('c.settings')}</h1><div className="sub">{lang === 'vi' ? 'Danh mục & quy tắc dữ liệu.' : 'Categories & data rules.'}</div></div></div>
      <CatalogEditor catalogKey="industries" title={lang === 'vi' ? 'Danh mục ngành' : 'Industry categories'} stringMode={true} />
    </div>
  );
}

/* New/Edit-customer modal (quick action target; truyền `editing` để sửa) */
function NewCustomerModal({ onClose, editing }) {
  const p = usePortal();
  const { t, lang } = useLang();
  const toast = useToast();
  const [auto, setAuto] = React.useState(!editing);
  const [f, setF] = React.useState(editing
    ? { name:editing.name, shortName:editing.shortName, code:editing.code, lang:editing.lang, mst:editing.mst, address:editing.address || '', industry:editing.industry, country:editing.country, owner:editing.owner, status:editing.status }
    : { name:'', shortName:'', code:'', lang:'vi', mst:'', address:'', industry:INDUSTRIES[0], country:'VN', owner:p.me.name, status:'prospect' });
  const code = editing ? editing.code : (auto ? genCustomerCode(f.name) : f.code.toUpperCase());
  const dup = !editing && p.customers.some(c => c.code === code) && code;
  const set = (k, v) => setF(s => ({ ...s, [k]: v }));
  // Tra cứu doanh nghiệp theo MST (VietQR, miễn phí, gọi trực tiếp client-side) → tự điền tên + địa chỉ.
  const [looking, setLooking] = React.useState(false);
  const lookupMst = async () => {
    const mst = (f.mst || '').trim();
    if (!mst) { toast(lang === 'vi' ? 'Nhập mã số thuế trước' : 'Enter tax ID first', 'error'); return; }
    setLooking(true);
    try {
      const r = await fetch('https://api.vietqr.io/v2/business/' + encodeURIComponent(mst));
      const j = await r.json().catch(() => null);
      if (j && j.code === '00' && j.data && j.data.name) {
        setF(s => ({ ...s, name:j.data.name, shortName:s.shortName || j.data.shortName || '', address:j.data.address || s.address }));
        toast(lang === 'vi' ? 'Đã lấy thông tin doanh nghiệp' : 'Company info fetched', 'success');
      } else {
        toast(lang === 'vi' ? 'Không tìm thấy doanh nghiệp với MST này' : 'No business found for this tax ID', 'error');
      }
    } catch (e) {
      toast((lang === 'vi' ? 'Tra cứu thất bại: ' : 'Lookup failed: ') + (e.message || e), 'error');
    } finally { setLooking(false); }
  };
  const create = () => {
    if (!f.name.trim()) { toast(lang === 'vi' ? 'Nhập tên khách hàng' : 'Enter a name', 'error'); return; }
    const rec = { ...(editing || {}), ...f, code, shortName:f.shortName || f.name.split(' ')[0] };
    if (editing) {
      p.updateCustomer(editing.code, rec);
      toast(lang === 'vi' ? 'Đã lưu thay đổi' : 'Saved', 'success');
      onClose(); return;
    }
    if (!code || dup) { toast(t('cust.dup'), 'error'); return; }
    p.addCustomer(rec);
    toast((lang === 'vi' ? 'Đã tạo KH ' : 'Created ') + code, 'success');
    onClose();
    p.navigate({ name:'module', module:'customers', sub:'detail', id:code });
  };
  return (
    <Modal title={editing ? (lang === 'vi' ? 'Sửa khách hàng' : 'Edit customer') : t('cust.new')} width={600} onClose={onClose}
      footer={<><button className="btn" onClick={onClose}>{t('c.cancel')}</button><button className="btn btn-primary" onClick={create} disabled={!!dup}>{editing ? t('c.save') : t('c.create')}</button></>}>
      <div className="col g16">
        <Field label={t('cust.name')}><input className="input" autoFocus value={f.name} onChange={e => set('name', e.target.value)} placeholder={lang === 'vi' ? 'VD: Công ty TNHH ABC' : 'e.g. ABC Company Ltd'} /></Field>
        <div className="flex g12">
          <div style={{ flex:1 }}><Field label={t('cust.code')} hint={editing ? '' : t('cust.codeLocked')} hintError={!editing && dup ? t('cust.dup') : ''}>
            <div className="flex g8 items-center">
              <input className={'input mono' + (dup ? ' is-error' : '')} value={code} disabled={!!editing} onChange={e => { setAuto(false); set('code', e.target.value); }} style={{ fontWeight:600, textTransform:'uppercase', background:editing ? 'var(--bg-sunken)' : undefined }} />
              {!editing && <button className={'btn btn-sm' + (auto ? ' btn-primary' : '')} title={t('cust.autocode')} onClick={() => setAuto(a => !a)}><i className="bi bi-magic"></i></button>}
            </div>
          </Field></div>
          <div style={{ flex:1 }}><Field label={t('cust.short')}><input className="input" value={f.shortName} onChange={e => set('shortName', e.target.value)} placeholder={f.name.split(' ')[0]} /></Field></div>
        </div>
        <div className="flex g12">
          <div style={{ flex:1 }}><Field label={t('cust.mst')} hint={lang === 'vi' ? 'Nhập MST rồi bấm tra cứu để tự điền tên + địa chỉ' : 'Enter tax ID then look up to autofill'}>
            <div className="flex g8 items-center">
              <input className="input mono" value={f.mst} onChange={e => set('mst', e.target.value)} placeholder="0300000000" />
              <button className="btn btn-sm" onClick={lookupMst} disabled={looking} title={t('cust.lookup')}>{looking ? <i className="bi bi-arrow-repeat spin"></i> : <i className="bi bi-search"></i>}</button>
            </div>
          </Field></div>
          <div style={{ flex:1 }}><Field label={t('cust.lang')}><select className="select" value={f.lang} onChange={e => set('lang', e.target.value)}>{Object.entries(LANGS).map(([k, v]) => <option key={k} value={k}>{v}</option>)}</select></Field></div>
        </div>
        <Field label={t('cust.address')}><input className="input" value={f.address} onChange={e => set('address', e.target.value)} placeholder={lang === 'vi' ? 'Địa chỉ trụ sở (tự điền khi tra cứu MST)' : 'Head office address'} /></Field>
        <div className="flex g12">
          <div style={{ flex:1 }}><Field label={t('cust.industry')}><select className="select" value={f.industry} onChange={e => set('industry', e.target.value)}>{(p.catalogs.industries || []).map(i => <option key={i}>{i}</option>)}</select></Field></div>
          <div style={{ flex:1 }}><Field label={t('cust.country')}><select className="select" value={f.country} onChange={e => set('country', e.target.value)}>{Object.entries(COUNTRIES).map(([k, v]) => <option key={k} value={k}>{v}</option>)}</select></Field></div>
        </div>
        <div className="flex g12">
          <div style={{ flex:1 }}><Field label={t('c.owner')}><select className="select" value={f.owner} onChange={e => set('owner', e.target.value)}>{p.users.map(u => <option key={u.id}>{u.name}</option>)}</select></Field></div>
          <div style={{ flex:1 }}><Field label={t('c.status')}><select className="select" value={f.status} onChange={e => set('status', e.target.value)}>{['prospect','active','inactive'].map(s => <option key={s} value={s}>{t('st.' + s)}</option>)}</select></Field></div>
        </div>
      </div>
    </Modal>
  );
}

/* Dashboard widget */
function CustomerWidget() {
  const p = usePortal();
  const { t, lang } = useLang();
  const top = p.customers.slice(0, 4);
  return (
    <WidgetShell wid="customers" icon="bi-building" iconColor="var(--primary)" title={t('cust.title')}
      onViewAll={() => p.openApp(CUST_APP)} onNew={() => p.quick('customers')} newLabel={t('cust.new')}>
        <div className="flex g16" style={{ marginBottom:8 }}>
          <div><div className="k-val" style={{ fontSize:'var(--fs-2xl)', fontWeight:700 }}>{p.customers.length}</div><div className="subtle" style={{ fontSize:'var(--fs-sm)' }}>{lang === 'vi' ? 'khách hàng' : 'customers'}</div></div>
          <div><div className="k-val" style={{ fontSize:'var(--fs-2xl)', fontWeight:700, color:'var(--success)' }}>{p.customers.filter(c => c.status === 'active').length}</div><div className="subtle" style={{ fontSize:'var(--fs-sm)' }}>{t('st.active')}</div></div>
        </div>
        {top.map(c => (
          <div key={c.code} className="list-row" style={{ cursor:'pointer' }} onClick={() => p.navigate({ name:'module', module:'customers', sub:'detail', id:c.code })}>
            <span className="mono" style={{ fontWeight:600, color:'var(--primary)', fontSize:'var(--fs-base)', width:64 }}>{c.code}</span><span className="grow truncate">{c.shortName}</span><StatusBadge status={c.status} />
          </div>
        ))}
    </WidgetShell>
  );
}

Object.assign(window, { CustomerModule, NewCustomerModal, CustomerWidget, genCustomerCode });
