import { useState, useEffect, useCallback, useRef } from 'react';
import * as XLSX from 'xlsx';
import { api } from '../../api.js';
import ConfirmModal from '../../components/ConfirmModal.jsx';
import Modal from '../../components/Modal.jsx';
import ResultBanner from '../../components/ResultBanner.jsx';
import PasswordStrength from '../../components/PasswordStrength.jsx';
import { fmt, Badge, UserStatusBadge } from './adminHelpers.jsx';

// ── Helpers ────────────────────────────────────────────────────────────────

function computedStatus(u) {
  if (u.status === 'deactivated') return 'deactivated';
  if (u.status === 'locked')      return 'locked';
  if (!u.email_verified)          return 'pending';
  return 'active';
}

const STATUS_OPTIONS = [
  { value: 'all',         label: 'Tous' },
  { value: 'active',      label: 'Actif' },
  { value: 'pending',     label: 'En attente' },
  { value: 'deactivated', label: 'Désactivé' },
  { value: 'locked',      label: 'Verrouillé' },
];

const ROLE_OPTIONS = [
  { value: 'all',   label: 'Tous' },
  { value: 'user',  label: 'Utilisateur' },
  { value: 'admin', label: 'Admin' },
];

const AVATAR_COLORS = [
  ['#dbeafe','#1d4ed8'], ['#fce7f3','#be185d'], ['#d1fae5','#065f46'],
  ['#fef3c7','#92400e'], ['#ede9fe','#5b21b6'], ['#fee2e2','#991b1b'],
  ['#cffafe','#0e7490'], ['#dcfce7','#166534'],
];

function avatarColor(name) {
  const code = [...(name || '?')].reduce((a, c) => a + c.charCodeAt(0), 0);
  return AVATAR_COLORS[code % AVATAR_COLORS.length];
}

function initials(u) {
  const n = u.display_name || u.email;
  const parts = n.trim().split(/\s+/);
  return parts.length >= 2
    ? (parts[0][0] + parts[1][0]).toUpperCase()
    : n.slice(0, 2).toUpperCase();
}

// ── Icônes SVG ─────────────────────────────────────────────────────────────
const IcoEdit   = () => <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>;
const IcoCheck  = () => <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12"/></svg>;
const IcoMail   = () => <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg>;
const IcoPause  = () => <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="6" y="4" width="4" height="16"/><rect x="14" y="4" width="4" height="16"/></svg>;
const IcoPlay   = () => <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polygon points="5 3 19 12 5 21 5 3"/></svg>;
const IcoTrash  = () => <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/><path d="M9 6V4h6v2"/></svg>;
const IcoDots   = () => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="5" r="1" fill="currentColor"/><circle cx="12" cy="12" r="1" fill="currentColor"/><circle cx="12" cy="19" r="1" fill="currentColor"/></svg>;
const IcoSearch = () => <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>;

function MenuBtn({ onClick, icon, label, danger = false }) {
  return (
    <button
      style={{ display: 'flex', alignItems: 'center', gap: 8, width: '100%', padding: '8px 14px', background: 'none', border: 'none', cursor: 'pointer', fontSize: 'var(--fs-sm)', color: danger ? 'var(--danger)' : 'var(--text)', textAlign: 'left' }}
      onMouseEnter={e => e.currentTarget.style.background = 'var(--surface-2)'}
      onMouseLeave={e => e.currentTarget.style.background = 'none'}
      onClick={onClick}
    >
      {icon}{label}
    </button>
  );
}

// ── Export ─────────────────────────────────────────────────────────────────
const STATUS_FR = { active: 'Actif', pending: 'En attente', deactivated: 'Désactivé', locked: 'Verrouillé' };

function toRows(users) {
  return users.map(u => ({
    ID: u.id, Nom: u.display_name || '', Email: u.email,
    Statut: STATUS_FR[computedStatus(u)] || '',
    Rôle: u.role === 'admin' ? 'Admin' : 'Utilisateur',
    '2FA': u.totp_enabled ? 'Oui' : 'Non',
    'Inscrit le': u.created_at ? u.created_at.replace('T', ' ').slice(0, 16) : '',
  }));
}

function dlBlob(content, filename, type) {
  const blob = new Blob([content], { type });
  const url  = URL.createObjectURL(blob);
  const a    = document.createElement('a');
  a.href = url; a.download = filename; a.click();
  URL.revokeObjectURL(url);
}

