// ===========================================================
// Unified staff workspace — "Nhân viên kinh doanh"
// Một nhân viên bán đa kênh: khách lẻ + đối tác (TikTok, nhà
// thuốc, gym, spa, đại lý...) đều nằm trong MỘT sổ khách hàng,
// chia tệp bằng bộ lọc.
// ===========================================================
const D = window.TVS_DATA;

// ----- helpers -----
const PARTNER_TYPE_SHORT = {
  'TikTok Affiliate': 'TikTok',
  'Nhà thuốc': 'Nhà thuốc',
  'Phòng gym': 'Gym',
  'PT / HLV': 'PT/HLV',
  'Spa & Nutritionist': 'Spa',
  'Doanh nghiệp (quà tặng NV)': 'Doanh nghiệp',
  'Đại lý / Phân phối': 'Đại lý',
};
const PARTNER_TYPE_VARIANT = {
  'TikTok Affiliate': 'info',
  'Nhà thuốc': 'success',
  'Phòng gym': 'warning',
  'PT / HLV': 'warning',
  'Spa & Nutritionist': 'info',
  'Doanh nghiệp (quà tặng NV)': 'platinum',
  'Đại lý / Phân phối': 'bronze',
};
function typeTag(row) {
  if (row.kind === 'b2c') return <Tag variant="outline">Khách lẻ</Tag>;
  return <Tag variant={PARTNER_TYPE_VARIANT[row.type] || 'info'}>{PARTNER_TYPE_SHORT[row.type] || row.type}</Tag>;
}

// Nhãn loại đối tác theo acquisitionChannel (khớp bootstrap-data.js / display.js).
const PARTNER_TYPE_LABEL = {
  tiktok_affiliate: 'TikTok Affiliate',
  pharmacy: 'Nhà thuốc',
  wholesale: 'Đại lý / Phân phối',
  gym: 'Phòng gym',
  pt: 'PT / HLV',
  spa: 'Spa & Nutritionist',
  enterprise: 'Doanh nghiệp (quà tặng NV)',
};
// Phân loại đối tác (lọc theo kênh) — nhóm khách Sapo lấy động từ D.customerGroups.
const PARTNER_CLASSIFY = [
  { channel: 'wholesale', label: 'Đại lý' },
  { channel: 'tiktok_affiliate', label: 'TikTok' },
  { channel: 'pharmacy', label: 'Nhà thuốc' },
  { channel: 'gym', label: 'Gym' },
  { channel: 'pt', label: 'PT/HLV' },
  { channel: 'spa', label: 'Spa' },
  { channel: 'enterprise', label: 'Doanh nghiệp' },
];

// Dựng object đối tác đầy đủ cho PartnerDrawer từ 1 dòng khách (ưu tiên bản trong D.partners).
function toPartner(c) {
  const found = (D.partners || []).find((x) => x.id === c.id);
  if (found) return found;
  return {
    id: c.id, name: c.name, type: PARTNER_TYPE_LABEL[c._channel] || c.partnerType || c.group || 'Đối tác',
    contact: c.name, phone: c.phone, email: c.email || '', city: c.city || '',
    tier: c.tier, stage: 'Prospect', gmv30d: 0, gmvYTD: c.ltv || 0,
    commission: 0, commissionEarned: 0, priceList: 'Theo thỏa thuận',
    nextAction: { type: 'Chăm sóc định kỳ', due: D.today },
  };
}

// Build the unified "sổ khách hàng" for one staff member
function buildBook(myId, myName) {
  const b2c = D.customers.map(c => ({
    kind: 'b2c', key: c.code || c.id, name: c.name, phone: c.phone, tier: c.tier,
    type: 'Khách lẻ', value: c.ltv, last: 'Mua ' + fmt.rel(c.lastOrder),
    due: c.followUp ? c.followUp.due : null,
    dueReason: c.followUp ? c.followUp.reason : null,
    owner: c._owner || window.ownerOf(c.id), ownerId: c._ownerId, raw: c,
  }));
  const partners = D.partners.map(p => ({
    kind: 'partner', key: p.id, name: p.name, phone: p.phone, tier: p.tier,
    type: p.type, value: p.gmvYTD || p.gmv30d, stage: p.stage,
    last: p.gmv30d > 0 ? 'GMV 30N ' + fmt.vnd(p.gmv30d) : 'Chưa có doanh số',
    due: p.nextAction ? p.nextAction.due : null,
    dueReason: p.nextAction ? p.nextAction.type : null,
    owner: p.owner, ownerId: p.ownerId, raw: p,
  }));
  const all = [...b2c, ...partners];
  const mine = all.filter(r => r.ownerId === myId || (!r.ownerId && r.owner === myName));
  const tasks = mine.filter(r => r.due).sort((a, b) => new Date(a.due) - new Date(b.due));
  const tasksToday = tasks.filter(r => new Date(r.due) <= D.today);
  return { all, mine, tasks, tasksToday };
}

