// ===========================================================
// Import khách hàng từ file CSV / Excel
// - CSV: parse thuần trong browser (hỗ trợ dấu phẩy / chấm phẩy / tab, ô có ngoặc kép)
// - Excel (.xlsx/.xls): nạp thư viện SheetJS từ CDN khi cần; offline thì hướng dẫn lưu CSV
// - Tự nhận diện cột theo tiêu đề tiếng Việt, cho phép chỉnh tay từng cột
// - Kiểm tra: thiếu tên, SĐT 9–11 số, trùng SĐT trong file & với khách có sẵn
// ===========================================================

// Các trường CRM có thể nhận từ file — thứ tự này cũng là thứ tự cột ở bảng xem trước
const IMPORT_FIELDS = [
  { id: 'name',      label: 'Họ tên *' },
  { id: 'phone',     label: 'SĐT *' },
  { id: 'email',     label: 'Email' },
  { id: 'city',      label: 'Thành phố' },
  { id: 'type',      label: 'Loại khách' },
  { id: 'tier',      label: 'Hạng thành viên' },
  { id: 'source',    label: 'Nguồn' },
  { id: 'birthYear', label: 'Năm sinh' },
  { id: 'gender',    label: 'Giới tính' },
  { id: 'tags',      label: 'Tags' },
  { id: 'note',      label: 'Ghi chú' },
  { id: 'company',   label: 'Công ty' },
  { id: 'skip',      label: '— Bỏ qua cột —' },
];

// Tiêu đề cột thường gặp (đã bỏ dấu) → trường CRM
const __IMPORT_ALIASES = {
  name:      ['ho ten','ho va ten','ten','ten khach','ten khach hang','khach hang','name','full name','fullname','customer','customer name'],
  phone:     ['sdt','so dien thoai','dien thoai','so dt','sodienthoai','phone','phone number','mobile','tel','telephone'],
  email:     ['email','e mail','mail','dia chi email'],
  city:      ['thanh pho','tinh','tinh thanh','tinh thanh pho','city','khu vuc','dia ban','province'],
  type:      ['loai','loai khach','loai khach hang','phan loai','segment','type','customer type'],
  tier:      ['hang','hang thanh vien','tier','membership'],
  source:    ['nguon','nguon khach','kenh','source','channel'],
  birthYear: ['nam sinh','birth year','birthyear','year of birth'],
  gender:    ['gioi tinh','gender','sex'],
  tags:      ['tags','tag','nhan','nhom'],
  note:      ['ghi chu','note','notes','mo ta','comment'],
  company:   ['cong ty','doanh nghiep','company','to chuc'],
};

function __guessField(header) {
  const h = searchNorm(String(header || '')).replace(/[^a-z0-9 ]/g, ' ').replace(/\s+/g, ' ').trim();
  if (!h) return 'skip';
  for (const f of Object.keys(__IMPORT_ALIASES)) {
    if (__IMPORT_ALIASES[f].includes(h)) return f;
  }
  for (const f of Object.keys(__IMPORT_ALIASES)) {
    if (__IMPORT_ALIASES[f].some(a => a.length >= 3 && h.includes(a))) return f;
  }
  return 'skip';
}

// CSV parser thuần — tự dò dấu phân cách, hỗ trợ ô trong ngoặc kép chứa xuống dòng
function __parseCSV(text) {
  text = String(text || '').replace(/^\uFEFF/, '');
  const firstLine = text.split(/\r?\n/, 1)[0] || '';
  const probe = firstLine.replace(/"[^"]*"/g, ''); // bỏ ô trong ngoặc kép — dấu phẩy bên trong không phải dấu phân cách
  const counts = [',', ';', '\t'].map(d => ({ d, n: probe.split(d).length }));
  counts.sort((a, b) => b.n - a.n);
  const delim = counts[0].n > 1 ? counts[0].d : ',';
  const rows = [];
  let row = [], cell = '', inQ = false;
  for (let i = 0; i < text.length; i++) {
    const ch = text[i];
    if (inQ) {
      if (ch === '"') {
        if (text[i + 1] === '"') { cell += '"'; i++; }
        else inQ = false;
      } else cell += ch;
    } else if (ch === '"') inQ = true;
    else if (ch === delim) { row.push(cell); cell = ''; }
    else if (ch === '\n' || ch === '\r') {
      if (ch === '\r' && text[i + 1] === '\n') i++;
      row.push(cell); cell = '';
      if (row.some(c => String(c).trim() !== '')) rows.push(row);
      row = [];
    } else cell += ch;
  }
  row.push(cell);
  if (row.some(c => String(c).trim() !== '')) rows.push(row);
  return rows;
}