function toCSV(users) {
  const headers = ['ID', 'Nom', 'Email', 'Statut', 'Rôle', '2FA', 'Inscrit le'];
  const rows    = toRows(users).map(r => Object.values(r).map(v => `"${String(v).replace(/"/g,'""')}"`).join(','));
  return '﻿' + [headers.map(h => `"${h}"`).join(','), ...rows].join('\n');
}
function toXLSX(users) {
  const ws = XLSX.utils.json_to_sheet(toRows(users));
  const wb = XLSX.utils.book_new();
  XLSX.utils.book_append_sheet(wb, ws, 'Utilisateurs');
  return XLSX.write(wb, { type: 'array', bookType: 'xlsx' });
}
function toJSON(users) { return JSON.stringify(toRows(users), null, 2); }

function ExportDropdown({ onCSV, onXLSX, onJSON }) {
  const [open, setOpen] = useState(false);
  const ref = useRef(null);
  useEffect(() => {
    if (!open) return;
    const h = e => { if (!ref.current?.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', h);
    return () => document.removeEventListener('mousedown', h);
  }, [open]);
  const choose = fn => { setOpen(false); fn(); };
  return (
    <div ref={ref} style={{ position: 'relative' }}>
      <button className="btn btn-outline btn-sm" onClick={() => setOpen(o => !o)}
        style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
          <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/>
        </svg>
        Exporter
      </button>
      {open && (
        <div className="export-dropdown" role="menu">
          <button role="menuitem" onClick={() => choose(onCSV)}>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="8" y1="13" x2="16" y2="13"/><line x1="8" y1="17" x2="16" y2="17"/></svg>
            <span><strong>Format CSV</strong><small>Compatible Excel, LibreOffice</small></span>
          </button>
          <button role="menuitem" onClick={() => choose(onXLSX)}>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><path d="M9 13l2 2 4-4"/></svg>
            <span><strong>Format Excel</strong><small>Fichier .xlsx Microsoft Excel</small></span>
          </button>
          <button role="menuitem" onClick={() => choose(onJSON)}>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><path d="M8 13h1.5a1 1 0 0 1 1 1v1a1 1 0 0 0 1 1 1 1 0 0 0-1 1v1a1 1 0 0 1-1 1H8"/><path d="M16 13h-1.5a1 1 0 0 0-1 1v1a1 1 0 0 1-1 1 1 1 0 0 1 1 1v1a1 1 0 0 0 1 1H16"/></svg>
            <span><strong>Format JSON</strong><small>Réimportable, structuré</small></span>
          </button>
        </div>
      )}
    </div>
  );
}

// ── Pagination ─────────────────────────────────────────────────────────────
const PAGE_SIZES = [10, 15, 25, 50];

function AdminPagination({ page, setPage, pageSize, setPageSize, total }) {
  const totalPages = Math.max(1, Math.ceil(total / pageSize));
  if (total === 0) return null;
  const delta = 2;
  const pages = [];
  for (let i = Math.max(1, page - delta); i <= Math.min(totalPages, page + delta); i++) pages.push(i);
  return (
    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '12px 0 0', gap: 16 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 12, flexShrink: 0 }}>
        <label style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 'var(--fs-xs)', color: 'var(--text-muted)', whiteSpace: 'nowrap' }}>
          Lignes par page
          <select value={pageSize} onChange={e => { setPageSize(Number(e.target.value)); setPage(1); }}
            style={{ fontSize: 'var(--fs-xs)', padding: '3px 6px', border: '1px solid var(--border)', borderRadius: 5, background: 'var(--surface)', color: 'var(--text)', cursor: 'pointer' }}>
            {PAGE_SIZES.map(n => <option key={n} value={n}>{n}</option>)}
          </select>
        </label>
        <span style={{ fontSize: 'var(--fs-xs)', color: 'var(--text-muted)', whiteSpace: 'nowrap' }}>Page {page} sur {totalPages}</span>
      </div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 3, flexShrink: 0 }}>
        <PBtn onClick={() => setPage(p => Math.max(1, p - 1))} disabled={page === 1}>‹</PBtn>
        {pages[0] > 1 && <><PBtn onClick={() => setPage(1)}>1</PBtn>{pages[0] > 2 && <Ellipsis />}</>}
        {pages.map(n => <PBtn key={n} onClick={() => setPage(n)} active={n === page}>{n}</PBtn>)}
        {pages[pages.length - 1] < totalPages && <>{pages[pages.length - 1] < totalPages - 1 && <Ellipsis />}<PBtn onClick={() => setPage(totalPages)}>{totalPages}</PBtn></>}
        <PBtn onClick={() => setPage(p => Math.min(totalPages, p + 1))} disabled={page === totalPages}>›</PBtn>
      </div>
    </div>
  );
}
const Ellipsis = () => <span style={{ padding: '0 2px', color: 'var(--text-muted)', fontSize: 12 }}>…</span>;
function PBtn({ onClick, disabled, active, children }) {
  return (
    <button onClick={onClick} disabled={disabled} style={{
      minWidth: 30, height: 30, padding: '0 6px', border: active ? '1.5px solid var(--primary)' : '1px solid var(--border)',
      borderRadius: 6, background: active ? 'var(--primary-bg, #eff6ff)' : 'var(--surface)',
      color: active ? 'var(--primary)' : 'var(--text)', fontSize: 13, fontWeight: active ? 600 : 400,
      cursor: disabled ? 'default' : 'pointer', opacity: disabled ? 0.4 : 1,
    }}
      onMouseEnter={e => { if (!disabled && !active) e.currentTarget.style.background = 'var(--surface-2)'; }}
      onMouseLeave={e => { if (!active) e.currentTarget.style.background = 'var(--surface)'; }}
    >{children}</button>
  );
}