// ===========================================================
// STAFF DASHBOARD
// ===========================================================
function StaffDashboard({ refreshKey, role, onNavigate, onOpenCustomer, onOpenPartner, onLogOrder, onRegister }) {
  const myName = role.person;
  const firstName = myName.split(' ').slice(-1)[0];
  const book = useMemo(() => buildBook(role.id, myName), [role.id, myName, refreshKey]);
  const myPartners = book.mine.filter(r => r.kind === 'partner');
  const myB2C = book.mine.filter(r => r.kind === 'b2c');
  const gmv30 = myPartners.reduce((s, r) => s + (r.raw.gmv30d || 0), 0);
  const k = D.kpis.telesale.person;
  const [quickSearch, setQuickSearch] = useState('');
  const [activity, setActivity] = useState(null);
  useEffect(() => { if (window.TVS_API && window.TVS_API.isAuthed()) window.TVS_API.api('/api/customers/my-activity').then(r => setActivity(r.items || [])).catch(() => setActivity([])); }, [refreshKey]);

  const openRow = (r) => r.kind === 'b2c' ? onOpenCustomer(r.raw) : onOpenPartner(r.raw);

  const searchResults = quickSearch.length >= 2 ? book.all.filter(r =>
    r.name.toLowerCase().includes(quickSearch.toLowerCase()) ||
    r.phone.replace(/\s/g,'').includes(quickSearch.replace(/\s/g,'')) ||
    r.key.toLowerCase().includes(quickSearch.toLowerCase())
  ).slice(0, 5) : [];

  return (
    <>
      <div className="page-header">
        <div>
          <div className="eyebrow">{role.team}</div>
          <h1>Chào {firstName} 👋</h1>
          <div className="sub">
            Bạn đang phụ trách <b style={{color:'var(--vs-dark-blue)'}}>{myB2C.length} khách lẻ</b> + <b style={{color:'var(--vs-dark-blue)'}}>{myPartners.length} đối tác</b> · <b style={{color:'var(--status-sale)'}}>{book.tasksToday.length} việc cần làm hôm nay</b>
          </div>
        </div>
        <div className="row gap-2">
          <button className="btn btn-secondary" onClick={onRegister}><Icon name="plus" size={14}/>Thêm khách</button>
          <button className="btn btn-cta" onClick={() => onNavigate('telesale')}><Icon name="phone" size={14}/>Console Gọi</button>
        </div>
      </div>

      {/* Hero — tra cứu nhanh cả khách lẻ lẫn đối tác */}
      <Card className="mb-5" style={{background:'linear-gradient(135deg, var(--vs-dark-blue) 0%, #01337f 100%)',color:'#fff',border:'none',position:'relative',overflow:'hidden'}}>
        <div style={{position:'absolute',right:-40,top:-40,width:240,height:240,background:'radial-gradient(circle,rgba(65,182,230,0.30) 0%,transparent 60%)',borderRadius:'50%'}}/>
        <div style={{position:'relative',zIndex:1}}>
          <div className="eyebrow" style={{color:'var(--vs-light-blue)',marginBottom:8}}>Tra cứu nhanh</div>
          <h2 style={{fontFamily:'var(--font-display)',fontWeight:700,fontSize:22,margin:'0 0 12px',color:'#fff'}}>Tìm khách lẻ hoặc đối tác — SĐT, tên, mã</h2>
          <div className="topbar-search" style={{background:'rgba(255,255,255,0.94)',padding:'13px 18px',fontSize:'var(--t-md)'}}>
            <Icon name="search" size={20} style={{color:'var(--vs-dark-blue)'}}/>
            <input placeholder="VD: 0903 821 447 · Pharmacity · C-10241..."
              value={quickSearch} onChange={(e)=>setQuickSearch(e.target.value)}
              style={{fontSize:'var(--t-md)',fontWeight:600}}/>
            <kbd>⌘K</kbd>
          </div>
          {searchResults.length > 0 && (
            <div className="card" style={{marginTop:12,padding:0,background:'#fff'}}>
              {searchResults.map(r => (
                <div key={r.key} onClick={() => { openRow(r); setQuickSearch(''); }}
                  style={{padding:'11px 16px',borderBottom:'1px solid var(--border-1)',display:'flex',gap:12,alignItems:'center',cursor:'pointer'}}>
                  <Avatar name={r.name} size="sm" tier={tierVariant(r.tier)}/>
                  <div style={{flex:1,minWidth:0}}>
                    <div className="strong fg-1 text-sm">{r.name}</div>
                    <div className="text-xs fg-muted tnum">{r.phone} · {r.key}</div>
                  </div>
                  {typeTag(r)}
                </div>
              ))}
            </div>
          )}
        </div>
      </Card>

      <div className="kpi-grid">
        <KPI feature label="Doanh số tháng này" value={fmt.vnd(k.sales)}
          sub="Từ đơn đã hoàn thành, khách của bạn"
          progress={Math.min(100, (k.sales/k.salesTarget)*100)}
          progressLabel={{left:fmt.vnd(k.sales), right:'Mục tiêu '+fmt.vnd(k.salesTarget)}}/>
        <KPI label="Việc hôm nay" value={book.tasksToday.length}
          sub={`${book.tasks.length - book.tasksToday.length} việc sắp tới`}/>
        <KPI label="Cuộc gọi hôm nay" value={k.calls} unit={`/ ${k.target}`} sub="Ghi nhận qua Console Gọi"
          progress={(k.calls/k.target)*100}/>
        <KPI label="GMV đối tác 30 ngày" value={fmt.vndFull(gmv30)} sub="Đối tác bạn phụ trách"/>
      </div>

      <div className="grid grid-7-5">
        {/* Việc cần làm — trộn khách lẻ + đối tác */}
        <Card pad={false}>
          <CardHeader title="Việc cần làm hôm nay" sub="Khách lẻ + đối tác, sắp xếp theo hạn"
            actions={<button className="btn btn-ghost btn-sm" onClick={() => onNavigate('customers')}>Tất cả →</button>}/>
          <div style={{padding:'var(--space-3) var(--space-5)'}}>
            {book.tasks.slice(0, 6).map(r => (
              <div key={r.key} className="activity" style={{cursor:'pointer'}} onClick={() => openRow(r)}>
                <Avatar name={r.name} size="sm" tier={tierVariant(r.tier)}/>
                <div className="body">
                  <div className="row gap-2">
                    <div className="title">{r.name}</div>
                    {typeTag(r)}
                  </div>
                  <div className="meta">{r.dueReason}</div>
                </div>
                <div className="row gap-1" onClick={(e) => e.stopPropagation()}>
                  <Tag variant={new Date(r.due) <= D.today ? 'danger' : 'warning'}>{fmt.rel(r.due)}</Tag>
                  {r.kind === 'b2c' && (
                    <button className="btn btn-icon btn-sm btn-cta" title="Gọi" onClick={() => onNavigate('telesale')}>
                      <Icon name="phone" size={13}/>
                    </button>
                  )}
                  <button className="btn btn-icon btn-sm btn-secondary" title="Ghi nhận đơn"
                    onClick={() => onLogOrder(r.kind === 'partner' ? {...r.raw, _b2b: true} : r.raw)}>
                    <Icon name="receipt" size={13}/>
                  </button>
                </div>
              </div>
            ))}
            {book.tasks.length === 0 && <div className="text-sm fg-muted" style={{padding:'var(--space-4) 0'}}>Không có việc nào — tuyệt vời!</div>}
          </div>
        </Card>

        {/* Đối tác của tôi */}
        <Card pad={false}>
          <CardHeader title="Đối tác của tôi" sub={`${myPartners.length} đối tác · GMV 30N ${fmt.vnd(gmv30)}`}/>
          <div style={{padding:'var(--space-3) var(--space-5)'}}>
            {myPartners.map(r => (
              <div key={r.key} className="activity" style={{cursor:'pointer'}} onClick={() => onOpenPartner(r.raw)}>
                <Avatar name={r.name} size="sm" tier={tierVariant(r.tier)}/>
                <div className="body">
                  <div className="row gap-2">
                    <div className="title text-sm">{r.name}</div>
                    {r.stage !== 'Active' && <Tag variant={r.stage === 'Onboarding' ? 'info' : 'warning'}>{r.stage}</Tag>}
                  </div>
                  <div className="meta">{typeTag(r)} <span style={{marginLeft:6}}>{r.last}</span></div>
                </div>
                <Icon name="chevron" size={14} style={{color:'var(--fg-muted)'}}/>
              </div>
            ))}
          </div>
        </Card>
      </div>

      <div className="grid grid-2 mt-4">
        <Card pad={false}>
          <CardHeader title="Doanh số 14 ngày qua" sub="Triệu đồng — tất cả kênh bạn phụ trách"/>
          <div className="card-pad">
            <BarChart data={D.kpis.telesale.trend} highlight={D.kpis.telesale.trend.length - 1} height={140}/>
          </div>
        </Card>
        <Card pad={false}>
          <CardHeader title="Hoạt động gần đây" sub="Sổ khách của bạn"/>
          <div style={{padding:'var(--space-2) var(--space-5)'}}>
            {activity === null && <div className="text-sm fg-muted" style={{padding:'var(--space-3) 0'}}>Đang tải…</div>}
            {activity && activity.length === 0 && <div className="text-sm fg-muted" style={{padding:'var(--space-3) 0'}}>Chưa có hoạt động nào — ghi chú, ghi nhận đơn sẽ hiện ở đây.</div>}
            {activity && activity.map((a, i) => (
              <div key={i} className="activity">
                <div className="ico"><Icon name={a.ico} size={14}/></div>
                <div className="body">
                  <div className="title text-sm">{a.t}</div>
                  <div className="meta">{fmt.rel(a.at)}</div>
                </div>
              </div>
            ))}
          </div>
        </Card>
      </div>
    </>
  );
}

