// support.jsx — MODULE Hỗ trợ sau bán: ticket 4 bước (Tiếp nhận→Xử lý→Kết quả→Báo cáo),
// gắn Khách hàng + Hợp đồng bán, kênh tiếp nhận chính là QR định danh theo hợp đồng.
// Phụ thuộc: cdata (SUPPORT_*, ticketSla, supportableContracts, qrInfoOf, contractParty, ev, TODAY),
// qr.jsx (ContractQRCard), contracts.jsx (catLabel), ui.jsx (primitives), sidebar (ModuleSidebar), help.jsx (ModuleHelp).
const SUPPORT_APP = APPS.find(a => a.id === 'support');

function TicketStatusBadge({ status }){
  const { lang } = useLang(); const s = SUPPORT_STATUS[status] || SUPPORT_STATUS.open;
  return <span className={'badge ' + s.cls}><i className={'bi ' + s.icon} style={{ fontSize:10, marginRight:3 }}></i>{s[lang] || s.vi}</span>;
}
function PriorityBadge({ priority }){
  const { lang } = useLang(); const p = SUPPORT_PRIORITIES[priority] || SUPPORT_PRIORITIES.med;
  return <span className={'badge ' + p.cls}>{p[lang] || p.vi}</span>;
}
function ChannelTag({ channel }){
  const { lang } = useLang(); const ch = SUPPORT_CHANNELS[channel] || SUPPORT_CHANNELS.qr;
  return <span className="flex g4 items-center subtle" style={{ fontSize:'var(--fs-base)' }}><i className={'bi ' + ch.icon}></i>{ch[lang] || ch.vi}</span>;
}

// 4-step status flow tracker
function TicketFlow({ status }){
  const { lang } = useLang();
  const cur = SUPPORT_FLOW.indexOf(status);
  return (
    <div className="flex items-center" style={{ gap:0, overflow:'auto', padding:'2px 0' }}>
      {SUPPORT_FLOW.map((st, i) => {
        const s = SUPPORT_STATUS[st]; const done = i < cur; const active = i === cur;
        return (
          <React.Fragment key={st}>
            <div className="flex g8 items-center" style={{ flex:'none' }}>
              <div style={{ width:30, height:30, borderRadius:'50%', display:'grid', placeItems:'center', flex:'none',
                background:active ? 'var(--primary)' : done ? 'var(--success)' : 'var(--bg-subtle)', color:active || done ? '#fff' : 'var(--text-muted)', border:'2px solid ' + (active || done ? 'transparent' : 'var(--border)') }}>
                <i className={'bi ' + (done ? 'bi-check-lg' : s.icon)} style={{ fontSize:13 }}></i>
              </div>
              <span style={{ fontWeight:active ? 600 : 500, color:active ? 'var(--text)' : 'var(--text-muted)', fontSize:'var(--fs-base)', whiteSpace:'nowrap' }}>{s[lang] || s.vi}</span>
            </div>
            {i < SUPPORT_FLOW.length - 1 && <div style={{ flex:'1 1 24px', minWidth:24, height:2, margin:'0 10px', background:i < cur ? 'var(--success)' : 'var(--border)' }}></div>}
          </React.Fragment>
        );
      })}
    </div>
  );
}

function SupportModule(){
  const p = usePortal();
  const { t } = useLang();
  const sub = p.route.sub || 'overview';
  const items = [
    { id:'overview', icon:'bi-pie-chart', label:t('c.overview') },
    { id:'board', icon:'bi-kanban', label:t('sp.board') },
    { id:'list', icon:'bi-list-ul', label:t('sp.tickets'), count:p.tickets.length },
    { id:'qr', icon:'bi-qr-code', label:t('qr.tab') },
    { 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:'support', sub:id, ...extra });
  return (<>
    <ModuleSidebar app={SUPPORT_APP} items={items} active={sub === 'detail' ? 'list' : sub} onNavigate={go} />
    <main className="main">
      {sub === 'overview' && <SpOverview go={go} />}
      {sub === 'board' && <SpBoard go={go} />}
      {sub === 'list' && <SpList go={go} />}
      {sub === 'detail' && <SpDetail code={p.route.id} go={go} />}
      {sub === 'qr' && <SpQR go={go} />}
      {sub === 'report' && <SpReport />}
      {sub === 'settings' && <SpSettings />}
      {sub === 'help' && <ModuleHelp moduleId="support" app={SUPPORT_APP} />}
    </main>
  </>);
}