// ── Composant principal ────────────────────────────────────────────────────
// ── Modale invitation ──────────────────────────────────────────────────────
function InviteUserModal({ open, onClose, onSent }) {
  const [form, setForm]       = useState({ email: '', role: 'user' });
  const [loading, setLoading] = useState(false);
  const [err, setErr]         = useState(null);
  const [sent, setSent]       = useState(false);

  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));
  const reset = () => { setForm({ email: '', role: 'user' }); setErr(null); setSent(false); };
  const handleClose = () => { reset(); onClose(); };

  const handleSubmit = async (e) => {
    e.preventDefault();
    setLoading(true); setErr(null);
    try {
      await api.post('/admin/invitations', { email: form.email, role: form.role });
      setSent(true);
      onSent?.();
    } catch (e) { setErr(e.message); }
    finally { setLoading(false); }
  };

  return (
    <Modal open={open} title="Inviter un utilisateur" onClose={handleClose} width={440}
      footer={sent ? (
        <button className="btn btn-primary" onClick={handleClose}>Fermer</button>
      ) : (
        <>
          <button type="button" className="btn btn-outline" onClick={handleClose} disabled={loading}>Annuler</button>
          <button type="submit" form="invite-user-form" className="btn btn-primary" disabled={loading}>
            {loading ? 'Envoi…' : 'Envoyer l\'invitation'}
          </button>
        </>
      )}
    >
      {sent ? (
        <div style={{ textAlign: 'center', padding: '12px 0' }}>
          <div style={{ width: 52, height: 52, borderRadius: '50%', background: '#f0fdf4', display: 'flex', alignItems: 'center', justifyContent: 'center', margin: '0 auto 16px' }}>
            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#16a34a" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/>
            </svg>
          </div>
          <p style={{ margin: '0 0 6px', fontWeight: 600, color: 'var(--text)' }}>Invitation envoyée !</p>
          <p style={{ margin: 0, fontSize: 13, color: 'var(--text-muted)' }}>
            Un email a été envoyé à <strong>{form.email}</strong>.<br/>Le lien est valable 7 jours.
          </p>
        </div>
      ) : (
        <>
          {err && <div className="error" style={{ marginBottom: 14 }}>{err}</div>}
          <form id="invite-user-form" onSubmit={handleSubmit} autoComplete="off" style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
            <div>
              <label>Email *</label>
              <input type="email" required autoComplete="off" placeholder="utilisateur@exemple.com"
                value={form.email} onChange={e => set('email', e.target.value)} />
            </div>
            <div>
              <label>Rôle</label>
              <select value={form.role} onChange={e => set('role', e.target.value)}>
                <option value="user">Utilisateur</option>
                <option value="admin">Administrateur</option>
              </select>
            </div>
            <p style={{ margin: 0, fontSize: 12, color: 'var(--text-muted)', lineHeight: 1.5 }}>
              L'invité recevra un email avec un lien sécurisé pour créer son compte. Son adresse email sera pré-remplie et non modifiable.
            </p>
          </form>
        </>
      )}
    </Modal>
  );
}