// ===========================================================
// UNIFIED CUSTOMERS — sổ khách hàng gộp khách lẻ + đối tác
// ===========================================================
function UnifiedCustomersScreen({ refreshKey, role, isManager, onOpenCustomer, onOpenPartner, onGoToTelesale, onLogOrder, onAddCustomer, onAddPartner }) {
  const CustomerPipeline = window.CustomerPipeline;
  const live = window.TVS_API && window.TVS_API.isAuthed();
  const [view, setView] = useState('list');
  const groups = D.customerGroups || [];

  // Bộ lọc server-side
  const [q, setQ] = useState('');
  const [groupId, setGroupId] = useState('');
  const [channel, setChannel] = useState(''); // acquisitionChannel — lọc phân loại đối tác
  const [showCreateGroup, setShowCreateGroup] = useState(false);
  const [tier, setTier] = useState('');
  const [boughtDays, setBoughtDays] = useState('');
  const [notBoughtDays, setNotBoughtDays] = useState('');
  const [minOrders, setMinOrders] = useState('');
  const [source, setSource] = useState(''); // '' | 'sapo' | 'crm'
  const [sort, setSort] = useState('recent');
  const [page, setPage] = useState(1);
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);
  const pageSize = 25;

  // Assign hàng loạt (admin/manager/cửa hàng)
  const canAssign = live && isManager; // chỉ admin phân bổ khách được
  const [selected, setSelected] = useState(() => new Set());
  const [assignees, setAssignees] = useState([]);
  const [assignToMultiple, setAssignToMultiple] = useState([]);
  const [assignBusy, setAssignBusy] = useState(false);
  const [mergeModalTarget, setMergeModalTarget] = useState(null);
  useEffect(() => { if (canAssign) window.TVS_API.assignable().then(r => setAssignees(r.users || [])).catch(()=>{}); }, [canAssign]);

  const [pendingMergeCount, setPendingMergeCount] = useState(0);
  useEffect(() => {
    if (live) {
      window.TVS_API.api('/api/reports/overview?days=7')
        .then(r => {
          if (r && r.pendingMerges != null) {
            setPendingMergeCount(r.pendingMerges);
          }
        })
        .catch(() => {});
    }
  }, [live]);

  const setFilter = (setter) => (v) => { setter(v); setPage(1); };

  useEffect(() => {
    if (!live) { // fallback demo
      setData({ customers: D.customers, total: D.customers.length, pages: 1, page: 1 });
      return;
    }
    setLoading(true);
    const p = new URLSearchParams({ page: String(page), pageSize: String(pageSize), sort });
    if (q.trim()) p.set('q', q.trim());
    if (groupId) p.set('groupId', groupId);
    if (channel) p.set('channel', channel);
    if (tier) p.set('tier', tier);
    if (boughtDays) p.set('boughtWithinDays', boughtDays);
    if (notBoughtDays) p.set('notBoughtSinceDays', notBoughtDays);
    if (minOrders) p.set('minOrders', minOrders);
    if (source) p.set('source', source);
    const t = setTimeout(() => {
      window.TVS_API.api('/api/customers/list?' + p.toString())
        .then(r => { setData(r); setLoading(false); })
        .catch(() => { setLoading(false); });
    }, q ? 300 : 0);
    return () => clearTimeout(t);
  }, [q, groupId, channel, tier, boughtDays, notBoughtDays, minOrders, source, sort, page, refreshKey]);

  const customers = data ? data.customers : [];
  const total = data ? data.total : 0;
  const pages = data ? (data.pages || 1) : 1;

  // Bỏ chọn khi đổi trang/bộ lọc
  useEffect(() => { setSelected(new Set()); }, [page, q, groupId, channel, tier, boughtDays, minOrders, sort]);
  const toggleSel = (id) => setSelected(s => { const n = new Set(s); n.has(id) ? n.delete(id) : n.add(id); return n; });
  const allOnPageSelected = customers.length > 0 && customers.every(c => selected.has(c.id));
  const toggleSelAll = () => setSelected(s => {
    const n = new Set(s);
    if (allOnPageSelected) customers.forEach(c => n.delete(c.id));
    else customers.forEach(c => n.add(c.id));
    return n;
  });
  const doBulkAssign = async () => {
    if (!selected.size) return;
    setAssignBusy(true);
    try {
      const r = await window.TVS_API.api('/api/customers/bulk-owner', { method:'PATCH', body: JSON.stringify({ customerIds: [...selected], ownerUserIds: assignToMultiple }) });
      window.tvsToast && window.tvsToast(`Đã giao ${r.assigned} khách thành công!`);
      setSelected(new Set()); setAssignToMultiple([]); setPage(p => p); // trigger reload
      // reload danh sách
      const p = new URLSearchParams({ page: String(page), pageSize: String(pageSize), sort });
      if (q.trim()) p.set('q', q.trim()); if (groupId) p.set('groupId', groupId); if (tier) p.set('tier', tier);
      if (boughtDays) p.set('boughtWithinDays', boughtDays); if (notBoughtDays) p.set('notBoughtSinceDays', notBoughtDays); if (minOrders) p.set('minOrders', minOrders);
      window.TVS_API.api('/api/customers/list?' + p.toString()).then(setData).catch(()=>{});
    } catch (e) { window.tvsToast && window.tvsToast('Lỗi giao khách: ' + e.message); }
    setAssignBusy(false);
  };

  // Nút follow-up: chưa có -> tạo (hôm nay); đã có -> đánh dấu xong (bỏ).
  const toggleFollow = async (c) => {
    if (!live) return;
    try {
      if (c.followUpId) {
        await window.TVS_API.api('/api/followups/' + c.followUpId + '/done', { method:'PATCH', body:'{}' });
        setData(d => ({ ...d, customers: d.customers.map(x => x.id===c.id?{...x,followUp:null,followUpId:null}:x) }));
        window.tvsToast && window.tvsToast('Đã bỏ follow-up');
      } else {
        const r = await window.TVS_API.api('/api/followups', { method:'POST', body: JSON.stringify({ customerId: c.id, bucket:'today', reason:'Cần chăm sóc' }) });
        setData(d => ({ ...d, customers: d.customers.map(x => x.id===c.id?{...x,followUp:{due:null,reason:'Cần chăm sóc'},followUpId:r.followUp.id}:x) }));
        window.tvsToast && window.tvsToast('Đã thêm vào Follow-up');
      }
    } catch (e) { window.tvsToast && window.tvsToast('Lỗi: ' + e.message); }
  };

  const getOwnerNames = (c) => {
    if (!c._collaboratorIds) return c._owner || '—';
    const ids = c._collaboratorIds.split(',').map(s => s.trim()).filter(Boolean);
    const names = ids.map(id => {
      const u = assignees.find(x => x.id === id);
      return u ? u.name : null;
    }).filter(Boolean);
    if (!names.length) return c._owner || '—';
    return names.join(' + ');
  };

  const toggleSort = (key) => {
    if (key === 'name') {
      setSort(sort === 'name' ? 'recent' : 'name');
    } else if (key === 'ltv') {
      setSort(sort === 'ltv' ? 'recent' : 'ltv');
    } else if (key === 'orders') {
      setSort(sort === 'orders' ? 'recent' : 'orders');
    } else if (key === 'recent_or_oldest') {
      setSort(sort === 'recent' ? 'longest_unbought' : 'recent');
    }
  };

  const renderSortIcon = (key) => {
    let active = false;
    let desc = true;
    if (key === 'name' && sort === 'name') { active = true; desc = false; }
    if (key === 'ltv' && sort === 'ltv') { active = true; desc = true; }
    if (key === 'orders' && sort === 'orders') { active = true; desc = true; }
    if (key === 'recent_or_oldest') {
      if (sort === 'recent') { active = true; desc = true; }
      else if (sort === 'longest_unbought') { active = true; desc = false; }
    }
    
    if (!active) return <span style={{marginLeft:4, opacity:0.35, fontSize:11}}>↕</span>;
    return <span style={{marginLeft:4, color:'var(--vs-light-blue)', fontSize:11}}>{desc ? '↓' : '↑'}</span>;
  };

  // Thêm khách vào Pipeline (lead) — chủ yếu dùng ở tab "Khách hàng từ CRM".
  const [pipelinedIds, setPipelinedIds] = useState(() => new Set());
  const addToPipeline = async (c) => {
    if (!live) return;
    try {
      await window.TVS_API.api('/api/pipeline', { method:'POST', body: JSON.stringify({ customerId: c.id, stage:'lead' }) });
      setPipelinedIds(s => new Set(s).add(c.id));
      window.tvsToast && window.tvsToast('Đã thêm vào Pipeline');
    } catch (e) { window.tvsToast && window.tvsToast('Lỗi: ' + e.message); }
  };

  // Danh sách userId đang phụ trách 1 khách (hỗ trợ nhiều người).
  const ownerIdsOf = (c) => c._collaboratorIds
    ? c._collaboratorIds.split(',').map(s => s.trim()).filter(Boolean)
    : (c._ownerId ? [c._ownerId] : []);

  // Đổi nhanh người phụ trách ngay trên list (không cần mở chi tiết) — có thể chọn NHIỀU người.
  const quickAssign = async (c, ids) => {
    if (!live) return;
    const list = (ids || []).filter(Boolean);
    const prev = { _ownerId: c._ownerId, _owner: c._owner, _collaboratorIds: c._collaboratorIds };
    const names = assignees.filter(u => list.includes(u.id)).map(u => u.name);
    // cập nhật lạc quan
    setData(d => ({ ...d, customers: d.customers.map(x => x.id === c.id ? { ...x, _ownerId: list[0] || null, _owner: names[0] || '', _collaboratorIds: list.length ? list.join(', ') : null } : x) }));
    try {
      await window.TVS_API.api('/api/customers/' + c.id + '/owner', { method: 'PATCH', body: JSON.stringify({ ownerUserIds: list }) });
      window.tvsToast && window.tvsToast(list.length ? `Đã giao cho ${names.join(' + ')}` : 'Đã bỏ phân công');
    } catch (e) {
      setData(d => ({ ...d, customers: d.customers.map(x => x.id === c.id ? { ...x, ...prev } : x) }));
      window.tvsToast && window.tvsToast('Lỗi: ' + e.message);
    }
  };

  const timeOptions = [{value:'',label:'Tất cả'},{value:'7',label:'7 ngày'},{value:'30',label:'30 ngày'},{value:'60',label:'60 ngày'},{value:'180',label:'180 ngày'}];

  // Mở hồ sơ: đối tác -> PartnerDrawer (có Hoa hồng & HĐ); khách lẻ -> Customer360.
  const openRow = (c) => {
    if (c._isPartner && onOpenPartner) onOpenPartner(toPartner(c));
    else onOpenCustomer(c);
  };

  // Phân loại đang chọn: '' (tất cả) | 'g:<groupId>' | 'c:<channel>'
  const classifyValue = channel ? 'c:' + channel : groupId ? 'g:' + groupId : '';
  const selectClassify = (val) => {
    if (!val) { setFilter(setGroupId)(''); setChannel(''); }
    else if (val.startsWith('g:')) { setFilter(setGroupId)(val.slice(2)); setChannel(''); setPage(1); }
    else if (val.startsWith('c:')) { setChannel(val.slice(2)); setFilter(setGroupId)(''); setPage(1); }
  };

  return (
    <>
      <div className="page-header">
        <div>
          <div className="eyebrow">{role.team}</div>
          <h1>{isManager ? 'Khách hàng — toàn hệ thống' : 'Khách hàng của tôi'}</h1>
          <div className="sub">
            <b style={{color:'var(--vs-dark-blue)'}}>{total.toLocaleString('vi-VN')} khách</b> khớp bộ lọc · đồng bộ trực tiếp từ Sapo
          </div>
        </div>
        <div className="row gap-2">
          <div className="row gap-1" style={{background:'var(--bg-soft)', padding:4, borderRadius:10, border:'1px solid var(--border-1)'}}>
            {[
              {value:'list',     label:'Khách mua theo thời gian thực', icon:'cart'},
              {value:'crm',      label:'Khách hàng từ CRM', icon:'database'},
              {value:'followups',label:'Lịch Follow-up', icon:'flag'},
              {value:'pipeline', label:'Pipeline', icon:'target'},
            ].map(opt => {
              const isActive = view === opt.value;
              return (
                <button
                  key={opt.value}
                  onClick={()=>{ setView(opt.value); setFilter(setSource)(opt.value==='crm'?'crm':''); }}
                  style={{
                    padding: '10px 20px',
                    fontSize: 14,
                    fontWeight: isActive ? 800 : 600,
                    letterSpacing: isActive ? '-0.01em' : '0',
                    borderRadius: 7,
                    background: isActive ? 'var(--vs-dark-blue)' : 'transparent',
                    color: isActive ? 'white' : 'var(--fg-2)',
                    border: 'none',
                    boxShadow: isActive ? '0 2px 10px rgba(0,64,160,0.25)' : 'none',
                    display: 'inline-flex',
                    alignItems: 'center',
                    gap: 8,
                    transition: 'all 0.15s ease',
                    cursor: 'pointer',
                    whiteSpace: 'nowrap'
                  }}
                >
                  <Icon name={opt.icon} size={15} style={{opacity: isActive ? 1 : 0.6}}/>
                  {opt.label}
                </button>
              );
            })}
          </div>
          {canAssign && (
            <button className="btn btn-secondary" onClick={() => setShowCreateGroup(true)} title="Tạo nhóm khách hàng">
              <Icon name="plus" size={14}/>Tạo nhóm
            </button>
          )}
          <button className="btn btn-secondary" onClick={onAddPartner}><Icon name="handshake" size={14}/>Thêm đối tác</button>
          <button className="btn btn-cta" onClick={onAddCustomer}><Icon name="plus" size={14}/>Thêm khách</button>
        </div>
      </div>

      {pendingMergeCount > 0 && (
        <div className="banner danger mb-4" style={{display:'flex', alignItems:'center', gap:'var(--space-3)', padding:'12px 16px', borderRadius:'var(--radius-md)', background:'rgba(255,75,75,0.08)', border:'1px solid rgba(255,75,75,0.22)', margin:'0 var(--space-4)'}}>
          <div style={{color:'var(--status-sale)', display:'flex', alignItems:'center'}}><Icon name="warning" size={18}/></div>
          <div className="text-sm fg-1" style={{color:'var(--fg-1)'}}>
            Phát hiện <b>{pendingMergeCount} khách hàng trùng SĐT</b> chờ gộp từ Sapo. Cần liên kết/đồng bộ để gộp dữ liệu.
          </div>
          <button className="btn btn-cta btn-sm" style={{background:'var(--status-sale)', color:'white'}} onClick={() => { setView('crm'); setFilter(setSource)('crm'); }}>
            Xem danh sách trùng cần gộp
          </button>
        </div>
      )}

      <div className="kpi-grid">
        <KPI feature label="Tổng khách (bộ lọc)" value={total.toLocaleString('vi-VN')} sub="Nguồn Sapo"/>
        <KPI label="Nhóm khách" value={groups.length} sub={groups.slice(0,2).map(g=>g.name).join(', ')}/>
        <KPI label="Sản phẩm" value={(D.products||[]).length} sub="Đang bán"/>
        <KPI label="Cửa hàng" value={(D.stores||[]).length} sub="Chi nhánh"/>
      </div>

      {(view === 'list' || view === 'crm') && (
      <Card pad={false}>
        {view === 'crm' && (
          <div className="banner" style={{margin:'var(--space-3) var(--space-5) 0'}}>
            <Icon name="users" size={14}/>
            <span className="text-sm">Khách nhập tay ở CRM hoặc import Excel — chưa/đang chờ khớp với Sapo. Có thể Follow-up hoặc thêm vào Pipeline ngay.</span>
          </div>
        )}
        {/* Bộ lọc */}
        <div style={{padding:'var(--space-3) var(--space-5)',display:'flex',gap:'var(--space-3)',alignItems:'center',borderBottom:'1px solid var(--border-1)',flexWrap:'wrap'}}>
          <div className="topbar-search" style={{flex:'0 1 240px'}}>
            <Icon name="search" size={14} style={{color:'var(--fg-muted)'}}/>
            <input placeholder="Tên, SĐT, mã..." value={q} onChange={(e)=>setFilter(setQ)(e.target.value)}/>
          </div>
          <div className="row gap-1"><span className="text-xs fg-muted">Hạng:</span>
            <select className="select select-sm" style={{height:30, padding:'0 8px', fontSize:13, background:'var(--bg-soft)'}} value={tier} onChange={e=>setFilter(setTier)(e.target.value)}>
              <option value="">Tất cả</option>
              <option value="platinum">Bạch kim</option>
              <option value="gold">Vàng</option>
              <option value="silver">Bạc</option>
              <option value="bronze">Đồng</option>
            </select>
          </div>
          <div className="row gap-1"><span className="text-xs fg-muted">Mua trong:</span>
            <select className="select select-sm" style={{height:30, padding:'0 8px', fontSize:13, background:'var(--bg-soft)'}} value={['','7','30','60','180'].includes(boughtDays)?boughtDays:'custom'} onChange={e=> {
              const v = e.target.value;
              setFilter(setBoughtDays)(v==='custom'?boughtDays||'90':v);
              if (v) setFilter(setNotBoughtDays)('');
            }}>
              <option value="">Tất cả</option>
              <option value="7">7 ngày</option>
              <option value="30">30 ngày</option>
              <option value="60">60 ngày</option>
              <option value="180">180 ngày</option>
              <option value="custom">Tùy chọn...</option>
            </select>
            {!['','7','30','60','180'].includes(boughtDays) && (
              <input className="input tnum" style={{width:65, height:30, padding:'0 8px', fontSize:13}} type="number" min="1" placeholder="ngày" value={boughtDays} onChange={e=>setFilter(setBoughtDays)(e.target.value)} title="Số ngày tùy chọn"/>
            )}
          </div>
          <div className="row gap-1"><span className="text-xs fg-muted">Rời bỏ/Chưa mua sau:</span>
            <select className="select select-sm" style={{height:30, padding:'0 8px', fontSize:13, background:'var(--bg-soft)'}} value={notBoughtDays} onChange={e=> {
              const v = e.target.value;
              setFilter(setNotBoughtDays)(v);
              if (v) setFilter(setBoughtDays)('');
            }}>
              <option value="">Tất cả</option>
              <option value="30">30 ngày</option>
              <option value="60">60 ngày</option>
              <option value="180">180 ngày</option>
              <option value="360">360 ngày</option>
            </select>
          </div>
          <button className={minOrders? 'btn btn-cta btn-sm':'btn btn-ghost btn-sm'} title="Chỉ khách đã quay lại mua (≥2 đơn)" onClick={()=>setFilter(setMinOrders)(minOrders?'':'2')}>
            <Icon name="refresh" size={12}/>Khách quay lại
          </button>
          <div className="row gap-1" style={{marginLeft:'auto'}}><span className="text-xs fg-muted">Sắp xếp:</span>
            <select className="select select-sm" style={{height:30, padding:'0 8px', fontSize:13, background:'var(--bg-soft)'}} value={sort} onChange={e=>setSort(e.target.value)}>
              <option value="recent">Mua gần nhất</option>
              <option value="longest_unbought">Chưa mua lâu nhất (Rời bỏ)</option>
              <option value="ltv">Doanh số</option>
              <option value="orders">Số đơn</option>
              <option value="name">Tên</option>
            </select>
          </div>
        </div>
        {/* Phân loại — gộp Nhóm khách (Sapo) + Loại đối tác B2B. Không áp dụng cho tab CRM. */}
        {view === 'list' && (
        <div style={{padding:'var(--space-3) var(--space-5)',display:'flex',gap:'var(--space-2)',alignItems:'center',borderBottom:'1px solid var(--border-1)',overflowX:'auto'}}>
          <span className="text-xs fg-muted" style={{flexShrink:0}}>Phân loại:</span>
          <button className={!classifyValue?'btn btn-cta btn-sm':'btn btn-ghost btn-sm'} style={{flexShrink:0}} onClick={()=>selectClassify('')}>Tất cả</button>
          {groups.map(g => (
            <button key={g.id} style={{flexShrink:0}} className={classifyValue==='g:'+g.id?'btn btn-cta btn-sm':'btn btn-ghost btn-sm'} onClick={()=>selectClassify('g:'+g.id)}>
              {g.name} <span className="tnum" style={{opacity:0.7}}>{g.count.toLocaleString('vi-VN')}</span>
            </button>
          ))}
          <span style={{flexShrink:0,width:1,height:20,background:'var(--border-2)',margin:'0 2px'}}/>
          {PARTNER_CLASSIFY.map(pt => (
            <button key={pt.channel} style={{flexShrink:0}} className={classifyValue==='c:'+pt.channel?'btn btn-cta btn-sm':'btn btn-ghost btn-sm'} onClick={()=>selectClassify('c:'+pt.channel)}>
              <Icon name="handshake" size={11}/>{pt.label}
            </button>
          ))}
        </div>
        )}
        {/* Thanh assign hàng loạt */}
        {canAssign && selected.size > 0 && (
          <div style={{padding:'var(--space-3) var(--space-5)',display:'flex',gap:'var(--space-4)',alignItems:'center',borderBottom:'1px solid var(--border-1)',background:'rgba(65,182,230,0.08)',flexWrap:'wrap'}}>
            <span className="strong text-sm" style={{color:'var(--vs-dark-blue)'}}>Đã chọn {selected.size} khách</span>
            <div className="row gap-3" style={{alignItems:'center'}}>
              <span className="text-xs fg-muted">Giao cho các nhân viên:</span>
              {assignees.map(u => (
                <label key={u.id} className="row gap-1 style-none" style={{cursor:'pointer', display:'flex', alignItems:'center'}}>
                  <input type="checkbox"
                    checked={assignToMultiple.includes(u.id)}
                    onChange={(e) => {
                      if (e.target.checked) setAssignToMultiple([...assignToMultiple, u.id]);
                      else setAssignToMultiple(assignToMultiple.filter(id => id !== u.id));
                    }}
                    style={{accentColor:'var(--vs-light-blue)', width:16, height:16}}/>
                  <span className="text-sm fg-1" style={{marginLeft:4}}>{u.name}</span>
                </label>
              ))}
            </div>
            <div className="row gap-2" style={{marginLeft:'auto'}}>
              <button className="btn btn-cta btn-sm" disabled={assignBusy} onClick={doBulkAssign}>{assignBusy?'Đang lưu…':'Lưu phân công'}</button>
              <button className="btn btn-ghost btn-sm" onClick={()=>{ setSelected(new Set()); setAssignToMultiple([]); }}>Bỏ chọn</button>
            </div>
          </div>
        )}
        <div className="table-wrap">
          <table className="tbl">
            <thead>
              <tr>
                {canAssign && <th style={{width:36}}><input type="checkbox" checked={allOnPageSelected} onChange={toggleSelAll} title="Chọn cả trang"/></th>}
                <th style={{cursor:'pointer'}} onClick={() => toggleSort('name')}>Tên {renderSortIcon('name')}</th>
                <th>Nhóm</th>
                <th>SĐT</th>
                <th className="num" style={{cursor:'pointer'}} onClick={() => toggleSort('ltv')}>Doanh số {renderSortIcon('ltv')}</th>
                <th className="num" style={{cursor:'pointer'}} onClick={() => toggleSort('orders')}>Đơn {renderSortIcon('orders')}</th>
                <th style={{cursor:'pointer'}} onClick={() => toggleSort('recent_or_oldest')}>Mua gần nhất {renderSortIcon('recent_or_oldest')}</th>
                <th>Follow-up</th>
                <th>Phụ trách</th>
                <th style={{textAlign:'right'}}>Hành động</th>
              </tr>
            </thead>
            <tbody>
              {loading && <tr><td colSpan={canAssign?10:9}><div className="text-sm fg-muted" style={{padding:'var(--space-4)'}}>Đang tải…</div></td></tr>}
              {!loading && customers.map(c => (
                <tr key={c.id} onClick={() => openRow(c)} className={selected.has(c.id)?'row-selected':''}>
                  {canAssign && <td onClick={(e)=>e.stopPropagation()}><input type="checkbox" checked={selected.has(c.id)} onChange={()=>toggleSel(c.id)}/></td>}
                  <td>
                    <div className="row gap-3">
                      <Avatar name={c.name} tier={tierVariant(c.tier)}/>
                      <div>
                        <div className="row gap-2" style={{alignItems:'center'}}>
                          <span className="strong">{c.name}</span>
                          {c._isPartner && <Tag variant="info">Đối tác</Tag>}
                        </div>
                        <div className="text-xs fg-muted">{c._isPartner && c.partnerType ? c.partnerType + ' · ' : ''}{c.code}</div>
                      </div>
                    </div>
                  </td>
                  <td>{c.group ? <Tag variant="outline">{c.group}</Tag> : <span className="text-xs fg-muted">—</span>}</td>
                  <td className="tnum text-sm">{c.phone}</td>
                  <td className="num strong">{c.ltv > 0 ? fmt.vndFull(c.ltv) : '—'}</td>
                  <td className="num tnum">{c.orders || 0}</td>
                  <td className="text-sm fg-muted">{c.lastOrder ? fmt.rel(c.lastOrder) : 'Chưa mua'}</td>
                  <td onClick={(e)=>e.stopPropagation()}>
                    <button className={c.followUpId ? 'btn btn-sm btn-cta' : 'btn btn-sm btn-ghost'} title={c.followUpId?'Bỏ follow-up':'Thêm follow-up'} onClick={()=>toggleFollow(c)}>
                      <Icon name={c.followUpId?'check':'flag'} size={12}/>{c.followUpId?'Đang follow':'Follow'}
                    </button>
                  </td>
                  <td onClick={(e) => e.stopPropagation()}>
                    {canAssign ? (
                      <OwnerPicker currentIds={ownerIdsOf(c)} assignees={assignees} onChange={(ids) => quickAssign(c, ids)} fallbackLabel={c._owner || ''}/>
                    ) : (
                      <span className="text-xs fg-2" style={{fontWeight:500}}>{getOwnerNames(c) || 'Chưa phân công'}</span>
                    )}
                  </td>
                  <td onClick={(e) => e.stopPropagation()} style={{textAlign:'right'}}>
                    <div className="row gap-1" style={{justifyContent:'flex-end'}}>
                      {view === 'crm' && (
                        <>
                          <button
                            className={c._sapoId ? "btn btn-sm" : "btn btn-sm btn-secondary"}
                            style={{
                              background: c._sapoId ? 'rgba(6,167,125,0.12)' : 'var(--bg-soft)',
                              color: c._sapoId ? 'var(--status-success)' : 'var(--status-sale)',
                              borderColor: c._sapoId ? 'rgba(6,167,125,0.25)' : 'rgba(255,75,75,0.22)',
                              fontSize: 11,
                              padding: '2px 8px',
                              display: 'inline-flex',
                              alignItems: 'center',
                              gap: 4
                            }}
                            onClick={(ev) => {
                              ev.stopPropagation();
                              if (c._sapoId) return;
                              setMergeModalTarget(c);
                            }}
                          >
                            <Icon name={c._sapoId ? "check" : "warning"} size={11}/>
                            {c._sapoId ? "Đã khớp Sapo" : "Chưa khớp với sapo"}
                          </button>
                        </>
                      )}
                      <button className="btn btn-icon btn-sm btn-secondary" title={pipelinedIds.has(c.id) ? 'Đã thêm vào Pipeline' : 'Thêm vào Pipeline'} disabled={pipelinedIds.has(c.id)} onClick={() => addToPipeline(c)}>
                        <Icon name={pipelinedIds.has(c.id) ? 'check' : 'target'} size={13}/>
                      </button>
                      <button className="btn btn-icon btn-sm btn-cta" title="Gọi" onClick={() => onGoToTelesale && onGoToTelesale(c)}><Icon name="phone" size={13}/></button>
                      <button className="btn btn-icon btn-sm btn-secondary" title="Ghi nhận đơn" onClick={() => onLogOrder(c._isPartner ? {...c, _b2b:true} : c)}><Icon name="receipt" size={13}/></button>
                      <button className="btn btn-icon btn-sm btn-ghost" title="Xem hồ sơ" onClick={() => openRow(c)}><Icon name="eye" size={13}/></button>
                    </div>
                  </td>
                </tr>
              ))}
              {!loading && customers.length === 0 && (
                <tr><td colSpan={canAssign?10:9}><div className="empty" style={{padding:'var(--space-6)'}}><div className="text-sm">Không có khách khớp bộ lọc</div></div></td></tr>
              )}
            </tbody>
          </table>
        </div>
        {/* Phân trang */}
        <div style={{padding:'var(--space-3) var(--space-5)',borderTop:'1px solid var(--border-1)',display:'flex',alignItems:'center',gap:12}}>
          <span className="text-sm fg-muted">Trang {page}/{pages} · {total.toLocaleString('vi-VN')} khách</span>
          <div className="row gap-1" style={{marginLeft:'auto'}}>
            <button className="btn btn-secondary btn-sm" disabled={page<=1} onClick={()=>setPage(p=>Math.max(1,p-1))}><Icon name="arrowleft" size={12}/>Trước</button>
            <button className="btn btn-secondary btn-sm" disabled={page>=pages} onClick={()=>setPage(p=>Math.min(pages,p+1))}>Sau<Icon name="arrowright" size={12}/></button>
          </div>
        </div>
      </Card>
      )}

      {showCreateGroup && (
        <CreateGroupModal
          selectedIds={[...selected]}
          onClose={() => setShowCreateGroup(false)}
          onCreated={async (group) => {
            setShowCreateGroup(false);
            setSelected(new Set());
            if (window.TVS_API && window.TVS_API.isAuthed()) {
              try { await window.TVS_API.refresh(); } catch (_) {}
            }
            // Nhảy tới nhóm vừa tạo
            setChannel(''); setFilter(setGroupId)(group.id);
            window.tvsRefreshUI && window.tvsRefreshUI();
            window.tvsToast && window.tvsToast(`Đã tạo nhóm "${group.name}"${group.count ? ` · ${group.count} khách` : ''}`);
          }}/>
      )}

      {view === 'followups' && <UnifiedFollowUps refreshKey={refreshKey} onOpen={(r)=>onOpenCustomer(r.raw)} onGoToTelesale={onGoToTelesale} role={role}/>}

      {view === 'pipeline' && (
        window.CustomerPipeline ? (
          <window.CustomerPipeline refreshKey={refreshKey} myName={isManager ? null : (role && role.person)} onOpenCustomer={onOpenCustomer} kind="customer" role={role}/>
        ) : (
          <Card><div className="card-pad text-sm fg-muted">Đang tải Pipeline...</div></Card>
        )
      )}

      {mergeModalTarget && (
        <div style={{
          position:'fixed', top:0, left:0, right:0, bottom:0,
          background:'rgba(0,0,0,0.5)', display:'flex', alignItems:'center',
          justifyContent:'center', zIndex:9999, padding:16
        }} onClick={() => setMergeModalTarget(null)}>
          <div style={{
            background:'#fff', borderRadius:12, padding:24,
            maxWidth:480, width:'100%', boxShadow:'0 10px 30px rgba(0,0,0,0.2)',
            display:'flex', flexDirection:'column', gap:16
          }} onClick={(e) => e.stopPropagation()}>
            <div className="row mb-1">
              <Icon name="warning" size={20} style={{color:'var(--status-sale)'}}/>
              <span className="strong fg-1" style={{fontSize:16, marginLeft:8}}>Chọn phương thức xử lý</span>
            </div>
            <p className="text-sm fg-muted" style={{margin:0, lineHeight:1.5}}>
              Khách hàng <b>{mergeModalTarget.name || mergeModalTarget.fullName}</b> ({mergeModalTarget.phone}) chưa được đồng bộ/khớp với Sapo. Bạn muốn xử lý thế nào?
            </p>
            <div className="col gap-2 mt-2">
              <button className="btn btn-cta btn-lg" style={{justifyContent:'center', display:'inline-flex', alignItems:'center', gap:8}} onClick={async () => {
                const target = mergeModalTarget;
                setMergeModalTarget(null);
                window.tvsToast && window.tvsToast('Đang kết nối & đối soát với Sapo...');
                try {
                  const r = await window.TVS_API.api(`/api/customers/${target.id}/resolve-merge`, { method: 'POST', body: JSON.stringify({ action: 'sapo' }) });
                  if (r.matched) {
                    window.tvsToast && window.tvsToast('Đồng bộ & Khớp Sapo thành công!');
                  } else {
                    alert(r.message || 'Không tìm thấy khách hàng trùng khớp trên Sapo.');
                  }
                  if (window.tvsRefreshUI) window.tvsRefreshUI();
                } catch(e) {
                  window.tvsToast && window.tvsToast('Lỗi: ' + e.message);
                }
              }}>
                <Icon name="refresh" size={16}/> Đồng bộ ngay
              </button>

              <button className="btn btn-secondary btn-lg" style={{justifyContent:'center', display:'inline-flex', alignItems:'center', gap:8}} onClick={async () => {
                const target = mergeModalTarget;
                setMergeModalTarget(null);
                window.tvsToast && window.tvsToast('Đang thiết lập khách hàng độc lập...');
                try {
                  await window.TVS_API.api(`/api/customers/${target.id}/resolve-merge`, { method: 'POST', body: JSON.stringify({ action: 'independent' }) });
                  window.tvsToast && window.tvsToast('Đã lưu thành khách độc lập CRM!');
                  if (window.tvsRefreshUI) window.tvsRefreshUI();
                } catch(e) {
                  window.tvsToast && window.tvsToast('Lỗi: ' + e.message);
                }
              }}>
                <Icon name="user" size={16}/> Giữ nguyên ở CRM
              </button>
            </div>
            <div style={{textAlign:'right', marginTop:8}}>
              <button className="btn btn-ghost btn-sm" onClick={() => setMergeModalTarget(null)}>Hủy bỏ</button>
            </div>
          </div>
        </div>
      )}
    </>
  );
}