// SheetJS nạp 1 lần từ CDN — chỉ khi người dùng chọn file Excel
let __xlsxPromise = null;
function __loadXLSX() {
  if (window.XLSX) return Promise.resolve(window.XLSX);
  if (__xlsxPromise) return __xlsxPromise;
  __xlsxPromise = new Promise((resolve, reject) => {
    const s = document.createElement('script');
    s.src = 'https://cdn.sheetjs.com/xlsx-0.20.3/package/dist/xlsx.full.min.js';
    s.onload = () => window.XLSX ? resolve(window.XLSX) : reject(new Error('XLSX missing'));
    s.onerror = () => { __xlsxPromise = null; reject(new Error('load failed')); };
    document.head.appendChild(s);
  });
  return __xlsxPromise;
}

async function __readImportFile(file) {
  if (/\.(xlsx|xls)$/i.test(file.name)) {
    let XLSX;
    try { XLSX = await __loadXLSX(); }
    catch (e) { throw new Error('Không tải được thư viện đọc Excel (cần mạng). Mở file trong Excel → Lưu dạng CSV rồi thử lại.'); }
    const buf = await file.arrayBuffer();
    const wb = XLSX.read(buf, { type: 'array' });
    const ws = wb.Sheets[wb.SheetNames[0]];
    const aoa = XLSX.utils.sheet_to_json(ws, { header: 1, raw: false, defval: '' });
    return aoa.filter(r => (r || []).some(c => String(c).trim() !== ''));
  }
  const text = await file.text();
  if (text.includes('\uFFFD')) {
    throw new Error('File CSV không phải mã UTF-8 nên tiếng Việt bị lỗi font. Mở file trong Excel → Lưu dạng "CSV UTF-8" rồi thử lại.');
  }
  return __parseCSV(text);
}

// Chuẩn hoá SĐT về dạng VN bắt đầu bằng 0 — Excel lưu ô số làm mất số 0 đầu, và khách hay ghi +84
function __normPhone(raw) {
  let d = phoneDigits(String(raw || ''));
  if (d.startsWith('84') && d.length >= 10) d = '0' + d.slice(2);
  else if (/^[35789]\d{8}$/.test(d)) d = '0' + d; // 9 số thiếu 0 đầu (đầu số di động VN)
  return d;
}

// "Bác sĩ; TikTok Affiliate" → ['doctor','affiliate_tiktok'] — nhận cả nhãn lẫn id
function __mapImportTypes(raw) {
  const parts = String(raw || '').split(/[;,/|]+/).map(s => s.trim()).filter(Boolean);
  const ids = [];
  parts.forEach(p => {
    const n = searchNorm(p);
    const nId = n.replace(/ /g, '_');
    let id = null;
    const opt = (D.typeOptions || []).find(o => searchNorm(o.label) === n || o.id === nId)
      || (D.typeOptions || []).find(o => n.length >= 3 && searchNorm(o.label).includes(n));
    if (opt) id = opt.id;
    if (!id && window.TYPE_META) {
      id = Object.keys(TYPE_META).find(k => searchNorm(TYPE_META[k][0]) === n || k === nId) || null;
    }
    if (id && !ids.includes(id)) ids.push(id);
  });
  return ids.length ? ids : ['retail'];
}

function __mapImportTier(raw) {
  const n = searchNorm(String(raw || ''));
  // Hạng thống nhất tiếng Việt: Kim cương → Vàng → Bạc → Đồng (nhận cả tên cũ/tiếng Anh)
  if (n.includes('kim cuong') || n.includes('diamond') || n.includes('platinum') || n.includes('bach kim')) return 'Kim cương';
  if (n.includes('gold') || n.includes('vang')) return 'Vàng';
  if (n.includes('silver') || n.includes('bac')) return 'Bạc';
  return 'Đồng';
}

function __mapImportGender(raw) {
  const n = searchNorm(String(raw || ''));
  if (n === 'nam' || n === 'male' || n === 'm') return 'Nam';
  if (n === 'nu' || n === 'female' || n === 'f') return 'Nữ';
  return raw ? 'Khác' : '—';
}