function SpOverview({ go }){
  const p = usePortal();
  const { t, lang } = useLang();
  const ts = p.tickets;
  const openCount = ts.filter(t2 => t2.status === 'open' || t2.status === 'process').length;
  const overdue = ts.filter(t2 => ticketSla(t2).overdue).length;
  const byStatus = SUPPORT_FLOW.map(st => ({ st, n:ts.filter(t2 => t2.status === st).length }));
  const byChan = {}; ts.forEach(t2 => { byChan[t2.channel] = (byChan[t2.channel] || 0) + 1; });
  const colors = ['var(--info)', 'var(--warning)', '#7A5AF8', 'var(--success)', 'var(--gray-400)'];
  const recent = [...ts].sort((a, b) => a.updatedDate < b.updatedDate ? 1 : -1).slice(0, 5);
  return (
    <div className="page pagefade">
      <div className="page-head"><div><h1>{t('sp.title')}</h1><div className="sub">{t('sp.subtitle')}</div></div>
        <button className="btn btn-primary" onClick={() => p.quick('support')}><i className="bi bi-plus-lg"></i>{t('sp.new')}</button></div>
      <div className="kpi-strip">
        <Kpi label={t('sp.tickets')} value={ts.length} icon="bi-headset" />
        <Kpi label={t('sp.open')} value={openCount} icon="bi-inbox" />
        <Kpi label={t('sp.overdue')} value={overdue} icon="bi-alarm" deltaDir={overdue ? 'down' : 'up'} />
        <Kpi label={t('qr.title')} value={supportableContracts(p.contracts).length} icon="bi-qr-code" />
      </div>
      <div className="dash-grid">
        <div className="card span2"><div className="card-head"><h3>{lang === 'vi' ? 'Ticket gần đây' : 'Recent tickets'}</h3><button className="btn btn-ghost btn-sm" onClick={() => go('list')}>{t('c.viewAll')}</button></div><div className="card-body">
          {recent.map(t2 => (
            <div key={t2.code} className="list-row" style={{ cursor:'pointer' }} onClick={() => go('detail', { id:t2.code })}>
              <span className="mono" style={{ fontWeight:600, color:'var(--accent)', fontSize:'var(--fs-base)', width:104 }}>{t2.code}</span>
              <span className="grow truncate">{t2.title}</span>
              <PriorityBadge priority={t2.priority} /><TicketStatusBadge status={t2.status} />
            </div>
          ))}
          {!recent.length && <EmptyState text={t('sp.empty')} />}
        </div></div>
        <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={byStatus.map((r, i) => ({ label:SUPPORT_STATUS[r.st][lang], value:r.n, color:colors[i % colors.length] }))} />
          <div className="hr" style={{ margin:'12px 0' }}></div>
          <h3 style={{ fontSize:'var(--fs-base)', marginBottom:8 }}>{lang === 'vi' ? 'Theo kênh' : 'By channel'}</h3>
          <DistribBars rows={Object.entries(byChan).sort((a, b) => b[1] - a[1]).map(([k, v], i) => ({ label:SUPPORT_CHANNELS[k][lang], value:v, color:colors[i % colors.length] }))} />
        </div></div>
      </div>
    </div>
  );
}