// ===== Lịch follow-up — dữ liệu thật từ /api/followups =====
function UnifiedFollowUps({ refreshKey, onOpen, onGoToTelesale, role, kind }) {
  const live = window.TVS_API && window.TVS_API.isAuthed();
  const [customDays1, setCustomDays1] = useState(null);
  const [customDays2, setCustomDays2] = useState(null);

  const handleBucketChange = (v) => {
    if (v === 'custom') {
      const daysStr = prompt("Nhập số ngày muốn hẹn nhắc lại (ví dụ: 3, 5, 10, 30):", "3");
      if (daysStr && !isNaN(+daysStr)) {
        const days = parseInt(daysStr, 10);
        setCustomDays1(days);
        const targetDate = new Date();
        targetDate.setDate(targetDate.getDate() + days);
        targetDate.setHours(9, 0, 0, 0);
        setForm(f => ({ ...f, bucket: 'custom', dueAt: targetDate.toISOString() }));
      }
    } else {
      setForm(f => ({ ...f, bucket: v, dueAt: null }));
    }
  };

  const handleEditBucketChange = (v) => {
    if (v === 'custom') {
      const daysStr = prompt("Nhập số ngày muốn hẹn nhắc lại (ví dụ: 3, 5, 10, 30):", "3");
      if (daysStr && !isNaN(+daysStr)) {
        const days = parseInt(daysStr, 10);
        setCustomDays2(days);
        const targetDate = new Date();
        targetDate.setDate(targetDate.getDate() + days);
        targetDate.setHours(9, 0, 0, 0);
        setEditForm(f => ({ ...f, bucket: 'custom', dueAt: targetDate.toISOString() }));
      }
    } else {
      setEditForm(f => ({ ...f, bucket: v, dueAt: null }));
    }
  };
  const isAdmin = role && role.role === 'admin';
  const [data, setData] = useState(null);
  const [creating, setCreating] = useState(false);
  const [form, setForm] = useState({ q: '', customer: null, reason: '', bucket: 'today' });
  const [picks, setPicks] = useState([]);
  const [showDone, setShowDone] = useState(false);

  const [editingId, setEditingId] = useState(null);
  const [editForm, setEditForm] = useState({ reason: '', bucket: 'today' });

  const load = () => {
    if (!live) { setData({ followUps: [], overdueCount: 0 }); return; }
    const qs = kind === 'partner' ? '?kind=partner' : kind === 'customer' ? '?kind=customer' : '';
    window.TVS_API.api('/api/followups' + qs).then(setData).catch(() => setData({ followUps: [], overdueCount: 0 }));
  };
  useEffect(() => { load(); }, [kind, refreshKey]);

  const startEdit = (f) => {
    setEditingId(f.id);
    setEditForm({ reason: f.reason, bucket: 'today' });
  };

  const saveEdit = async (id) => {
    try {
      await window.TVS_API.api('/api/followups/' + id, {
        method: 'PATCH',
        body: JSON.stringify({ reason: editForm.reason, bucket: editForm.bucket, dueAt: editForm.dueAt })
      });
      setEditingId(null);
      window.tvsToast && window.tvsToast('Đã cập nhật follow-up');
      load();
    } catch (e) {
      window.tvsToast && window.tvsToast('Lỗi: ' + e.message);
    }
  };

  // tìm khách để gắn vào follow-up
  useEffect(() => {
    if (!live || form.q.trim().length < 2) { setPicks([]); return; }
    const t = setTimeout(() => {
      window.TVS_API.api('/api/customers/list?pageSize=6&q=' + encodeURIComponent(form.q.trim()))
        .then(r => setPicks(r.customers || [])).catch(() => setPicks([]));
    }, 250);
    return () => clearTimeout(t);
  }, [form.q]);

  const create = async () => {
    if (!form.customer) { window.tvsToast && window.tvsToast('Chọn khách hàng trước'); return; }
    try {
      await window.TVS_API.api('/api/followups', { method: 'POST', body: JSON.stringify({ customerId: form.customer.id, reason: form.reason || 'Chăm sóc khách', bucket: form.bucket, dueAt: form.dueAt }) });
      setCreating(false); setForm({ q: '', customer: null, reason: '', bucket: 'today' }); setPicks([]);
      window.tvsToast && window.tvsToast('Đã tạo follow-up'); load();
    } catch (e) { window.tvsToast && window.tvsToast('Lỗi: ' + e.message); }
  };
  const markDone = async (id) => {
    setData(d => ({ ...d, followUps: d.followUps.map(f => f.id === id ? { ...f, status: 'done' } : f) }));
    try { await window.TVS_API.api('/api/followups/' + id + '/done', { method: 'PATCH', body: '{}' }); } catch (e) { load(); }
  };

  if (data === null) return <Card><div className="text-sm fg-muted">Đang tải follow-up…</div></Card>;

  const pending = data.followUps.filter(f => f.status === 'pending');
  const now = new Date();
  const bucketOf = (f) => {
    if (!f.dueAt) return 'later';
    const diff = Math.round((new Date(f.dueAt) - new Date(now.toDateString())) / 86400000);
    if (new Date(f.dueAt) < now && diff < 0) return 'overdue';
    if (diff <= 0) return 'today';
    if (diff === 1) return 'tomorrow';
    if (diff <= 7) return 'week';
    return 'later';
  };
  const groups = { overdue: [], today: [], tomorrow: [], week: [], later: [] };
  pending.forEach(f => groups[bucketOf(f)].push(f));
  const done = (data.followUps || []).filter(f => f.status === 'done');
  const sections = [
    {key:'overdue', label:'Quá hạn',    variant:'danger'},
    {key:'today',   label:'Hôm nay',    variant:'danger'},
    {key:'tomorrow',label:'Ngày mai',   variant:'warning'},
    {key:'week',    label:'7 ngày tới', variant:'info'},
    {key:'later',   label:'Sau đó',     variant:'outline'},
  ];

  return (
    <Card pad={false}>
      <CardHeader title="Lịch follow-up" sub={`${pending.length} việc đang mở · ${data.overdueCount} quá hạn`}
        actions={<button className="btn btn-cta btn-sm" onClick={() => setCreating(c => !c)}><Icon name="plus" size={12}/>Tạo follow-up</button>}/>

      {creating && (
        <div className="card-pad" style={{background:'var(--bg-soft)',borderBottom:'1px solid var(--border-1)'}}>
          <div className="field-label">Khách hàng</div>
          {form.customer ? (
            <div className="row gap-2 mb-2"><Tag variant="info">{form.customer.name} · {form.customer.phone}</Tag>
              <button className="btn btn-ghost btn-sm" onClick={()=>setForm(f=>({...f,customer:null,q:''}))}>Đổi</button></div>
          ) : (
            <div style={{position:'relative'}}>
              <input className="input mb-2" placeholder="Tìm tên / SĐT khách..." value={form.q} onChange={e=>setForm(f=>({...f,q:e.target.value}))}/>
              {picks.length>0 && (
                <div className="card" style={{position:'absolute',zIndex:5,left:0,right:0,padding:0,maxHeight:200,overflow:'auto'}}>
                  {picks.map(c=>(
                    <div key={c.id} style={{padding:'8px 12px',cursor:'pointer',borderBottom:'1px solid var(--border-1)'}} onClick={()=>setForm(f=>({...f,customer:c,q:'',}))}>
                      <div className="text-sm strong">{c.name}</div><div className="text-xs fg-muted tnum">{c.phone} · {c.code}</div>
                    </div>
                  ))}
                </div>
              )}
            </div>
          )}
          <div className="field-label">Nội dung công việc</div>
          <input className="input mb-2" placeholder="VD: Gọi tư vấn combo, gửi bảng giá..." value={form.reason} onChange={e=>setForm(f=>({...f,reason:e.target.value}))}/>
          <div className="field-label">Thời gian</div>
          <Segmented value={form.bucket} options={[{value:'today',label:'Hôm nay'},{value:'tomorrow',label:'Ngày mai'},{value:'7days',label:'7 ngày tới'},{value:'custom',label: customDays1 ? `Sau ${customDays1} ngày` : 'Sau X ngày'}]} onChange={handleBucketChange}/>
          <div className="row gap-2 mt-3"><button className="btn btn-cta btn-sm" onClick={create}>Tạo</button><button className="btn btn-ghost btn-sm" onClick={()=>setCreating(false)}>Hủy</button></div>
        </div>
      )}

      <div className="card-pad">
        {pending.length === 0 && <div className="empty" style={{padding:'var(--space-6)'}}><div className="text-sm">Chưa có follow-up nào đang mở</div></div>}
        {sections.map(s => {
          if (groups[s.key].length === 0) return null;
          return (
            <div key={s.key} style={{marginBottom:24}}>
              <div className="row mb-3" style={{gap:10}}>
                <Tag variant={s.variant} dot>{s.label}</Tag>
                <span className="text-xs fg-muted">{groups[s.key].length} việc</span>
                <div style={{flex:1,height:1,background:'var(--border-1)'}}/>
              </div>
              <div className="grid grid-2">
                {groups[s.key].map(f => (
                  <div key={f.id} className="pipeline-card" style={{margin:0, border: editingId === f.id ? '1px solid var(--vs-light-blue)' : '1px solid var(--border-2)'}}>
                    <div className="row gap-3" style={{alignItems:'flex-start'}}>
                      <Avatar name={f.customer ? f.customer.name : '?'}/>
                      <div style={{flex:1,minWidth:0}}>
                        <div className="row gap-2">
                          <div className="strong fg-1" style={{cursor:'pointer'}} onClick={()=>onOpen && f.customer && onOpen({raw:D.customers.find(c=>c.id===f.customer.id)||{id:f.customer.id,name:f.customer.name,phone:f.customer.phone,tags:[],consent:{sms:true,email:true,zalo:true,call:true}},kind:'b2c'})}>{f.customer ? f.customer.name : '—'}</div>
                          {f.overdue && <Tag variant="danger">Quá hạn</Tag>}
                        </div>
                        <div className="text-xs fg-muted mt-1 tnum">{f.customer ? f.customer.phone : ''}{f.assignedTo ? ' · ' + f.assignedTo : ''}</div>
                        
                        {editingId === f.id ? (
                          <div style={{marginTop:8}}>
                            <div className="field-label" style={{fontSize:11, marginBottom:2}}>Nội dung công việc</div>
                            <input className="input mb-2 input-sm" style={{height:28, padding:'4px 8px', fontSize:12}}
                              value={editForm.reason} onChange={e=>setEditForm(f=>({...f,reason:e.target.value}))}/>
                            
                            <div className="field-label" style={{fontSize:11, marginBottom:2}}>Hạn nhắc lại</div>
                            <Segmented value={editForm.bucket} options={[{value:'today',label:'Hôm nay'},{value:'tomorrow',label:'Ngày mai'},{value:'7days',label:'7 ngày tới'},{value:'custom',label: customDays2 ? `Sau ${customDays2} ngày` : 'Sau X ngày'}]} onChange={handleEditBucketChange}/>
                            
                            <div className="row gap-2 mt-3">
                              <button className="btn btn-cta btn-sm" onClick={() => saveEdit(f.id)}>Lưu</button>
                              <button className="btn btn-ghost btn-sm" onClick={() => setEditingId(null)}>Hủy</button>
                            </div>
                          </div>
                        ) : (
                          <>
                            <div className="text-sm fg-2 mt-2" style={{lineHeight:1.5}}>
                              <Icon name="flag" size={12} style={{display:'inline-block',marginRight:4,color:'var(--vs-light-blue)'}}/>{f.reason}
                            </div>
                            <div className="row mt-3 gap-1">
                              <button className="btn btn-cta btn-sm" onClick={() => onGoToTelesale && f.customer && onGoToTelesale(D.customers.find(c=>c.id===f.customer.id)||{id:f.customer.id})}>
                                <Icon name="phone" size={12}/>Gọi
                              </button>
                              <button className="btn btn-secondary btn-sm" onClick={() => startEdit(f)}>Sửa</button>
                              <button className="btn btn-ghost btn-sm" onClick={() => markDone(f.id)}><Icon name="check" size={12}/>Đã xong</button>
                            </div>
                          </>
                        )}
                      </div>
                    </div>
                  </div>
                ))}
              </div>
            </div>
          );
        })}

        {/* Thống kê hoàn thành */}
        {done.length > 0 && (
          <div style={{marginTop:8, borderTop:'1px solid var(--border-1)', paddingTop:8}}>
            <button
              onClick={() => setShowDone(d => !d)}
              style={{width:'100%', textAlign:'left', background:'none', border:'none', padding:'8px 0', cursor:'pointer', display:'flex', alignItems:'center', gap:8, color:'var(--fg-2)'}}
            >
              <Icon name={showDone ? 'chevron-down' : 'chevron-right'} size={14}/>
              <span className="text-sm strong">Đã hoàn thành: {done.length} follow-up</span>
              <Tag variant="success" style={{marginLeft:4}}>{done.filter(f => {
                const d = f.doneAt ? new Date(f.doneAt) : null;
                return d && (Date.now() - d.getTime()) < 7 * 864e5;
              }).length} tuần này</Tag>
            </button>
            {showDone && (
              <div className="grid grid-2" style={{paddingBottom:8}}>
                {done.map(f => (
                  <div key={f.id} className="pipeline-card" style={{margin:0, opacity:0.75, border:'1px solid rgba(0,200,80,0.2)'}}>
                    <div className="row gap-2">
                      <Avatar name={f.customer ? f.customer.name : '?'} size="sm"/>
                      <div style={{flex:1, minWidth:0}}>
                        <div className="strong fg-1 text-sm">{f.customer ? f.customer.name : '—'}</div>
                        <div className="text-xs fg-muted tnum">{f.customer ? f.customer.phone : ''}</div>
                      </div>
                      <Tag variant="success" dot>Xong</Tag>
                    </div>
                    <div className="text-xs fg-2 mt-2">{f.reason}</div>
                    <div className="row mt-2" style={{justifyContent:'space-between'}}>
                      <div className="text-xs fg-muted">{f.assignedTo || ''}</div>
                      <div className="text-xs fg-muted">{f.doneAt ? new Date(f.doneAt).toLocaleDateString('vi-VN') : ''}</div>
                    </div>
                  </div>
                ))}
              </div>
            )}
          </div>
        )}
      </div>
    </Card>
  );
}