// ── Modale renvoi invitation ───────────────────────────────────────────────
function ResendInviteModal({ user, onClose, onSuccess, onError }) {
  const [loading, setLoading] = useState(false);
  const [err, setErr]         = useState(null);

  const handleClose = () => { setErr(null); onClose(); };

  const handleSend = async () => {
    setLoading(true); setErr(null);
    try {
      await api.post('/admin/invitations', { email: user.email, role: user.role });
      // Ferme d'abord, puis notifie le parent — évite le re-render concurrent
      handleClose();
      onSuccess?.(user.email);
    } catch (e) {
      setErr(e.message);
    } finally { setLoading(false); }
  };

  const identity = user?.display_name ? `${user.display_name} (${user.email})` : user?.email;

  return (
    <Modal open={!!user} title="Renvoyer l'invitation" onClose={handleClose} width={420}
      footer={
        <>
          <button className="btn btn-outline" onClick={handleClose} disabled={loading}>Annuler</button>
          <button className="btn btn-primary" onClick={handleSend} disabled={loading}>
            {loading ? 'Envoi…' : 'Renvoyer'}
          </button>
        </>
      }
    >
      {err && <div className="error" style={{ marginBottom: 14 }}>{err}</div>}
      <p style={{ margin: 0, color: 'var(--text-muted)', fontSize: 14, lineHeight: 1.6 }}>
        Renvoyer un nouveau lien d'invitation à <strong style={{ color: 'var(--text)' }}>{identity}</strong> ?
        <br/>L'ancien lien sera révoqué et remplacé par un nouveau valable 7 jours.
      </p>
    </Modal>
  );
}

// ── Modale création utilisateur ────────────────────────────────────────────
function CreateUserModal({ open, onClose, onCreated }) {
  const [minPasswordLength, setMinPasswordLength] = useState(8);
  useEffect(() => {
    fetch('/api/app-info').then(r => r.json()).then(d => {
      if (d.minPasswordLength) setMinPasswordLength(d.minPasswordLength);
    }).catch(() => {});
  }, []);

  const [form, setForm]       = useState({ email: '', password: '', displayName: '', role: 'user' });
  const [loading, setLoading] = useState(false);
  const [err, setErr]         = useState(null);

  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));

  const reset = () => { setForm({ email: '', password: '', displayName: '', role: 'user' }); setErr(null); };

  const handleClose = () => { reset(); onClose(); };

  const handleSubmit = async (e) => {
    e.preventDefault();
    setLoading(true); setErr(null);
    try {
      const created = await api.post('/admin/users', {
        email:       form.email,
        password:    form.password,
        displayName: form.displayName || undefined,
        role:        form.role,
      });
      reset();
      onCreated?.(created);
      onClose();
    } catch (e) { setErr(e.message); }
    finally { setLoading(false); }
  };

  return (
    <Modal open={open} title="Créer un utilisateur" onClose={handleClose} width={460}
      footer={<>
        <button type="button" className="btn btn-outline" onClick={handleClose} disabled={loading}>Annuler</button>
        <button type="submit" form="create-user-form" className="btn btn-primary" disabled={loading}>
          {loading ? 'Création…' : 'Créer le compte'}
        </button>
      </>}
    >
      {err && <div className="error" style={{ marginBottom: 14 }}>{err}</div>}
      <form id="create-user-form" onSubmit={handleSubmit} autoComplete="off" style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
        <div>
          <label>Nom affiché</label>
          <input autoComplete="off" value={form.displayName} onChange={e => set('displayName', e.target.value)} placeholder="Prénom Nom" />
        </div>
        <div>
          <label>Email *</label>
          <input type="email" required autoComplete="off" value={form.email} onChange={e => set('email', e.target.value)} placeholder="utilisateur@exemple.com" />
        </div>
        <div>
          <label>Mot de passe *</label>
          <input type="password" required minLength={minPasswordLength} autoComplete="new-password" value={form.password} onChange={e => set('password', e.target.value)} placeholder={`${minPasswordLength} caractères minimum`} />
          <PasswordStrength password={form.password} minLength={minPasswordLength} />
        </div>
        <div>
          <label>Rôle</label>
          <select value={form.role} onChange={e => set('role', e.target.value)}>
            <option value="user">Utilisateur</option>
            <option value="admin">Administrateur</option>
          </select>
        </div>
      </form>
    </Modal>
  );
}