function SpBoard({ go }){
  const p = usePortal();
  const { t, lang } = useLang();
  const ts = p.tickets;
  return (
    <div className="page pagefade">
      <div className="page-head"><div><h1>{t('sp.board')}</h1><div className="sub">{lang === 'vi' ? 'Theo quy trình: Tiếp nhận → Xử lý → Kết quả → Báo cáo.' : 'Open → Process → Result → Report.'}</div></div>
        <button className="btn btn-primary" onClick={() => p.quick('support')}><i className="bi bi-plus-lg"></i>{t('sp.new')}</button></div>
      <div className="flex g12 items-start" style={{ overflowX:'auto', paddingBottom:8 }}>
        {SUPPORT_FLOW.map(st => {
          const col = ts.filter(t2 => t2.status === st);
          const s = SUPPORT_STATUS[st];
          return (
            <div key={st} style={{ flex:'1 1 250px', minWidth:250, background:'var(--bg-subtle)', borderRadius:'var(--r-lg)', border:'1px solid var(--border)', padding:10 }}>
              <div className="flex justify-between items-center" style={{ marginBottom:10, padding:'0 2px' }}>
                <span className="flex g6 items-center" style={{ fontWeight:600 }}><i className={'bi ' + s.icon} style={{ color:'var(--primary)' }}></i>{s[lang]}</span>
                <span className="badge badge-neutral">{col.length}</span>
              </div>
              <div className="col g8" style={{ display:'flex', flexDirection:'column', gap:8 }}>
                {col.map(t2 => { const sla = ticketSla(t2); const cust = CustomerStore.byCode(t2.customer) || {}; return (
                  <div key={t2.code} className="card" style={{ cursor:'pointer', padding:0 }} onClick={() => go('detail', { id:t2.code })}>
                    <div className="card-body" style={{ padding:'10px 12px' }}>
                      <div className="flex justify-between items-center" style={{ marginBottom:5 }}>
                        <span className="mono" style={{ fontWeight:600, color:'var(--accent)', fontSize:'var(--fs-sm)' }}>{t2.code}</span>
                        <PriorityBadge priority={t2.priority} />
                      </div>
                      <div style={{ fontWeight:500, fontSize:'var(--fs-base)', marginBottom:6, lineHeight:1.35 }}>{t2.title}</div>
                      <div className="flex justify-between items-center">
                        <span className="flex g6 items-center subtle" style={{ fontSize:'var(--fs-sm)' }}><Avatar name={cust.shortName || t2.customer} size={16} />{cust.shortName || t2.customer}</span>
                        {sla.overdue && <span className="badge badge-error" style={{ height:18 }}><i className="bi bi-alarm" style={{ fontSize:9, marginRight:2 }}></i>{t('sp.overdue')}</span>}
                      </div>
                    </div>
                  </div>
                ); })}
                {!col.length && <div className="subtle" style={{ textAlign:'center', padding:'16px 0', fontSize:'var(--fs-sm)' }}>—</div>}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

function SpList({ go }){
  const p = usePortal();
  const { t, lang } = useLang();
  const [q, setQ] = React.useState('');
  const [status, setStatus] = React.useState('all');
  const [prio, setPrio] = React.useState('all');
  const srt = useTableSort();
  const list = srt.sortRows(p.tickets.filter(t2 => {
    const cust = CustomerStore.byCode(t2.customer) || {};
    return (status === 'all' || t2.status === status) && (prio === 'all' || t2.priority === prio) &&
      (!q || (t2.code + t2.title + (cust.name || '') + (t2.contract || '')).toLowerCase().includes(q.toLowerCase()));
  }).sort((a, b) => a.updatedDate < b.updatedDate ? 1 : -1),
    { code:x => x.code, title:x => x.title, cust:x => { const c = CustomerStore.byCode(x.customer) || {}; return c.shortName || c.name || x.customer; },
      contract:x => x.contract, channel:x => (SUPPORT_CHANNELS[x.channel] || {})[lang] || x.channel,
      prio:x => ({ urgent:0, high:1, med:2, low:3 })[x.priority] ?? 9, updated:x => x.updatedDate, status:x => SUPPORT_FLOW.indexOf(x.status) });
  return (
    <div className="page pagefade">
      <div className="page-head"><div><h1>{t('sp.tickets')}</h1><div className="sub">{list.length} {lang === 'vi' ? 'ticket' : 'tickets'}</div></div>
        <button className="btn btn-primary" onClick={() => p.quick('support')}><i className="bi bi-plus-lg"></i>{t('sp.new')}</button></div>
      <div className="toolbar">
        <div className="topsearch" style={{ width:220, 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', ...SUPPORT_FLOW].map(s => <button key={s} className={status === s ? 'is-active' : ''} onClick={() => setStatus(s)}>{s === 'all' ? t('c.all') : SUPPORT_STATUS[s][lang]}</button>)}
        </div>
        <select className="select" style={{ width:'auto' }} value={prio} onChange={e => setPrio(e.target.value)}>
          <option value="all">{t('sp.priority')}: {t('c.all')}</option>
          {Object.entries(SUPPORT_PRIORITIES).map(([k, v]) => <option key={k} value={k}>{v[lang]}</option>)}
        </select>
      </div>
      <div className="card"><div style={{ overflow:'auto' }}>
        <table className="tbl">
          <thead><tr><ThSort k="code" sort={srt}>{t('sp.code')}</ThSort><ThSort k="title" sort={srt}>{t('sp.titleField')}</ThSort><ThSort k="cust" sort={srt}>{t('sp.customer')}</ThSort><ThSort k="contract" sort={srt}>{t('sp.contract')}</ThSort><ThSort k="channel" sort={srt}>{t('sp.channel')}</ThSort><ThSort k="prio" sort={srt}>{t('sp.priority')}</ThSort><ThSort k="updated" sort={srt}>{t('sp.updated')}</ThSort><ThSort k="status" sort={srt}>{t('c.status')}</ThSort></tr></thead>
          <tbody>
            {list.map(t2 => { const cust = CustomerStore.byCode(t2.customer) || {}; const sla = ticketSla(t2); return (
              <tr key={t2.code} onClick={() => go('detail', { id:t2.code })}>
                <td><span className="mono" style={{ fontWeight:600, color:'var(--accent)' }}>{t2.code}</span></td>
                <td><div style={{ fontWeight:500 }}>{t2.title}</div><div className="subtle" style={{ fontSize:'var(--fs-sm)' }}>{supportCatLabel(t2.category, lang)}{sla.overdue ? ' · ' : ''}{sla.overdue && <span style={{ color:'var(--error)' }}>{t('sp.overdue')}</span>}</div></td>
                <td><span className="flex g8 items-center"><Avatar name={cust.shortName || t2.customer} size={20} />{cust.shortName || cust.name || t2.customer}</span></td>
                <td>{t2.contract ? <span className="mono" style={{ color:'var(--primary)', fontWeight:600, fontSize:'var(--fs-base)' }}>{t2.contract}</span> : <span className="subtle">—</span>}</td>
                <td><ChannelTag channel={t2.channel} /></td>
                <td><PriorityBadge priority={t2.priority} /></td>
                <td className="mono muted" style={{ fontSize:'var(--fs-base)' }}>{fmtDate(t2.updatedDate)}</td>
                <td><TicketStatusBadge status={t2.status} /></td>
              </tr>
            ); })}
          </tbody>
        </table>
        {!list.length && <EmptyState text={t('sp.empty')} />}
      </div></div>
    </div>
  );
}

function SpDetail({ code, go }){
  const p = usePortal();
  const { t, lang } = useLang();
  const toast = useToast();
  const t2 = p.tickets.find(x => x.code === code);
  const [note, setNote] = React.useState('');
  const [resultText, setResultText] = React.useState('');
  if (!t2) return <div className="page"><EmptyState text={t('sp.empty')} /></div>;
  const cust = CustomerStore.byCode(t2.customer) || {};
  const contact = t2.contact ? (ContactStore.byCode(t2.contact) || {}) : {};
  const contract = t2.contract ? p.contracts.find(c => c.code === t2.contract) : null;
  const sla = ticketSla(t2);
  const idx = SUPPORT_FLOW.indexOf(t2.status);
  const nextStatus = SUPPORT_FLOW[idx + 1];

  const patch = (changes, evt) => {
    const next = { ...t2, ...changes, updatedDate:TODAY };
    if (evt) next.interactions = [...(t2.interactions || []), evt];
    p.patchTicket(t2.code, next);
  };
  const addNote = () => { if (!note.trim()) return; patch({}, ev(TODAY, p.me.name, note.trim(), 'note')); setNote(''); toast(lang === 'vi' ? 'Đã thêm ghi chú' : 'Note added', 'success'); };
  const advance = () => {
    if (!nextStatus) return;
    const label = SUPPORT_STATUS[nextStatus][lang];
    const evt = ev(TODAY, p.me.name, (lang === 'vi' ? 'Chuyển trạng thái sang ' : 'Moved to ') + label, 'status');
    const changes = { status:nextStatus };
    if (nextStatus === 'result') changes.result = { date:TODAY, by:p.me.name, summary:resultText.trim() || (lang === 'vi' ? 'Đã xử lý xong yêu cầu.' : 'Issue resolved.') };
    if (nextStatus === 'report') changes.report = { date:TODAY, to:t2.customer, summary:lang === 'vi' ? 'Đã gửi báo cáo & biên bản hỗ trợ cho khách hàng.' : 'Support report sent to customer.' };
    patch(changes, evt);
    toast((lang === 'vi' ? 'Đã chuyển sang ' : 'Moved to ') + label, 'success');
    setResultText('');
  };
  const reopen = () => { patch({ status:'open', result:null, report:null }, ev(TODAY, p.me.name, lang === 'vi' ? 'Mở lại ticket' : 'Ticket reopened', 'status')); toast(lang === 'vi' ? 'Đã mở lại' : 'Reopened'); };

  const evIcon = { create:'bi-plus-circle', assign:'bi-person-check', note:'bi-chat-left-text', status:'bi-arrow-right-circle' };
  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>
          <div><h1 style={{ fontSize:'var(--fs-2xl)' }}>{t2.title}</h1>
            <div className="sub flex g8 items-center wrap"><span className="mono" style={{ color:'var(--accent)', fontWeight:600 }}>{t2.code}</span> · <TicketStatusBadge status={t2.status} /> · <PriorityBadge priority={t2.priority} /> · <ChannelTag channel={t2.channel} /></div></div>
        </div>
        <div className="flex g8">
          {nextStatus && <button className="btn btn-primary" onClick={advance}><i className={'bi ' + SUPPORT_STATUS[nextStatus].icon}></i>{t('sp.advanceTo')}: {SUPPORT_STATUS[nextStatus][lang]}</button>}
          {t2.status === 'report' && <button className="btn" onClick={reopen}><i className="bi bi-arrow-counterclockwise"></i>{t('sp.reopen')}</button>}
        </div>
      </div>

      <div className="card" style={{ marginBottom:14 }}><div className="card-body"><TicketFlow status={t2.status} /></div></div>

      <div className="split" style={{ marginBottom:14 }}>
        <div className="col g12" style={{ display:'flex', flexDirection:'column', gap:12 }}>
          <div className="card"><div className="card-head"><h3 style={{ fontSize:'var(--fs-md)' }}>{t('sp.desc')}</h3></div><div className="card-body">
            <p style={{ margin:0, lineHeight:1.6 }}>{t2.desc}</p>
          </div></div>

          {t2.result && <div className="card" style={{ borderColor:'color-mix(in srgb, #7A5AF8 30%, transparent)' }}><div className="card-head"><h3 style={{ fontSize:'var(--fs-md)' }}><i className="bi bi-clipboard2-check" style={{ marginRight:6, color:'#7A5AF8' }}></i>{t('sp.result')}</h3><span className="subtle mono" style={{ fontSize:'var(--fs-sm)' }}>{fmtDate(t2.result.date)} · {t2.result.by}</span></div><div className="card-body"><p style={{ margin:0, lineHeight:1.6 }}>{t2.result.summary}</p></div></div>}
          {t2.report && <div className="card" style={{ borderColor:'color-mix(in srgb, var(--success) 30%, transparent)', background:'var(--success-bg)' }}><div className="card-head"><h3 style={{ fontSize:'var(--fs-md)' }}><i className="bi bi-file-earmark-bar-graph" style={{ marginRight:6, color:'var(--success)' }}></i>{t('sp.report')}</h3><span className="subtle mono" style={{ fontSize:'var(--fs-sm)' }}>{fmtDate(t2.report.date)}</span></div><div className="card-body"><p style={{ margin:0, lineHeight:1.6 }}>{t2.report.summary}</p></div></div>}

          {nextStatus === 'result' && <div className="card"><div className="card-head"><h3 style={{ fontSize:'var(--fs-md)' }}>{t('sp.markResult')}</h3></div><div className="card-body col g8">
            <textarea className="textarea" rows={2} value={resultText} onChange={e => setResultText(e.target.value)} placeholder={lang === 'vi' ? 'Tóm tắt kết quả xử lý (tùy chọn)…' : 'Result summary (optional)…'}></textarea>
          </div></div>}

          <div className="card"><div className="card-head"><h3 style={{ fontSize:'var(--fs-md)' }}>{t('sp.interactions')}</h3></div><div className="card-body">
            <div className="col" style={{ display:'flex', flexDirection:'column' }}>
              {[...(t2.interactions || [])].reverse().map((iv, i) => (
                <div key={i} className="flex g12" style={{ paddingBottom:14, position:'relative' }}>
                  <div style={{ display:'flex', flexDirection:'column', alignItems:'center', flex:'none' }}>
                    <div style={{ width:26, height:26, borderRadius:'50%', display:'grid', placeItems:'center', background:'var(--primary-light)', color:'var(--primary)', flex:'none' }}><i className={'bi ' + (evIcon[iv.type] || 'bi-dot')} style={{ fontSize:12 }}></i></div>
                    {i < t2.interactions.length - 1 && <div style={{ width:2, flex:1, background:'var(--border)', marginTop:2 }}></div>}
                  </div>
                  <div style={{ flex:1, paddingTop:2 }}>
                    <div className="flex g8 items-center" style={{ marginBottom:2 }}><span style={{ fontWeight:600, fontSize:'var(--fs-base)' }}>{iv.by}</span><span className="subtle mono" style={{ fontSize:'var(--fs-sm)' }}>{fmtDate(iv.date)}</span></div>
                    <div className="subtle" style={{ fontSize:'var(--fs-base)', lineHeight:1.5 }}>{iv.note}</div>
                  </div>
                </div>
              ))}
            </div>
            <div className="flex g8" style={{ marginTop:4 }}>
              <input className="input" value={note} onChange={e => setNote(e.target.value)} placeholder={t('sp.addNote') + '…'} onKeyDown={e => { if (e.key === 'Enter') addNote(); }} />
              <button className="btn btn-primary" onClick={addNote}><i className="bi bi-send"></i></button>
            </div>
          </div></div>
        </div>

        <div className="col g12" style={{ display:'flex', flexDirection:'column', gap:12 }}>
          <div className="card"><div className="card-head"><h3 style={{ fontSize:'var(--fs-md)' }}>{lang === 'vi' ? 'Thông tin' : 'Details'}</h3></div><div className="card-body">
            <div className="det-row"><span className="dk">{t('sp.customer')}</span><span className="dv link" style={{ cursor:'pointer', color:'var(--primary)' }} onClick={() => p.navigate({ name:'module', module:'customers', sub:'detail', id:t2.customer })}>{cust.name || t2.customer}</span></div>
            <div className="det-row"><span className="dk">{t('sp.contract')}</span><span className="dv">{contract ? <span className="mono link" style={{ cursor:'pointer', color:'var(--primary)', fontWeight:600 }} onClick={() => p.navigate({ name:'module', module:'contracts', sub:'detail', id:contract.code })}>{contract.code}</span> : <span className="subtle">{t('sp.noContract')}</span>}</span></div>
            <div className="det-row"><span className="dk">{t('sp.contact')}</span><span className="dv">{contact.name || '—'}</span></div>
            <div className="det-row"><span className="dk">{t('sp.category')}</span><span className="dv">{supportCatLabel(t2.category, lang)}</span></div>
            <div className="det-row"><span className="dk">{t('sp.assignee')}</span><span className="dv flex g6 items-center"><Avatar name={t2.assignee} size={20} />{t2.assignee}</span></div>
            <div className="det-row"><span className="dk">{t('sp.created')}</span><span className="dv mono">{fmtDate(t2.createdDate)}</span></div>
            <div className="det-row"><span className="dk">{t('sp.sla')}</span><span className="dv mono" style={{ color:sla.overdue ? 'var(--error)' : 'var(--text)' }}>{fmtDate(sla.due)}{sla.overdue ? ' · ' + t('sp.overdue') : ''}</span></div>
          </div></div>
          {contract && <ContractQRCard c={contract} compact />}
        </div>
      </div>
    </div>
  );
}

function SpQR({ go }){
  const p = usePortal();
  const { t, lang } = useLang();
  const [q, setQ] = React.useState('');
  const [show, setShow] = React.useState('active');
  const list = supportableContracts(p.contracts).filter(c => {
    const info = qrInfoOf(c);
    const party = contractParty(c);
    return (show === 'all' || (show === 'active' ? info.active : info.expired)) &&
      (!q || (c.code + (party.name || '') + (party.shortName || '')).toLowerCase().includes(q.toLowerCase()));
  });
  return (
    <div className="page pagefade">
      <div className="page-head"><div><h1>{t('qr.tab')}</h1><div className="sub">{t('qr.desc')}</div></div></div>
      <div className="card" style={{ marginBottom:14, background:'var(--primary-light)', borderColor:'color-mix(in srgb, var(--primary) 24%, transparent)' }}><div className="card-body flex g10 items-start" style={{ fontSize:'var(--fs-base)' }}>
        <i className="bi bi-info-circle" style={{ color:'var(--primary)', marginTop:2, fontSize:15 }}></i>
        <span>{lang === 'vi' ? 'Mỗi hợp đồng bán có một QR định danh để khách hàng quét và gửi ticket hỗ trợ. QR tự hết hạn khi hợp đồng hết hạn; khi gia hạn hợp đồng, mã định danh gắn theo mã hợp đồng được giữ nguyên.' : 'Each sell contract carries an identity QR for customers to submit tickets. It expires with the contract and is preserved across renewals.'}</span>
      </div></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">
          {[['active', t('qr.active')], ['expired', t('qr.expired')], ['all', t('c.all')]].map(([k, lbl]) => <button key={k} className={show === k ? 'is-active' : ''} onClick={() => setShow(k)}>{lbl}</button>)}
        </div>
      </div>
      <div className="dash-grid">
        {list.map(c => <ContractQRCard key={c.code} c={c} />)}
      </div>
      {!list.length && <div className="card"><EmptyState text={lang === 'vi' ? 'Không có hợp đồng phù hợp' : 'No matching contracts'} /></div>}
    </div>
  );
}

function SpSettings(){
  const { t, lang } = useLang();
  return (
    <div className="page pagefade">
      <div className="page-head"><div><h1>{t('c.settings')}</h1><div className="sub">{lang === 'vi' ? 'Quy tắc SLA & loại ticket.' : 'SLA rules & categories.'}</div></div></div>
      <div className="split">
        <div className="card"><div className="card-head"><h3>{lang === 'vi' ? 'SLA theo mức ưu tiên' : 'SLA by priority'}</h3></div><div className="card-body">
          {Object.entries(SUPPORT_PRIORITIES).map(([k, v]) => (
            <div key={k} className="det-row"><span className="dk"><PriorityBadge priority={k} /></span><span className="dv mono">{v.slaH}h {lang === 'vi' ? 'xử lý' : 'to resolve'}</span></div>
          ))}
        </div></div>
        <div className="card"><div className="card-head"><h3>{lang === 'vi' ? 'Loại ticket' : 'Categories'}</h3></div><div className="card-body flex g8 wrap">
          {SUPPORT_CATEGORIES.map(c => <span key={c.id} className="badge badge-neutral" style={{ height:26 }}>{c[lang]}</span>)}
        </div></div>
      </div>
    </div>
  );
}

/* New-ticket modal (quick action target) */
function genTicketCode(list){
  const yr = TODAY.slice(0, 4);
  const max = list.reduce((m, t2) => { const mm = (t2.code || '').match(new RegExp('SP-' + yr + '-(\\d+)')); return mm ? Math.max(m, +mm[1]) : m; }, 0);
  return 'SP-' + yr + '-' + String(max + 1).padStart(3, '0');
}
function NewTicketModal({ onClose, presetContract, presetCustomer }){
  const p = usePortal();
  const { t, lang } = useLang();
  const toast = useToast();
  const code = genTicketCode(p.tickets);
  const preset = presetContract ? p.contracts.find(c => c.code === presetContract) : null;
  const [f, setF] = React.useState({
    customer:preset ? preset.customer : (presetCustomer || (p.customers[0] ? p.customers[0].code : '')),
    contract:preset ? preset.code : '',
    title:'', desc:'', channel:'qr', category:'su-co', priority:'med', assignee:p.me.name,
  });
  const set = (k, v) => setF(s => ({ ...s, [k]:v }));
  const custContracts = supportableContracts(p.contracts).filter(c => c.customer === f.customer);
  const onCustomer = (code2) => setF(s => ({ ...s, customer:code2, contract:'' }));
  const create = () => {
    if (!f.title.trim()) { toast(lang === 'vi' ? 'Nhập tiêu đề' : 'Enter a title', 'error'); return; }
    if (!f.customer) { toast(lang === 'vi' ? 'Chọn khách hàng' : 'Select a customer', 'error'); return; }
    const rec = { code, customer:f.customer, contract:f.contract || null, contact:null, channel:f.channel, category:f.category, priority:f.priority,
      status:'open', title:f.title.trim(), desc:f.desc.trim(), assignee:f.assignee, createdDate:TODAY, updatedDate:TODAY,
      interactions:[ ev(TODAY, p.me.name, lang === 'vi' ? 'Tạo ticket trên hệ thống.' : 'Ticket created.', 'create') ] };
    p.addTicket(rec);
    toast((lang === 'vi' ? 'Đã tạo ticket ' : 'Created ') + code, 'success');
    onClose();
    p.navigate({ name:'module', module:'support', sub:'detail', id:code });
  };
  return (
    <Modal title={t('sp.new')} width={640} onClose={onClose}
      footer={<><button className="btn" onClick={onClose}>{t('c.cancel')}</button><button className="btn btn-primary" onClick={create}>{t('c.create')}</button></>}>
      <div className="col g16">
        <div className="flex g12 items-center" style={{ padding:'2px 0' }}><span className="subtle">{t('sp.code')}:</span><span className="mono" style={{ fontWeight:600, color:'var(--accent)' }}>{code}</span><span className="badge badge-neutral">{lang === 'vi' ? 'tự sinh' : 'auto'}</span></div>
        <Field label={t('sp.titleField')}><input className="input" autoFocus value={f.title} onChange={e => set('title', e.target.value)} placeholder={lang === 'vi' ? 'VD: Lỗi đồng bộ dữ liệu sau cập nhật' : 'e.g. Sync error after update'} /></Field>
        <Field label={t('sp.desc')}><textarea className="textarea" rows={3} value={f.desc} onChange={e => set('desc', e.target.value)} placeholder={lang === 'vi' ? 'Mô tả chi tiết yêu cầu của khách…' : 'Describe the request…'}></textarea></Field>
        <div className="flex g12 wrap">
          <div style={{ flex:'1 1 220px' }}><Field label={t('sp.customer')}><select className="select" value={f.customer} onChange={e => onCustomer(e.target.value)}>{p.customers.map(c => <option key={c.code} value={c.code}>{c.code} · {c.name}</option>)}</select></Field></div>
          <div style={{ flex:'1 1 200px' }}><Field label={t('sp.contract')}><select className="select" value={f.contract} onChange={e => set('contract', e.target.value)}>
            <option value="">— {t('sp.noContract')} —</option>
            {custContracts.map(c => <option key={c.code} value={c.code}>{c.code} · {catLabel(c.category, lang)}</option>)}
          </select></Field></div>
        </div>
        <div className="flex g12 wrap">
          <div style={{ flex:'1 1 150px' }}><Field label={t('sp.channel')}><select className="select" value={f.channel} onChange={e => set('channel', e.target.value)}>{Object.entries(SUPPORT_CHANNELS).map(([k, v]) => <option key={k} value={k}>{v[lang]}</option>)}</select></Field></div>
          <div style={{ flex:'1 1 150px' }}><Field label={t('sp.category')}><select className="select" value={f.category} onChange={e => set('category', e.target.value)}>{SUPPORT_CATEGORIES.map(c => <option key={c.id} value={c.id}>{c[lang]}</option>)}</select></Field></div>
          <div style={{ flex:'1 1 130px' }}><Field label={t('sp.priority')}><select className="select" value={f.priority} onChange={e => set('priority', e.target.value)}>{Object.entries(SUPPORT_PRIORITIES).map(([k, v]) => <option key={k} value={k}>{v[lang]}</option>)}</select></Field></div>
          <div style={{ flex:'1 1 160px' }}><Field label={t('sp.assignee')}><select className="select" value={f.assignee} onChange={e => set('assignee', e.target.value)}>{Object.values(USERS).map((u, i) => <option key={i}>{u.name}</option>)}</select></Field></div>
        </div>
      </div>
    </Modal>
  );
}

/* Dashboard widget */
function SpReport(){
  const p = usePortal();
  const { t, lang } = useLang();
  const toast = useToast();
  const stLbl = st => (SUPPORT_STATUS[st] ? (SUPPORT_STATUS[st][lang] || SUPPORT_STATUS[st].vi) : st);
  const period = useReportPeriod('thisyear');
  const months = monthsInPeriod(period);
  const all = p.tickets.filter(t2 => inPeriod(t2.createdDate, period));
  const openCount = all.filter(t2 => t2.status === 'open' || t2.status === 'process').length;
  const done = all.filter(t2 => t2.status === 'result' || t2.status === 'report').length;
  const overdue = all.filter(t2 => ticketSla(t2).overdue).length;
  const cntByMonth = tallyByMonth(all, t2 => t2.createdDate);
  const statusSlices = SUPPORT_FLOW.map((st, i) => ({ label:stLbl(st), value:all.filter(t2 => t2.status === st).length, color:REPORT_COLORS[i % REPORT_COLORS.length] })).filter(s => s.value > 0);
  const byAssignee = {}; all.forEach(t2 => { byAssignee[t2.assignee] = (byAssignee[t2.assignee] || 0) + 1; });
  const rankRows = Object.entries(byAssignee).sort((a, b) => b[1] - a[1]).slice(0, 8).map(([a, n]) => ({ label:a, value:n }));
  const excel = () => exportReportXLSX([{
    name:lang === 'vi' ? 'Ticket hỗ trợ' : 'Support tickets',
    header:[lang === 'vi' ? 'Mã ticket' : 'Code', lang === 'vi' ? 'Khách hàng' : 'Customer', lang === 'vi' ? 'Tiêu đề' : 'Title', lang === 'vi' ? 'Nhóm' : 'Category', lang === 'vi' ? 'Ưu tiên' : 'Priority', lang === 'vi' ? 'Trạng thái' : 'Status', lang === 'vi' ? 'Người xử lý' : 'Assignee', lang === 'vi' ? 'Ngày tạo' : 'Created'],
    widths:[16, 20, 40, 14, 10, 12, 20, 12],
    rows:all.map(t2 => { const cu = CustomerStore.byCode(t2.customer) || {}; const pr = SUPPORT_PRIORITIES[t2.priority] || {}; return [t2.code, cu.shortName || cu.name || t2.customer, t2.title, supportCatLabel(t2.category, lang), pr[lang] || pr.vi || t2.priority, stLbl(t2.status), t2.assignee, t2.createdDate]; }),
  }], 'bao-cao-ho-tro_' + period.from + '_' + period.to, toast, lang);

  return (
    <ReportShell title={t('c.report') + ' · ' + t('sp.title')} subtitle={lang === 'vi' ? 'Ticket hỗ trợ tạo trong kỳ.' : 'Support tickets created in period.'} period={period} onExcel={excel}>
      <div className="kpi-strip">
        <Kpi label={lang === 'vi' ? 'Ticket tạo' : 'Created'} value={all.length} icon="bi-headset" />
        <Kpi label={t('sp.open')} value={openCount} icon="bi-inboxes" />
        <Kpi label={lang === 'vi' ? 'Đã xử lý' : 'Resolved'} value={done} icon="bi-check2-circle" />
        <Kpi label={t('sp.overdue')} value={overdue} icon="bi-alarm" />
      </div>
      {all.length === 0 ? <div className="card"><div className="card-body"><EmptyState icon="bi-calendar-x" text={lang === 'vi' ? 'Không có ticket nào trong kỳ này' : 'No tickets in this period'} /></div></div> : <>
      <div className="dash-grid" style={{ marginBottom:14 }}>
        <div className="card span2"><div className="card-head"><h3>{lang === 'vi' ? 'Ticket tạo theo tháng' : 'Tickets created by month'}</h3></div><div className="card-body"><ReportBars months={months} series={cntByMonth} color="#0FB0AB" /></div></div>
        <div className="card"><div className="card-head"><h3>{lang === 'vi' ? 'Cơ cấu theo trạng thái' : 'By status'}</h3></div><div className="card-body"><ReportDonut slices={statusSlices} /></div></div>
      </div>
      <div className="dash-grid" style={{ marginBottom:14 }}>
        <div className="card span2"><div className="card-head"><h3>{lang === 'vi' ? 'Xu hướng số ticket' : 'Tickets — 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 người xử lý' : 'Top assignees'}</h3></div><div className="card-body"><ReportRank rows={rankRows} /></div></div>
      </div>
      <div className="card"><div className="card-head"><h3>{lang === 'vi' ? 'Chi tiết ticket' : 'Ticket detail'}</h3><span className="badge badge-neutral">{all.length}</span></div><div style={{ overflow:'auto' }}>
        <table className="tbl"><thead><tr><th>{lang === 'vi' ? 'Mã' : 'Code'}</th><th>{lang === 'vi' ? 'Khách hàng' : 'Customer'}</th><th>{lang === 'vi' ? 'Tiêu đề' : 'Title'}</th><th>{lang === 'vi' ? 'Ưu tiên' : 'Priority'}</th><th>{lang === 'vi' ? 'Trạng thái' : 'Status'}</th><th>{lang === 'vi' ? 'Ngày tạo' : 'Created'}</th></tr></thead>
          <tbody>{all.slice().sort((a, b) => String(b.createdDate).localeCompare(String(a.createdDate))).map(t2 => { const cu = CustomerStore.byCode(t2.customer) || {}; return (
            <tr key={t2.code}><td><span className="mono" style={{ fontWeight:600, color:'var(--accent)' }}>{t2.code}</span></td><td>{cu.shortName || cu.name || t2.customer}</td><td className="truncate" style={{ maxWidth:280 }}>{t2.title}</td><td><PriorityBadge priority={t2.priority} /></td><td><TicketStatusBadge status={t2.status} /></td><td className="mono">{t2.createdDate}</td></tr>
          ); })}</tbody></table>
      </div></div>
      </>}
    </ReportShell>
  );
}

function SupportWidget(){
  const p = usePortal();
  const { t } = useLang();
  const ts = p.tickets;
  const openCount = ts.filter(t2 => t2.status === 'open' || t2.status === 'process').length;
  const overdue = ts.filter(t2 => ticketSla(t2).overdue).length;
  const recent = [...ts].sort((a, b) => a.updatedDate < b.updatedDate ? 1 : -1).slice(0, 3);
  return (
    <WidgetShell wid="support" icon="bi-headset" iconColor={SUPPORT_APP.color} title={t('sp.title')}
      onViewAll={() => p.openApp(SUPPORT_APP)} onNew={() => p.quick('support')} newLabel={t('sp.new')}>
        <div className="flex g16" style={{ marginBottom:8 }}>
          <div><div className="k-val" style={{ fontSize:'var(--fs-2xl)', fontWeight:700 }}>{openCount}</div><div className="subtle" style={{ fontSize:'var(--fs-sm)' }}>{t('sp.open')}</div></div>
          <div><div className="k-val" style={{ fontSize:'var(--fs-2xl)', fontWeight:700, color:overdue ? 'var(--error)' : 'var(--success)' }}>{overdue}</div><div className="subtle" style={{ fontSize:'var(--fs-sm)' }}>{t('sp.overdue')}</div></div>
        </div>
        {recent.map(t2 => (
          <div key={t2.code} className="list-row" style={{ cursor:'pointer' }} onClick={() => p.navigate({ name:'module', module:'support', sub:'detail', id:t2.code })}>
            <span className="mono" style={{ fontWeight:600, color:'var(--accent)', fontSize:'var(--fs-sm)', width:96 }}>{t2.code}</span><span className="grow truncate">{t2.title}</span><TicketStatusBadge status={t2.status} />
          </div>
        ))}
    </WidgetShell>
  );
}

Object.assign(window, { SupportModule, NewTicketModal, SupportWidget, TicketStatusBadge, PriorityBadge, ChannelTag, TicketFlow, genTicketCode });