// ===========================================================
// Tạo nhóm khách hàng — nhập tên, tùy chọn gán các khách đang chọn.
// ===========================================================
function CreateGroupModal({ selectedIds = [], onClose, onCreated }) {
  const [name, setName] = useState('');
  const [assign, setAssign] = useState(selectedIds.length > 0);
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState(null);

  const submit = async () => {
    const nm = name.trim();
    if (!nm) { setErr('Nhập tên nhóm'); return; }
    setBusy(true); setErr(null);
    try {
      if (window.TVS_API && window.TVS_API.isAuthed()) {
        const body = { name: nm };
        if (assign && selectedIds.length) body.customerIds = selectedIds;
        const r = await window.TVS_API.api('/api/customers/groups', { method: 'POST', body: JSON.stringify(body) });
        onCreated && onCreated(r.group || { id: '', name: nm, count: 0 });
      } else {
        onCreated && onCreated({ id: '', name: nm, count: 0 });
      }
    } catch (e) {
      setErr(e.message || 'Lỗi tạo nhóm');
      setBusy(false);
    }
  };

  return (
    <div className="drawer-overlay" style={{justifyContent:'center',alignItems:'center'}} onClick={onClose}>
      <div onClick={(e) => e.stopPropagation()}
        style={{background:'var(--bg-page)',width:'min(460px, 95vw)',borderRadius:'var(--radius-lg)',overflow:'hidden',boxShadow:'var(--shadow-pop)',animation:'slidein 0.28s var(--ease-out)'}}>
        <div style={{padding:'var(--space-5) var(--space-6)',background:'var(--vs-dark-blue)',color:'#fff'}}>
          <div className="row">
            <div style={{flex:1}}>
              <div className="eyebrow" style={{color:'var(--vs-light-blue)'}}>Nhóm khách hàng</div>
              <h2 style={{margin:'4px 0 0',fontFamily:'var(--font-display)',fontSize:20,fontWeight:700,color:'#fff'}}>Tạo nhóm mới</h2>
            </div>
            <button onClick={onClose} style={{background:'rgba(255,255,255,0.18)',border:'none',color:'#fff',width:36,height:36,borderRadius:'50%',cursor:'pointer',display:'inline-flex',alignItems:'center',justifyContent:'center'}}>
              <Icon name="close" size={16}/>
            </button>
          </div>
        </div>
        <div style={{padding:'var(--space-5) var(--space-6)'}}>
          <div className="field-label">Tên nhóm <span style={{color:'var(--status-sale)'}}>*</span></div>
          <input className="input mb-3" autoFocus placeholder="VD: Khách VIP quý 3, Đối tác miền Bắc..." value={name}
            onChange={(e)=>setName(e.target.value)} onKeyDown={(e)=>{ if (e.key==='Enter') submit(); }}/>
          {selectedIds.length > 0 && (
            <label className="row" style={{cursor:'pointer',padding:'var(--space-3) var(--space-4)',background: assign ? 'rgba(65,182,230,0.06)' : 'var(--bg-soft)',border:`1px solid ${assign ? 'rgba(65,182,230,0.30)' : 'var(--border-1)'}`,borderRadius:'var(--radius-md)'}}>
              <input type="checkbox" checked={assign} onChange={(e)=>setAssign(e.target.checked)} style={{accentColor:'var(--vs-light-blue)',width:18,height:18}}/>
              <div style={{flex:1,marginLeft:8}}>
                <div className="strong fg-1 text-sm">Gán {selectedIds.length} khách đang chọn vào nhóm này</div>
                <div className="text-xs fg-muted">Có thể bỏ chọn để tạo nhóm rỗng rồi thêm khách sau.</div>
              </div>
            </label>
          )}
          <div className="text-xs fg-muted mt-3" style={{lineHeight:1.5}}>Nhóm tạo ở CRM. Đẩy lên Sapo để đồng bộ hoặc giữ nguyên tại CRM đều được.</div>
          {err && <div className="banner danger mt-3" style={{padding:'8px 10px'}}><span className="text-sm">{err}</span></div>}
        </div>
        <div style={{padding:'var(--space-4) var(--space-6)',background:'var(--bg-soft)',borderTop:'1px solid var(--border-1)',display:'flex',gap:'var(--space-3)',justifyContent:'flex-end'}}>
          <button className="btn btn-ghost" onClick={onClose}>Hủy</button>
          <button className="btn btn-cta" disabled={busy || !name.trim()} onClick={submit}>{busy ? 'Đang tạo…' : <><Icon name="plus" size={14}/>Tạo nhóm</>}</button>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { StaffDashboard, UnifiedCustomersScreen, UnifiedFollowUps, CreateGroupModal });