// ── Composant principal ────────────────────────────────────────────────────
export default function UsersSection({ currentUserId }) {
  const [users, setUsers]         = useState([]);
  const [initialLoading, setInitialLoading] = useState(true);
  const [err, setErr]             = useState(null);
  const [confirmAction, setConfirmAction] = useState(null);
  const [showCreate, setShowCreate] = useState(false);
  const [showInvite, setShowInvite] = useState(false);
  const [resendTarget, setResendTarget] = useState(null);
  const [banner, setBanner] = useState(null); // { ok, msg }
  const [search, setSearch]   = useState('');
  const [filterStatus, setFilterStatus] = useState('all');
  const [filterRole, setFilterRole]     = useState('all');
  const [openMenu, setOpenMenu] = useState(null);
  const [page, setPage]       = useState(1);
  const [pageSize, setPageSize] = useState(10);

  const load = useCallback(async (initial = false) => {
    if (initial) setInitialLoading(true);
    try { setUsers(await api.get('/admin/users')); }
    catch (e) { setErr(e.message); }
    finally { if (initial) setInitialLoading(false); }
  }, []);

  useEffect(() => { load(true); }, [load]);
  useEffect(() => { setPage(1); }, [search, filterStatus, filterRole]);
  useEffect(() => {
    if (!openMenu) return;
    const close = () => setOpenMenu(null);
    window.addEventListener('scroll', close, true);
    return () => window.removeEventListener('scroll', close, true);
  }, [openMenu]);

  // ── Filtrage ──────────────────────────────────────────────────────────
  const filtered = users.filter(u => {
    const q = search.toLowerCase();
    if (q && !u.email.toLowerCase().includes(q) && !(u.display_name || '').toLowerCase().includes(q)) return false;
    if (filterStatus !== 'all' && computedStatus(u) !== filterStatus) return false;
    if (filterRole !== 'all' && u.role !== filterRole) return false;
    return true;
  });

  const totalPages = Math.max(1, Math.ceil(filtered.length / pageSize));
  const safePage   = Math.min(page, totalPages);
  const paged      = filtered.slice((safePage - 1) * pageSize, safePage * pageSize);

  // ── Actions ───────────────────────────────────────────────────────────
  const confirm = (title, message, onConfirm, confirmLabel) =>
    setConfirmAction({ title, message, onConfirm, confirmLabel });

  const toggleRole = u => {
    const newRole = u.role === 'admin' ? 'user' : 'admin';
    confirm('Changer le rôle',
      `Changer le rôle de ${u.display_name || u.email} → ${newRole === 'admin' ? 'Administrateur' : 'Utilisateur'} ?`,
      async () => { await api.patch(`/admin/users/${u.id}/role`, { role: newRole }); load(); }, 'Confirmer');
  };
  const verifyEmail = u => confirm("Vérifier l'email",
    `Marquer l'email de ${u.display_name || u.email} comme vérifié ?`,
    async () => { await api.patch(`/admin/users/${u.id}/verify-email`, {}); load(); }, 'Confirmer');
  const resendInvitation = u => setResendTarget(u);
  const resendVerifEmail = async u => {
    try {
      await api.post('/auth/resend-verification', { email: u.email });
      setBanner({ ok: true, msg: `Email de vérification renvoyé à ${u.email}.` });
    } catch (e) { setBanner({ ok: false, msg: e.message }); }
  };
  const setStatus = (u, status) => {
    const labels = { deactivated: 'Désactiver', active: 'Réactiver', locked: 'Verrouiller' };
    confirm(`${labels[status]} le compte`, `${labels[status]} le compte de ${u.display_name || u.email} ?`,
      async () => { await api.patch(`/admin/users/${u.id}/status`, { status }); load(); }, labels[status]);
  };
  const deleteUser = u => {
    const identity = u.display_name ? `${u.display_name} (${u.email})` : u.email;
    confirm(
      "Supprimer l'utilisateur",
      `Supprimer définitivement le compte de ${identity} ? Cette action est irréversible et effacera toutes ses données.`,
      async () => { await api.del(`/admin/users/${u.id}`); load(); }
    );
  };

  const openMenuFor = (e, u) => {
    e.stopPropagation();
    const rect = e.currentTarget.getBoundingClientRect();
    setOpenMenu({ user: u, x: rect.right, y: rect.bottom });
  };

  // ── Pill dropdown custom ───────────────────────────────────────────────
  const PillDropdown = ({ label, value, options, onChange }) => {
    const [open, setOpen] = useState(false);
    const ref = useRef(null);
    const selected = options.find(o => o.value === value);
    useEffect(() => {
      if (!open) return;
      const h = e => { if (!ref.current?.contains(e.target)) setOpen(false); };
      document.addEventListener('mousedown', h);
      return () => document.removeEventListener('mousedown', h);
    }, [open]);
    return (
      <div ref={ref} style={{ position: 'relative' }}>
        <button onClick={() => setOpen(o => !o)} style={{
          display: 'inline-flex', alignItems: 'center', gap: 4,
          padding: '5px 10px', borderRadius: 20,
          border: `1px solid ${open ? 'var(--primary)' : 'var(--border)'}`,
          background: 'var(--surface)', cursor: 'pointer', fontSize: 'var(--fs-xs)', whiteSpace: 'nowrap',
        }}>
          <span style={{ color: 'var(--text-muted)' }}>{label} :</span>
          <span style={{ color: 'var(--text)', fontWeight: 500 }}>{selected?.label}</span>
          <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"
            style={{ color: 'var(--text-muted)', marginLeft: 2, transform: open ? 'rotate(180deg)' : 'none', transition: 'transform .15s' }}>
            <polyline points="6 9 12 15 18 9"/>
          </svg>
        </button>
        {open && (
          <div style={{
            position: 'absolute', top: 'calc(100% + 6px)', left: 0, zIndex: 200,
            background: 'var(--surface)', border: '1px solid var(--border)',
            borderRadius: 8, boxShadow: '0 4px 16px rgba(0,0,0,.12)',
            padding: '4px 0', minWidth: 160,
          }}>
            {options.map(o => (
              <button key={o.value} onClick={() => { onChange(o.value); setOpen(false); }} style={{
                display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                width: '100%', padding: '7px 14px', background: 'none', border: 'none',
                cursor: 'pointer', fontSize: 'var(--fs-sm)', textAlign: 'left',
                color: o.value === value ? 'var(--primary)' : 'var(--text)',
                fontWeight: o.value === value ? 500 : 400,
              }}
                onMouseEnter={e => { if (o.value !== value) e.currentTarget.style.background = 'var(--surface-2)'; }}
                onMouseLeave={e => e.currentTarget.style.background = 'none'}
              >
                {o.label}
                {o.value === value && (
                  <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
                    <polyline points="20 6 9 17 4 12"/>
                  </svg>
                )}
              </button>
            ))}
          </div>
        )}
      </div>
    );
  };

  const today = new Date().toISOString().slice(0, 10);

  if (initialLoading) return <p style={{ color: 'var(--text-muted)' }}>Chargement…</p>;
  if (err)     return <p style={{ color: '#ef4444' }}>{err}</p>;

  return (
    <>
      <div className="card" style={{ padding: 0, overflow: 'hidden' }}>

        {/* Bannière succès/erreur */}
        {banner && (
          <ResultBanner
            result={banner}
            onDismiss={() => setBanner(null)}
            style={{ margin: '12px 20px 0', borderRadius: 8 }}
          />
        )}

        {/* En-tête */}
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '20px 20px 14px', gap: 16, borderBottom: '1px solid var(--border)', flexWrap: 'wrap' }}>
          <div>
            <h3 style={{ margin: '0 0 3px' }}>Comptes utilisateurs</h3>
            <p style={{ margin: 0, fontSize: 'var(--fs-xs)', color: 'var(--text-muted)' }}>
              Gérez les comptes et les accès des membres de l'application.
            </p>
          </div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
            <div className="project-search-wrap" style={{ minWidth: 200 }}>
              <IcoSearch />
              <input className="project-search-input" type="search" placeholder="Rechercher…"
                value={search} onChange={e => setSearch(e.target.value)} />
              {search && (
                <button className="project-search-clear" onClick={() => setSearch('')}>
                  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
                </button>
              )}
            </div>
            <ExportDropdown
              onCSV={()   => dlBlob(toCSV(filtered),  `utilisateurs_${today}.csv`,  'text/csv;charset=utf-8')}
              onXLSX={() => dlBlob(toXLSX(filtered), `utilisateurs_${today}.xlsx`, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')}
              onJSON={() => dlBlob(toJSON(filtered),  `utilisateurs_${today}.json`, 'application/json')}
            />
            <button className="btn btn-outline btn-sm" onClick={() => setShowInvite(true)}
              style={{ display: 'flex', alignItems: 'center', gap: 6, whiteSpace: 'nowrap' }}>
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg>
              Inviter
            </button>
            <button className="btn btn-primary btn-sm" onClick={() => setShowCreate(true)}
              style={{ display: 'flex', alignItems: 'center', gap: 6, whiteSpace: 'nowrap' }}>
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="8.5" cy="7" r="4"/><line x1="20" y1="8" x2="20" y2="14"/><line x1="23" y1="11" x2="17" y2="11"/></svg>
              Ajouter un utilisateur
            </button>
          </div>
        </div>

        {/* Filtres pills */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '12px 20px', borderBottom: '1px solid var(--border)' }}>
          <PillDropdown label="Rôle"   value={filterRole}   options={ROLE_OPTIONS}   onChange={setFilterRole} />
          <PillDropdown label="Statut" value={filterStatus} options={STATUS_OPTIONS} onChange={setFilterStatus} />
          <span style={{ marginLeft: 'auto', fontSize: 'var(--fs-xs)', color: 'var(--text-muted)' }}>
            {filtered.length !== users.length
              ? `${filtered.length} / ${users.length} utilisateur${users.length !== 1 ? 's' : ''}`
              : `${users.length} utilisateur${users.length !== 1 ? 's' : ''}`
            }
          </span>
          {(filterRole !== 'all' || filterStatus !== 'all' || search) && (
            <button onClick={() => { setFilterRole('all'); setFilterStatus('all'); setSearch(''); }}
              style={{ fontSize: 'var(--fs-xs)', color: 'var(--text-muted)', background: 'none', border: 'none', cursor: 'pointer', padding: '4px 6px', borderRadius: 4 }}
              onMouseEnter={e => e.currentTarget.style.color = 'var(--danger)'}
              onMouseLeave={e => e.currentTarget.style.color = 'var(--text-muted)'}
            >Réinitialiser</button>
          )}
        </div>

        {/* Tableau */}
        {filtered.length === 0 ? (
          <p style={{ color: 'var(--text-muted)', textAlign: 'center', padding: '40px 20px' }}>
            Aucun utilisateur ne correspond aux filtres.
          </p>
        ) : (
          <div style={{ overflowX: 'auto' }}>
            <table style={{ margin: 0 }}>
              <thead>
                <tr>
                  <th style={{ paddingLeft: 20 }}>Utilisateur</th>
                  <th>Statut</th>
                  <th>Rôle</th>
                  <th>2FA</th>
                  <th>Inscrit le</th>
                  <th style={{ width: 48, paddingRight: 20 }}></th>
                </tr>
              </thead>
              <tbody>
                {paged.map(u => {
                  const [bg, fg] = avatarColor(u.display_name || u.email);
                  return (
                    <tr key={u.id} style={{ height: 62 }}>
                      <td style={{ paddingLeft: 20 }}>
                        <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
                          <div style={{
                            width: 38, height: 38, borderRadius: '50%', flexShrink: 0,
                            background: bg, color: fg,
                            display: 'flex', alignItems: 'center', justifyContent: 'center',
                            fontSize: 13, fontWeight: 700, letterSpacing: '.03em',
                          }}>
                            {initials(u)}
                          </div>
                          <div>
                            <div style={{ fontWeight: 600, fontSize: 'var(--fs-sm)', lineHeight: 1.3 }}>
                              {u.display_name || <em style={{ color: 'var(--text-muted)', fontStyle: 'normal' }}>{u.email.split('@')[0]}</em>}
                              {u.id === currentUserId && <span style={{ marginLeft: 6, fontSize: 10, color: 'var(--text-muted)', fontWeight: 400 }}>(vous)</span>}
                            </div>
                            <div style={{ fontSize: 11, color: 'var(--text-muted)', lineHeight: 1.3 }}>{u.email}</div>
                          </div>
                        </div>
                      </td>
                      <td><UserStatusBadge status={u.status || 'active'} emailVerified={u.email_verified} /></td>
                      <td><Badge role={u.role} /></td>
                      <td>
                        {u.totp_enabled
                          ? <span style={{ fontSize: 11, fontWeight: 600, padding: '2px 8px', borderRadius: 10, background: '#eff6ff', color: '#1d4ed8', border: '1px solid #bfdbfe' }}>🔐 Actif</span>
                          : <span style={{ color: 'var(--text-muted)' }}>—</span>
                        }
                      </td>
                      <td style={{ color: 'var(--text-muted)', fontSize: 12 }}>{fmt(u.created_at)}</td>
                      <td style={{ paddingRight: 20 }}>
                        <button onClick={e => openMenuFor(e, u)} style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--text-muted)', padding: '4px 6px', borderRadius: 4, display: 'flex', alignItems: 'center' }}
                          onMouseEnter={e => { e.currentTarget.style.background = 'var(--surface-2)'; e.currentTarget.style.color = 'var(--text)'; }}
                          onMouseLeave={e => { e.currentTarget.style.background = 'none'; e.currentTarget.style.color = 'var(--text-muted)'; }}>
                          <IcoDots />
                        </button>
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
            <div style={{ padding: '0 20px 16px' }}>
              <AdminPagination page={safePage} setPage={setPage} pageSize={pageSize} setPageSize={setPageSize} total={filtered.length} />
            </div>
          </div>
        )}
      </div>

      {/* Menu ⋮ contextuel */}
      {openMenu && (
        <>
          <div style={{ position: 'fixed', inset: 0, zIndex: 299 }} onClick={() => setOpenMenu(null)} />
          <div style={{
            position: 'fixed', left: openMenu.x, top: openMenu.y,
            transform: 'translateX(-100%) translateY(4px)', zIndex: 300,
            background: 'var(--surface)', border: '1px solid var(--border)',
            borderRadius: 8, boxShadow: '0 4px 20px rgba(0,0,0,0.15)', padding: '4px 0', minWidth: 200,
          }}>
            <MenuBtn icon={<IcoEdit />} label={openMenu.user.role === 'admin' ? 'Rétrograder en utilisateur' : 'Promouvoir en admin'} onClick={() => { setOpenMenu(null); toggleRole(openMenu.user); }} />
            {!openMenu.user.email_verified && <>
              <MenuBtn icon={<IcoMail />} label="Renvoyer l'invitation" onClick={() => { setOpenMenu(null); resendInvitation(openMenu.user); }} />
              <MenuBtn icon={<IcoCheck />} label="Vérifier l'email" onClick={() => { setOpenMenu(null); verifyEmail(openMenu.user); }} />
              <MenuBtn icon={<IcoMail />} label="Nouvelle demande de vérification" onClick={() => { setOpenMenu(null); resendVerifEmail(openMenu.user); }} />
            </>}
            {openMenu.user.id !== currentUserId && <>
              {(!openMenu.user.status || openMenu.user.status === 'active') && <MenuBtn icon={<IcoPause />} label="Désactiver" onClick={() => { setOpenMenu(null); setStatus(openMenu.user, 'deactivated'); }} />}
              {openMenu.user.status === 'deactivated' && <MenuBtn icon={<IcoPlay />} label="Réactiver" onClick={() => { setOpenMenu(null); setStatus(openMenu.user, 'active'); }} />}
              {openMenu.user.status === 'locked' && <MenuBtn icon={<IcoPlay />} label="Déverrouiller" onClick={() => { setOpenMenu(null); setStatus(openMenu.user, 'active'); }} />}
              <div style={{ height: 1, background: 'var(--border)', margin: '4px 0' }} />
              <MenuBtn icon={<IcoTrash />} label="Supprimer" danger onClick={() => { setOpenMenu(null); deleteUser(openMenu.user); }} />
            </>}
          </div>
        </>
      )}

      <InviteUserModal
        open={showInvite}
        onClose={() => setShowInvite(false)}
        onSent={() => load()}
      />

      <ResendInviteModal
        user={resendTarget}
        onClose={() => setResendTarget(null)}
        onSuccess={email => {
          load();
          setBanner({ ok: true, msg: `Invitation renvoyée à ${email}. Le lien est valable 7 jours.` });
        }}
      />

      <CreateUserModal
        open={showCreate}
        onClose={() => setShowCreate(false)}
        onCreated={() => load()}
      />

      <ConfirmModal
        open={!!confirmAction}
        title={confirmAction?.title}
        message={confirmAction?.message}
        confirmLabel={confirmAction?.confirmLabel}
        onConfirm={async () => {
          try { await confirmAction.onConfirm(); }
          catch (e) { setErr(e.message); }
          finally { setConfirmAction(null); }
        }}
        onCancel={() => setConfirmAction(null)}
      />
    </>
  );
}