// File mẫu CSV (BOM UTF-8 để Excel mở đúng tiếng Việt)
function __downloadImportTemplate() {
  const rows = [
    ['Họ tên', 'SĐT', 'Email', 'Thành phố', 'Loại khách', 'Hạng', 'Nguồn', 'Năm sinh', 'Giới tính', 'Tags', 'Ghi chú'],
    ['Nguyễn Văn An', '0901 234 567', 'an.nv@email.vn', 'TP.HCM', 'Khách lẻ', 'Đồng', 'Facebook', '1990', 'Nam', 'Vitamin; Whey Protein', 'Quan tâm combo tăng cơ'],
    ['Trần Thị Bích', '0912 345 678', 'bich.tt@email.vn', 'Hà Nội', 'Bác sĩ; TikTok Affiliate', 'Vàng', 'Giới thiệu', '1985', 'Nữ', 'Referral', 'Bác sĩ nhi — giới thiệu phụ huynh'],
  ];
  const csv = '\uFEFF' + rows.map(r => r.map(c => /[",;\n]/.test(c) ? '"' + c.replace(/"/g, '""') + '"' : c).join(',')).join('\r\n');
  const url = URL.createObjectURL(new Blob([csv], { type: 'text/csv;charset=utf-8' }));
  const a = document.createElement('a');
  a.href = url; a.download = 'mau-import-khach-hang.csv';
  a.click();
  setTimeout(() => URL.revokeObjectURL(url), 2000);
}

function ImportCustomersModal({ role, onClose }) {
  const [step, setStep] = useState(1);          // 1 chọn file · 2 kiểm tra · 3 kết quả
  // Marketing / Admin: gán toàn bộ khách import cho 1 nhân viên bán hàng
  const canAssign = !!(role && window.tvsCan && tvsCan(role, 'customers.assign'));
  const staffOptions = canAssign ? window.tvsSalesStaff() : [];
  const [assignTo, setAssignTo] = useState('');
  const [fileName, setFileName] = useState('');
  const [aoa, setAoa] = useState(null);         // array-of-arrays từ file (dòng 0 = tiêu đề)
  const [mapping, setMapping] = useState([]);   // trường CRM cho từng cột
  const [error, setError] = useState(null);
  const [busy, setBusy] = useState(false);
  const [dragOver, setDragOver] = useState(false);
  const [result, setResult] = useState(null);
  const fileRef = useRef(null);

  const handleFile = async (file) => {
    if (!file) return;
    setError(null); setBusy(true);
    try {
      const rows = await __readImportFile(file);
      if (!rows || rows.length < 2) throw new Error('File không có dữ liệu — cần 1 dòng tiêu đề + ít nhất 1 dòng khách.');
      if (rows[0].length > 40) throw new Error('File có quá nhiều cột (' + rows[0].length + ') — kiểm tra lại dấu phân cách.');
      if (rows.length > 2001) throw new Error('File có ' + (rows.length - 1) + ' dòng — vượt giới hạn 2.000 khách mỗi lần nhập. Chia nhỏ file rồi nhập nhiều lần.');
      const seen = new Set();
      const map = rows[0].map(h => {
        const f = __guessField(h);
        if (f === 'skip' || seen.has(f)) return 'skip';
        seen.add(f);
        return f;
      });
      setFileName(file.name);
      setAoa(rows);
      setMapping(map);
      setStep(2);
    } catch (e) {
      setError(e.message || 'Không đọc được file.');
    } finally {
      setBusy(false);
    }
  };

  // Đổi mapping 1 cột — mỗi trường chỉ gán cho 1 cột, cột nào đang giữ trường đó thì trả về "skip"
  const setColField = (col, field) => {
    setMapping(m => m.map((f, i) => i === col ? field : (field !== 'skip' && f === field ? 'skip' : f)));
  };

  // Phân tích từng dòng: hợp lệ / lỗi / trùng SĐT
  const analysis = useMemo(() => {
    if (!aoa) return [];
    const idx = {};
    mapping.forEach((f, i) => { if (f !== 'skip' && !(f in idx)) idx[f] = i; });
    const existingPhones = new Set(
      [...D.customers, ...D.partners].map(c => __normPhone(c.phone)).filter(d => d.length >= 9)
    );
    const seenInFile = new Set();
    return aoa.slice(1).map((r, i) => {
      const get = f => idx[f] != null ? String(r[idx[f]] ?? '').trim() : '';
      const name = get('name') || get('company');
      const phone = get('phone');
      const pd = __normPhone(phone);
      let status = 'ok', reason = '';
      if (!name) { status = 'error'; reason = 'Thiếu họ tên'; }
      else if (!phone) { status = 'error'; reason = 'Thiếu SĐT'; }
      else if (pd.length < 9 || pd.length > 11) { status = 'error'; reason = 'SĐT không hợp lệ'; }
      else if (existingPhones.has(pd)) { status = 'dup'; reason = 'Trùng SĐT khách có sẵn'; }
      else if (seenInFile.has(pd)) { status = 'dup'; reason = 'Trùng SĐT trong file'; }
      if (status === 'ok') seenInFile.add(pd);
      return { line: i + 2, get, name, phone: (pd.length >= 9 ? pd : phone), status, reason };
    });
  }, [aoa, mapping]);

  const okRows  = analysis.filter(r => r.status === 'ok');
  const dupRows = analysis.filter(r => r.status === 'dup');
  const errRows = analysis.filter(r => r.status === 'error');
  const hasName  = mapping.includes('name') || mapping.includes('company');
  const hasPhone = mapping.includes('phone');

  const doImport = () => {
    const nowY = new Date().getFullYear();
    // Cấp id chắc chắn chưa tồn tại (kể cả khách đã lưu ở phiên trước)
    const taken = new Set([
      ...D.customers.map(c => c.id),
      ...((window.TVS_SESSION.customers || []).map(c => c.id)),
    ]);
    let seq = Date.now();
    const nextId = () => {
      let id;
      do { id = 'C-' + String(seq++).slice(-6); } while (taken.has(id));
      taken.add(id);
      return id;
    };
    const records = okRows.map((row, i) => {
      const g = row.get;
      const types = __mapImportTypes(g('type'));
      const by = parseInt(g('birthYear'), 10);
      const age = (by >= 1920 && by <= nowY - 5) ? nowY - by : null;
      const tags = g('tags') ? g('tags').split(/[;|]+/).map(s => s.trim()).filter(Boolean).slice(0, 4) : [];
      return {
        id: nextId(),
        owner: (canAssign && assignTo) || (role ? role.person : 'Chưa gán'),
        name: row.name,
        company: g('company') || null,
        phone: row.phone,
        email: g('email') || '',
        tier: __mapImportTier(g('tier')), joined: D.iso(0),
        gender: __mapImportGender(g('gender')), age,
        city: g('city') || '—',
        ltv: 0, orders: 0, avgOrder: 0, lastOrder: null,
        segment: types[0], roles: types.slice(1),
        affiliateId: null,
        source: g('source') || 'Import file',
        channel: g('source') || 'Import file',
        tags: [...new Set([...tags, 'New'])],
        note: g('note') || ('Import từ file ' + fileName),
        nps: null,
        consent: { call: true, sms: true, email: true, zalo: true },
        preferredContact: 'Zalo',
        followUp: null,
      };
    });
    D.customers.unshift(...records);
    window.TVS_SESSION.addCustomers(records);
    setResult({ added: records.length, dup: dupRows.length, err: errRows.length });
    setStep(3);
    window.tvsToast('Đã nhập ' + records.length + ' khách hàng từ file');
  };

  const statusTag = (r) => r.status === 'ok'
    ? <Tag variant="success" dot>Hợp lệ</Tag>
    : r.status === 'dup'
      ? <Tag variant="warning" dot>{r.reason}</Tag>
      : <Tag variant="danger" dot>{r.reason}</Tag>;

  return (
    <div className="drawer-overlay" style={{ justifyContent: 'center', alignItems: 'center' }}>
      <div onClick={(e) => e.stopPropagation()}
        style={{ background: 'var(--bg-page)', width: 'min(880px, 95vw)', maxHeight: '92vh', display: 'flex', flexDirection: 'column', borderRadius: 'var(--radius-lg)', overflow: 'hidden', boxShadow: 'var(--shadow-pop)', animation: 'slidein 0.28s var(--ease-out)' }}>

        {/* Header */}
        <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ập khách hàng hàng loạt</div>
              <h2 style={{ margin: '4px 0 0', fontFamily: 'var(--font-display)', fontSize: 22, fontWeight: 700, color: '#fff' }}>
                {step === 1 ? 'Chọn file CSV / Excel' : step === 2 ? 'Kiểm tra dữ liệu trước khi nhập' : 'Hoàn tất!'}
              </h2>
              {step === 2 && <div className="text-xs mt-1" style={{ color: 'rgba(255,255,255,0.75)' }}>📄 {fileName} · {analysis.length} dòng dữ liệu</div>}
            </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>

        {/* Body */}
        <div style={{ padding: 'var(--space-5) var(--space-6)', overflow: 'auto', flex: 1 }}>
          {step === 1 && (
            <>
              <div
                onDragOver={(e) => { e.preventDefault(); setDragOver(true); }}
                onDragLeave={() => setDragOver(false)}
                onDrop={(e) => { e.preventDefault(); setDragOver(false); handleFile(e.dataTransfer.files && e.dataTransfer.files[0]); }}
                onClick={() => fileRef.current && fileRef.current.click()}
                style={{
                  border: '2px dashed ' + (dragOver ? 'var(--vs-light-blue)' : 'var(--border-1)'),
                  background: dragOver ? 'rgba(65,182,230,0.06)' : 'var(--bg-soft)',
                  borderRadius: 'var(--radius-lg)', padding: 'var(--space-8) var(--space-6)',
                  textAlign: 'center', cursor: 'pointer', transition: 'all 0.15s',
                }}>
                <div style={{ color: 'var(--vs-light-blue)', marginBottom: 8 }}><Icon name="upload" size={36}/></div>
                <div className="bold" style={{ color: 'var(--fg-1)', fontSize: 15 }}>{busy ? 'Đang đọc file…' : 'Kéo thả file vào đây, hoặc bấm để chọn'}</div>
                <div className="text-sm fg-muted mt-2">Hỗ trợ .csv, .xlsx, .xls — dòng đầu tiên là tiêu đề cột</div>
                <input ref={fileRef} type="file" accept=".csv,.xlsx,.xls" style={{ display: 'none' }}
                  onChange={(e) => { handleFile(e.target.files && e.target.files[0]); e.target.value = ''; }}/>
              </div>

              {error && (
                <div className="banner danger mt-4">
                  <Icon name="close" size={16}/>
                  <div style={{ flex: 1 }}>{error}</div>
                </div>
              )}

              <div className="row mt-4" style={{ alignItems: 'flex-start', gap: 'var(--space-4)' }}>
                <div style={{ flex: 1 }} className="text-sm fg-2">
                  <div className="bold mb-1" style={{ color: 'var(--fg-1)' }}>Cột bắt buộc: Họ tên + SĐT.</div>
                  Các cột tuỳ chọn: Email, Thành phố, Loại khách (VD: "Bác sĩ; TikTok Affiliate"), Hạng, Nguồn, Năm sinh, Giới tính, Tags, Ghi chú. Hệ thống tự nhận diện tiêu đề cột — anh/chị có thể chỉnh lại ở bước sau.
                </div>
                <button className="btn btn-secondary" onClick={__downloadImportTemplate}>
                  <Icon name="download" size={14}/>Tải file mẫu
                </button>
              </div>
            </>
          )}

          {step === 2 && (
            <>
              {/* Tổng kết nhanh */}
              <div className="row gap-2 mb-4" style={{ flexWrap: 'wrap' }}>
                <Tag variant="success" dot>{okRows.length} khách sẽ nhập</Tag>
                {dupRows.length > 0 && <Tag variant="warning" dot>{dupRows.length} trùng SĐT — bỏ qua</Tag>}
                {errRows.length > 0 && <Tag variant="danger" dot>{errRows.length} dòng lỗi — bỏ qua</Tag>}
              </div>

              {(!hasName || !hasPhone) && (
                <div className="banner warning mb-4">
                  <Icon name="flag" size={16}/>
                  <div style={{ flex: 1 }}>Chưa gán cột <b>{!hasName ? 'Họ tên' : 'SĐT'}</b> — chọn trường tương ứng ở hàng "Gán vào trường" bên dưới.</div>
                </div>
              )}

              {/* Bảng xem trước: hàng 1 = tiêu đề file, hàng 2 = select gán trường, sau đó là dữ liệu */}
              <Card pad={false}>
                <div className="table-wrap" style={{ maxHeight: 380, overflowY: 'auto' }}>
                  <table className="tbl">
                    <thead>
                      <tr>
                        <th style={{ width: 40 }}>#</th>
                        {aoa[0].map((h, i) => <th key={i}>{String(h || ('Cột ' + (i + 1)))}</th>)}
                        <th>Trạng thái</th>
                      </tr>
                      <tr>
                        <th className="text-xs fg-muted" style={{ fontWeight: 400 }}>Gán:</th>
                        {aoa[0].map((h, i) => (
                          <th key={i}>
                            <select className="select" style={{ width: '100%', minWidth: 110, fontSize: 12, padding: '4px 8px' }}
                              value={mapping[i] || 'skip'} onChange={(e) => setColField(i, e.target.value)}>
                              {IMPORT_FIELDS.map(f => <option key={f.id} value={f.id}>{f.label}</option>)}
                            </select>
                          </th>
                        ))}
                        <th/>
                      </tr>
                    </thead>
                    <tbody>
                      {analysis.slice(0, 100).map((r, ri) => (
                        <tr key={ri} style={r.status === 'error' ? { background: 'rgba(191,9,35,0.04)' } : r.status === 'dup' ? { background: 'rgba(236,167,44,0.05)' } : undefined}>
                          <td className="text-xs fg-muted tnum">{r.line}</td>
                          {aoa[0].map((_, ci) => (
                            <td key={ci} className="text-sm" style={{ maxWidth: 180, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }} title={String(aoa[ri + 1][ci] ?? '')}>
                              {String(aoa[ri + 1][ci] ?? '')}
                            </td>
                          ))}
                          <td>{statusTag(r)}</td>
                        </tr>
                      ))}
                    </tbody>
                  </table>
                </div>
                {analysis.length > 100 && (
                  <div style={{ padding: 'var(--space-2) var(--space-5)', borderTop: '1px solid var(--border-1)' }} className="text-xs fg-muted">
                    Hiển thị 100 / {analysis.length} dòng — toàn bộ dòng hợp lệ vẫn được nhập đủ.
                  </div>
                )}
              </Card>

              <div className="text-xs fg-muted mt-3" style={{ lineHeight: 1.6 }}>
                Khách nhập vào sẽ gán phụ trách: {canAssign ? (
                  <select className="select" style={{ display: 'inline-block', width: 'auto', padding: '2px 24px 2px 8px', fontSize: 'inherit', margin: '0 4px' }} value={assignTo} onChange={(e) => setAssignTo(e.target.value)}>
                    <option value="">{role ? `Chính tôi (${role.person})` : '—'}</option>
                    {staffOptions.filter(o => !role || o !== role.person).map(o => <option key={o} value={o}>{o}</option>)}
                  </select>
                ) : <b style={{ color: 'var(--fg-1)' }}>{role ? role.person : '—'}</b>} · Hạng mặc định Bronze nếu trống · Đồng ý liên hệ đặt mặc định "Có" — chỉnh trong hồ sơ nếu khách từ chối (PDPL).
              </div>
            </>
          )}

          {step === 3 && result && (
            <div style={{ textAlign: 'center', padding: 'var(--space-8) 0' }}>
              <div style={{ width: 72, height: 72, margin: '0 auto var(--space-4)', borderRadius: '50%', background: 'rgba(6,167,125,0.12)', color: 'var(--status-success)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                <Icon name="check" size={36} stroke={2.5}/>
              </div>
              <h3 style={{ margin: 0, fontSize: 20, color: 'var(--fg-1)', fontFamily: 'var(--font-display)' }}>Đã nhập {result.added} khách hàng</h3>
              <div className="text-sm fg-muted mt-2">
                {result.dup > 0 && <>{result.dup} dòng trùng SĐT đã bỏ qua · </>}
                {result.err > 0 && <>{result.err} dòng lỗi đã bỏ qua · </>}
                Khách mới nằm đầu bảng, gắn tag "New".
              </div>
            </div>
          )}
        </div>

        {/* Footer */}
        <div style={{ padding: 'var(--space-4) var(--space-6)', borderTop: '1px solid var(--border-1)', display: 'flex', gap: 'var(--space-3)', background: 'var(--bg-soft)' }}>
          {step === 2 && <button className="btn btn-secondary" onClick={() => { setStep(1); setAoa(null); setError(null); }}><Icon name="arrowleft" size={14}/>Chọn file khác</button>}
          <div style={{ flex: 1 }}/>
          {step !== 3 && <button className="btn btn-ghost" onClick={onClose}>Huỷ</button>}
          {step === 2 && (
            <button className="btn btn-cta" disabled={okRows.length === 0} style={okRows.length === 0 ? { opacity: 0.5, cursor: 'not-allowed' } : undefined}
              onClick={() => okRows.length > 0 && doImport()}>
              <Icon name="upload" size={14}/>Nhập {okRows.length} khách hàng
            </button>
          )}
          {step === 3 && <button className="btn btn-cta" onClick={onClose}>Xem danh sách khách</button>}
        </div>
      </div>
    </div>
  );
}

window.ImportCustomersModal = ImportCustomersModal;
