/* global React, Badge, Btn, Dot, Icon, RefChip, Sigil, formatTS */
// CommunicationPage — Boîte unifiée IMAP + Composer + Config
// Câblée sur /api/comm/* (GET inbox, emails/:id, stats, POST reprocess)
// Règle 5 : CRUD complet (read détail + reprocess + lien contact/opp)
// Règle 6 : onglet Configuration OAuth2

const { useState: _cpUseS, useEffect: _cpUseE, useRef: _cpUseR, useMemo: _cpUseM } = React;

const API = () => (window.AE_API && window.AE_API.BASE) || '';

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

function timeAgo(iso) {
  if (!iso) return '—';
  const m = (Date.now() - new Date(iso).getTime()) / 60000;
  if (m < 1) return 'maintenant';
  if (m < 60) return `${Math.round(m)} min`;
  if (m < 1440) return `${Math.round(m / 60)} h`;
  if (m < 10080) return `${Math.round(m / 1440)} j`;
  return new Date(iso).toLocaleDateString('fr-FR', { day: '2-digit', month: 'short' });
}

function sizeKb(bytes) {
  if (!bytes) return '—';
  if (bytes < 1024) return bytes + ' o';
  if (bytes < 1048576) return (bytes / 1024).toFixed(0) + ' Ko';
  return (bytes / 1048576).toFixed(1) + ' Mo';
}

// Helper bouton compact pour les actions email
function btnStyle(tone) {
  const t = {
    red:        { bg: 'var(--rouge-tint)', fg: 'var(--rouge)', border: 'var(--rouge)' },
    'red-strong': { bg: 'var(--rouge)',     fg: '#fff',         border: 'var(--rouge)' },
    amber:      { bg: 'rgba(245,158,11,.1)', fg: '#92400E',     border: 'rgba(245,158,11,.45)' },
    default:    { bg: 'var(--paper)',       fg: 'var(--ink-2)', border: 'var(--line-2)' },
  }[tone] || { bg: 'var(--paper)', fg: 'var(--ink-2)', border: 'var(--line-2)' };
  return {
    padding: '4px 10px', fontSize: 11, fontWeight: 500,
    background: t.bg, color: t.fg,
    border: '1px solid ' + t.border, borderRadius: 4,
    cursor: 'pointer',
  };
}

const SENDER_META = {
  boss:    { label: 'Direction',  tone: 'plasma', bg: 'var(--plasma-tint)', border: 'var(--plasma-tint)' },
  client:  { label: 'Client',     tone: 'signal', bg: 'var(--signal-tint)', border: 'var(--signal-soft)' },
  partner: { label: 'Partenaire', tone: 'copper', bg: 'var(--copper-tint)', border: 'var(--copper-tint)' },
  unknown: { label: 'Inconnu',    tone: 'muted',  bg: 'var(--paper-2)',     border: 'var(--line)' },
};

const STATUS_META = {
  processed:  { label: 'Traité',     tone: 'signal' },
  pending:    { label: 'En attente', tone: 'amber' },
  error:      { label: 'Erreur',     tone: 'rouge' },
  ignored:    { label: 'Ignoré',     tone: 'muted' },
};

// ── Main page ─────────────────────────────────────────────────────────────────

function CommunicationPage() {
  const [tab, setTab] = _cpUseS('inbox');       // inbox | compose | config
  const [selectedId, setSelectedId] = _cpUseS(null);
  const [selectedFolder, setSelectedFolder] = _cpUseS('INBOX');
  const [refreshKey, setRefreshKey] = _cpUseS(0);
  const [scanning, setScanning] = _cpUseS(false);
  const [scanReport, setScanReport] = _cpUseS(null);

  const refresh = () => setRefreshKey(k => k + 1);

  const scanProspects = async () => {
    if (scanning) return;
    if (!window.confirm('Scanner toute la boîte mail et créer automatiquement des fiches prospects (Contacts + Organisations) pour tous les expéditeurs inconnus ?')) return;
    setScanning(true);
    try {
      const r = await fetch(API() + '/api/comm/scan-prospects', { method: 'POST' }).then(r => r.json());
      setScanReport(r);
      // Déclenche un re-hydrate global pour que CRM/Contacts voient les nouveaux prospects
      if (window.AE_API?.hydrate) window.AE_API.hydrate();
      refresh();
    } catch (e) {
      setScanReport({ status: 'error', error: e.message });
    } finally {
      setScanning(false);
    }
  };

  return (
    <div style={{ padding: '20px 24px 40px' }}>
      {/* Header */}
      <div style={{ display: 'flex', alignItems: 'flex-end', gap: 16, marginBottom: 18 }}>
        <div>
          <div style={{ fontSize: 11, color: 'var(--ink-4)', textTransform: 'uppercase', letterSpacing: 0.8, fontWeight: 600 }}>IMAP · Gmail · Outlook · Slack</div>
          <h1 style={{ fontSize: 26, fontWeight: 600, letterSpacing: '-0.02em', margin: '4px 0 0' }}>Communication</h1>
          <div style={{ fontSize: 13, color: 'var(--ink-3)', marginTop: 4 }}>Boîte unifiée · Match auto contact & opportunité · Pièces jointes · Intégration Odoo.</div>
        </div>
        <span style={{ flex: 1 }}/>
        <Btn variant="outline" size="sm" icon={<Icon.refresh/>} onClick={refresh}>Actualiser</Btn>
        <Btn variant="outline" size="sm" onClick={scanProspects} disabled={scanning}>
          {scanning ? '⏳ Scan en cours…' : '🔍 Scanner → Prospects'}
        </Btn>
        <Btn variant="signal" size="sm" icon={<Icon.plus/>} onClick={() => setTab('compose')}>Composer</Btn>
      </div>

      {scanReport && <ScanReportModal report={scanReport} onClose={() => setScanReport(null)} />}

      {/* Tabs */}
      <div style={{ display: 'flex', gap: 2, padding: 2, background: 'var(--paper-2)', border: '1px solid var(--line)', borderRadius: 6, width: 'fit-content', marginBottom: 20 }}>
        {[
          { key: 'inbox',   label: '📬 Boîte unifiée' },
          { key: 'compose', label: '✍️ Composer'      },
          { key: 'config',  label: '⚙️ Configuration' },
        ].map(t => (
          <button key={t.key} onClick={() => setTab(t.key)} style={{
            padding: '6px 14px', fontSize: 12, fontWeight: 500, borderRadius: 4, cursor: 'pointer',
            background: tab === t.key ? 'var(--paper)' : 'transparent',
            color: tab === t.key ? 'var(--ink)' : 'var(--ink-4)',
            border: tab === t.key ? '1px solid var(--line)' : '1px solid transparent',
          }}>{t.label}</button>
        ))}
      </div>

      {tab === 'inbox'   && <InboxTab selectedId={selectedId} onSelect={setSelectedId} refreshKey={refreshKey} onRefresh={refresh} selectedFolder={selectedFolder} setSelectedFolder={setSelectedFolder} />}
      {tab === 'compose' && <ComposeTab onSent={() => { setTab('inbox'); refresh(); }} />}
      {tab === 'config'  && <ConfigTab refreshKey={refreshKey} />}
    </div>
  );
}

// ── Scan Report Modal ──────────────────────────────────────────────────────
// Affiche le rapport du POST /api/comm/scan-prospects : nb emails scannés,
// prospects créés, organisations créées, liste détaillée avec nav CRM.
function ScanReportModal({ report, onClose }) {
  const isError = report?.status === 'error';
  const prospects = report?.prospects || [];
  const created = prospects.filter(p => p.status === 'created');
  const existing = prospects.filter(p => p.status === 'existing');

  return (
    <div onClick={e => { if (e.target === e.currentTarget) onClose(); }}
      style={{ position: 'fixed', inset: 0, zIndex: 100, background: 'rgba(14,16,16,0.6)', display: 'grid', placeItems: 'center', backdropFilter: 'blur(3px)', padding: 16 }}>
      <div style={{ width: 'min(820px, 100%)', maxHeight: '90vh', overflowY: 'auto', background: 'var(--paper)', border: '1px solid var(--line-2)', borderRadius: 12, boxShadow: 'var(--shadow-3)' }}>
        {/* Header */}
        <div style={{ padding: '16px 22px', borderBottom: '1px solid var(--line)', display: 'flex', alignItems: 'center', gap: 10, position: 'sticky', top: 0, background: 'var(--paper)' }}>
          <span style={{ fontSize: 20 }}>{isError ? '⚠️' : '🔍'}</span>
          <div style={{ flex: 1 }}>
            <div style={{ fontSize: 15, fontWeight: 700 }}>{isError ? 'Erreur scan boîte mail' : 'Rapport de scan — Prospects extraits'}</div>
            <div style={{ fontSize: 11, color: 'var(--ink-4)', marginTop: 2 }}>
              {isError ? report.error : `Analyse automatique · extraction entités · upsert Contact + Organization · source="email_scan"`}
            </div>
          </div>
          <button onClick={onClose} style={{ fontSize: 18, color: 'var(--ink-4)', padding: 4 }}>✕</button>
        </div>

        {isError ? (
          <div style={{ padding: 22 }}>
            <div style={{ padding: 16, background: 'var(--rouge-tint)', border: '1px solid var(--rouge)', borderRadius: 6, fontSize: 13, color: 'var(--rouge)' }}>
              {report.error}
            </div>
          </div>
        ) : (
          <div style={{ padding: '16px 22px' }}>
            {/* KPI strip */}
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: 1, border: '1px solid var(--line)', borderRadius: 6, background: 'var(--line)', marginBottom: 18 }}>
              <KpiCell label="Emails scannés" value={report.scanned || 0} tone="ink"/>
              <KpiCell label="Expéditeurs uniques" value={report.uniqueSenders || 0} tone="ink"/>
              <KpiCell label="Prospects créés" value={report.created || 0} tone="signal" highlight/>
              <KpiCell label="Orgs créées" value={report.createdOrgs || 0} tone="plasma"/>
              <KpiCell label="Déjà existants" value={report.existing || 0} tone="muted"/>
            </div>

            {report.skipped > 0 && (
              <div style={{ marginBottom: 16, padding: '8px 12px', background: 'var(--paper-2)', border: '1px solid var(--line)', borderRadius: 5, fontSize: 11, color: 'var(--ink-4)' }}>
                ℹ {report.skipped} expéditeur{report.skipped > 1 ? 's' : ''} ignoré{report.skipped > 1 ? 's' : ''} (no-reply, postmaster, bounces, notifications automatiques…)
              </div>
            )}

            {/* Nouveaux prospects */}
            {created.length > 0 && (
              <div style={{ marginBottom: 18 }}>
                <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--signal-deep)', textTransform: 'uppercase', letterSpacing: 0.5, marginBottom: 8 }}>
                  ✓ {created.length} nouveaux prospects créés
                </div>
                <div style={{ border: '1px solid var(--line)', borderRadius: 6, overflow: 'hidden' }}>
                  <div style={{ maxHeight: 320, overflowY: 'auto' }}>
                    {created.map(p => (
                      <div key={p.email} style={{ display: 'grid', gridTemplateColumns: '1fr auto auto', gap: 10, padding: '10px 14px', borderBottom: '1px solid var(--hairline)', fontSize: 12, alignItems: 'center' }}>
                        <div style={{ minWidth: 0 }}>
                          <div style={{ fontWeight: 600, color: 'var(--ink)' }}>{p.name || p.email}</div>
                          <div style={{ fontSize: 11, color: 'var(--ink-4)', marginTop: 2 }}>
                            {p.email}
                            {p.company && <> · <span style={{ color: 'var(--plasma)' }}>🏢 {p.company}</span></>}
                            {p.phone && <> · <span className="mono">📞 {p.phone}</span></>}
                            {p.siret && <> · <span className="mono">SIRET {p.siret}</span></>}
                          </div>
                        </div>
                        <span style={{ fontSize: 10, padding: '2px 7px', background: 'var(--signal-tint)', color: 'var(--signal-deep)', borderRadius: 3, fontWeight: 600, whiteSpace: 'nowrap' }}>
                          {p.emailCount} email{p.emailCount > 1 ? 's' : ''}
                        </span>
                        <button onClick={() => { window.AE_NAV?.('crm'); onClose(); }}
                          style={{ fontSize: 10, padding: '4px 9px', background: 'var(--paper)', border: '1px solid var(--line-2)', borderRadius: 3, color: 'var(--ink-2)', whiteSpace: 'nowrap' }}>
                          → CRM ↗
                        </button>
                      </div>
                    ))}
                  </div>
                </div>
              </div>
            )}

            {/* Déjà existants (collapsed) */}
            {existing.length > 0 && (
              <details style={{ marginBottom: 12 }}>
                <summary style={{ cursor: 'pointer', fontSize: 11, color: 'var(--ink-4)', fontWeight: 600, textTransform: 'uppercase', letterSpacing: 0.5, padding: '8px 0' }}>
                  {existing.length} expéditeur{existing.length > 1 ? 's' : ''} déjà en base (pas re-créé{existing.length > 1 ? 's' : ''})
                </summary>
                <div style={{ border: '1px solid var(--line)', borderRadius: 6, overflow: 'hidden', marginTop: 8, maxHeight: 240, overflowY: 'auto' }}>
                  {existing.map(p => (
                    <div key={p.email} style={{ display: 'grid', gridTemplateColumns: '1fr auto', gap: 10, padding: '7px 14px', borderBottom: '1px solid var(--hairline)', fontSize: 11 }}>
                      <div>
                        <span style={{ fontWeight: 500 }}>{p.name || '—'}</span>
                        <span style={{ color: 'var(--ink-4)', marginLeft: 6 }}>· {p.email}</span>
                      </div>
                      <span style={{ fontSize: 10, color: 'var(--ink-4)' }}>Contact #{p.contactId}</span>
                    </div>
                  ))}
                </div>
              </details>
            )}

            {created.length === 0 && existing.length === 0 && (
              <div style={{ padding: 32, textAlign: 'center', color: 'var(--ink-4)' }}>
                <div style={{ fontSize: 32, marginBottom: 10 }}>📭</div>
                <div style={{ fontSize: 13 }}>Aucun expéditeur éligible trouvé dans la boîte mail.</div>
                <div style={{ fontSize: 11, marginTop: 4 }}>Configurez d'abord la connexion IMAP et attendez que des emails soient reçus.</div>
              </div>
            )}
          </div>
        )}

        {/* Footer */}
        <div style={{ padding: '12px 22px', borderTop: '1px solid var(--line)', background: 'var(--paper-2)', display: 'flex', alignItems: 'center', gap: 10 }}>
          <span style={{ fontSize: 10, color: 'var(--ink-4)', flex: 1 }}>
            Les prospects créés sont visibles immédiatement dans le module <strong>CRM Clients</strong> avec le tag <code>email_scan</code>.
          </span>
          {created.length > 0 && (
            <Btn variant="signal" size="sm" onClick={() => { window.AE_NAV?.('crm'); onClose(); }}>→ Voir dans CRM</Btn>
          )}
          <Btn variant="outline" size="sm" onClick={onClose}>Fermer</Btn>
        </div>
      </div>
    </div>
  );
}

function KpiCell({ label, value, tone, highlight }) {
  const color = tone === 'signal' ? 'var(--signal-deep)'
    : tone === 'plasma' ? 'var(--plasma)'
    : tone === 'muted' ? 'var(--ink-4)'
    : 'var(--ink)';
  return (
    <div style={{ padding: '10px 12px', background: highlight ? 'var(--signal-tint)' : 'var(--paper)' }}>
      <div style={{ fontSize: 10, color: 'var(--ink-4)', textTransform: 'uppercase', letterSpacing: 0.5, fontWeight: 600 }}>{label}</div>
      <div style={{ fontSize: 20, fontWeight: 700, color, marginTop: 3 }}>{value}</div>
    </div>
  );
}

// ── Inbox tab ─────────────────────────────────────────────────────────────────

function InboxTab({ selectedId, onSelect, refreshKey, onRefresh, selectedFolder = 'INBOX', setSelectedFolder }) {
  const [messages, setMessages] = _cpUseS([]);
  const [total, setTotal] = _cpUseS(0);
  const [page, setPage] = _cpUseS(1);
  const [pages, setPages] = _cpUseS(1);
  const [loading, setLoading] = _cpUseS(false);
  const [search, setSearch] = _cpUseS('');
  const [filterStatus, setFilterStatus] = _cpUseS('');
  const [filterSender, setFilterSender] = _cpUseS('');
  const [stats, setStats] = _cpUseS(null);

  // Charger stats
  _cpUseE(() => {
    fetch(API() + '/api/comm/stats').then(r => r.json()).then(d => setStats(d)).catch(() => {});
  }, [refreshKey]);

  // Charger inbox OU dossier IMAP selon selection
  _cpUseE(() => {
    let cancelled = false;
    setLoading(true);

    // INBOX → DB locale (avec matching contacts/opps)
    // Autres dossiers → fetch IMAP live (Sent, Drafts, Spam, Trash, Archives…)
    const isInbox = !selectedFolder || selectedFolder === 'INBOX';

    let url, mapper;
    if (isInbox) {
      const qs = new URLSearchParams({ page, limit: 25 });
      if (filterStatus) qs.set('status', filterStatus);
      if (filterSender) qs.set('senderType', filterSender);
      if (search) qs.set('search', search);
      url = API() + '/api/comm/inbox?' + qs;
      mapper = (d) => ({ messages: d.messages || [], total: d.total || 0, pages: d.pages || 1 });
    } else {
      const qs = new URLSearchParams({ limit: 50 });
      if (search) qs.set('search', search);
      url = API() + '/api/comm/folder/' + encodeURIComponent(selectedFolder) + '/messages?' + qs;
      mapper = (d) => ({
        // Normalise la réponse IMAP au format Email DB pour réutiliser MessageRow
        messages: (d.messages || []).map(m => ({
          id: 'imap:' + selectedFolder + ':' + m.uid,  // pseudo-id pour key React
          uid: m.uid,
          imapMailbox: selectedFolder,
          messageId: m.messageId,
          fromAddress: m.fromAddress, fromName: m.fromName,
          toAddress: m.toAddress,
          subject: m.subject,
          receivedAt: m.receivedAt,
          status: m.isRead ? 'matched' : 'pending',
          senderType: 'client',
        })),
        total: d.total || 0, pages: 1,
      });
    }

    fetch(url).then(r => r.json()).then(d => {
      if (cancelled) return;
      const out = mapper(d);
      setMessages(out.messages);
      setTotal(out.total);
      setPages(out.pages);
    }).catch(() => {
      if (!cancelled) setMessages([]);
    }).finally(() => { if (!cancelled) setLoading(false); });
    return () => { cancelled = true; };
  }, [page, filterStatus, filterSender, search, refreshKey, selectedFolder]);

  // Stats strip
  const byStatus = _cpUseM(() => {
    const m = {};
    (stats?.byStatus || []).forEach(s => { m[s.status] = Number(s.count); });
    return m;
  }, [stats]);

  const bySender = _cpUseM(() => {
    const m = {};
    (stats?.bySenderType || []).forEach(s => { m[s.senderType] = Number(s.count); });
    return m;
  }, [stats]);

  return (
    <div>
      {/* Stats strip */}
      {stats && (
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: 1, border: '1px solid var(--line)', borderRadius: 6, background: 'var(--line)', marginBottom: 16 }}>
          {[
            { label: 'Total reçus', value: total || 0, mono: true },
            { label: 'Traités', value: byStatus.processed || 0, tone: 'signal', mono: true },
            { label: 'En attente', value: byStatus.pending || 0, tone: 'amber', mono: true },
            { label: 'Direction', value: bySender.boss || 0, tone: 'plasma', mono: true },
            { label: 'Clients', value: bySender.client || 0, tone: 'signal', mono: true },
          ].map((s, i) => (
            <div key={i} style={{ padding: '10px 14px', background: 'var(--paper)' }}>
              <div style={{ fontSize: 10, color: 'var(--ink-4)', textTransform: 'uppercase', letterSpacing: 0.5, fontWeight: 600, display: 'flex', alignItems: 'center', gap: 5 }}>
                {s.tone && <Dot tone={s.tone} size={6}/>}
                {s.label}
              </div>
              <div className={s.mono ? 'mono' : ''} style={{ fontSize: 18, fontWeight: 700, marginTop: 3 }}>{s.value}</div>
            </div>
          ))}
        </div>
      )}

      {/* Filters */}
      <div style={{ display: 'flex', gap: 8, alignItems: 'center', marginBottom: 12 }}>
        <input
          value={search}
          onChange={e => { setSearch(e.target.value); setPage(1); }}
          placeholder="Rechercher (objet, expéditeur…)"
          style={{ flex: 1, padding: '6px 10px', fontSize: 12, border: '1px solid var(--line-2)', borderRadius: 5, background: 'var(--paper)', color: 'var(--ink)', fontFamily: 'inherit' }}
        />
        <select value={filterSender} onChange={e => { setFilterSender(e.target.value); setPage(1); }} style={{ padding: '6px 10px', fontSize: 12, border: '1px solid var(--line-2)', borderRadius: 5, background: 'var(--paper)', color: 'var(--ink-2)', fontFamily: 'inherit' }}>
          <option value="">Tous expéditeurs</option>
          <option value="boss">Direction</option>
          <option value="client">Clients</option>
          <option value="partner">Partenaires</option>
          <option value="unknown">Inconnus</option>
        </select>
        <select value={filterStatus} onChange={e => { setFilterStatus(e.target.value); setPage(1); }} style={{ padding: '6px 10px', fontSize: 12, border: '1px solid var(--line-2)', borderRadius: 5, background: 'var(--paper)', color: 'var(--ink-2)', fontFamily: 'inherit' }}>
          <option value="">Tous statuts</option>
          <option value="processed">Traités</option>
          <option value="pending">En attente</option>
          <option value="error">Erreurs</option>
        </select>
      </div>

      {/* Master-detail layout : Folders | List | Detail */}
      <div style={{ display: 'grid', gridTemplateColumns: '200px 380px 1fr', gap: 14, minHeight: 520 }}>
        {/* Folders sidebar (IMAP) */}
        <FoldersSidebar
          activeFolder={selectedFolder}
          onSelect={(f) => {
            setSelectedFolder?.(f);
            setFilterStatus('');
            setPage(1);
            onSelect(null);  // déselect message courant lors du changement de dossier
          }}
        />
        {/* Left — message list */}
        <div style={{ border: '1px solid var(--line)', borderRadius: 6, background: 'var(--paper)', overflow: 'hidden', display: 'flex', flexDirection: 'column' }}>
          <div style={{ padding: '8px 12px', borderBottom: '1px solid var(--hairline)', background: 'var(--paper-2)', fontSize: 10, fontWeight: 600, color: 'var(--ink-4)', textTransform: 'uppercase', letterSpacing: 0.5, display: 'flex', alignItems: 'center', gap: 8 }}>
            <span>Messages</span>
            <span className="mono" style={{ background: 'var(--line)', padding: '1px 5px', borderRadius: 10 }}>{total}</span>
            {loading && <Dot tone="plasma" pulse size={5}/>}
          </div>
          <div style={{ flex: 1, overflow: 'auto' }}>
            {loading && messages.length === 0 && window.Skeleton && (
              <div style={{ padding: 12 }}><window.Skeleton kind="rows" count={8} height={64}/></div>
            )}
            {!loading && messages.length === 0 && (
              window.EmptyState ? (
                <window.EmptyState
                  icon="📭"
                  title={search ? 'Aucun message trouvé' : 'Boîte vide'}
                  sub={search ? `Aucun email ne correspond à "${search}". Essayez d'autres mots-clés.` : 'Vos emails apparaîtront ici une fois la connexion IMAP configurée. En attendant, le module Communication reste prêt pour la composition.'}
                />
              ) : (
                <div style={{ padding: 40, textAlign: 'center', color: 'var(--ink-4)', fontSize: 13 }}>
                  <div style={{ fontSize: 32, marginBottom: 10 }}>📭</div>
                  <div>Aucun message{search ? ' pour cette recherche' : ''}</div>
                </div>
              )
            )}
            {messages.map(m => (
              <MessageRow key={m.id} msg={m} selected={selectedId === m.id} onClick={() => onSelect(m.id)}/>
            ))}
          </div>
          {/* Pagination */}
          {pages > 1 && (
            <div style={{ padding: '8px 12px', borderTop: '1px solid var(--hairline)', display: 'flex', alignItems: 'center', gap: 6 }}>
              <button onClick={() => setPage(p => Math.max(1, p - 1))} disabled={page <= 1} style={{ padding: '3px 8px', fontSize: 11, border: '1px solid var(--line-2)', borderRadius: 4, background: 'var(--paper)', cursor: 'pointer' }}>←</button>
              <span style={{ flex: 1, textAlign: 'center', fontSize: 11, color: 'var(--ink-4)' }} className="mono">p.{page}/{pages}</span>
              <button onClick={() => setPage(p => Math.min(pages, p + 1))} disabled={page >= pages} style={{ padding: '3px 8px', fontSize: 11, border: '1px solid var(--line-2)', borderRadius: 4, background: 'var(--paper)', cursor: 'pointer' }}>→</button>
            </div>
          )}
        </div>

        {/* Right — detail */}
        <div>
          {selectedId ? (
            <EmailDetail id={selectedId} onReprocess={onRefresh}/>
          ) : (
            <div style={{ border: '1px solid var(--line)', borderRadius: 6, background: 'var(--paper)', height: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 12, color: 'var(--ink-4)' }}>
              <div style={{ fontSize: 40 }}>📨</div>
              <div style={{ fontSize: 13 }}>Sélectionnez un message</div>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

function MessageRow({ msg, selected, onClick }) {
  const sender = SENDER_META[msg.senderType] || SENDER_META.unknown;
  const statusM = STATUS_META[msg.status] || STATUS_META.pending;
  const initials = (msg.fromName || msg.fromAddress || '?').slice(0, 2).toUpperCase();
  return (
    <button onClick={onClick} style={{
      width: '100%', padding: '10px 12px', textAlign: 'left', cursor: 'pointer',
      display: 'flex', gap: 10, alignItems: 'flex-start',
      borderBottom: '1px solid var(--hairline)',
      background: selected ? 'var(--paper-2)' : 'transparent',
      borderLeft: selected ? '2px solid var(--signal)' : '2px solid transparent',
    }}>
      {/* Avatar */}
      <div style={{
        width: 30, height: 30, borderRadius: '50%', flexShrink: 0,
        background: sender.bg, border: `1px solid ${sender.border}`,
        display: 'grid', placeItems: 'center', fontSize: 10, fontWeight: 700,
        color: `var(--${sender.tone})`,
      }}>{initials}</div>
      {/* Content */}
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 5, marginBottom: 3 }}>
          <span style={{ fontSize: 12, fontWeight: 600, color: 'var(--ink)', flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
            {msg.fromName || msg.fromAddress}
          </span>
          <span className="mono" style={{ fontSize: 10, color: 'var(--ink-5)', flexShrink: 0 }}>{timeAgo(msg.receivedAt)}</span>
        </div>
        <div style={{ fontSize: 12, color: 'var(--ink-2)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', marginBottom: 4 }}>
          {msg.subject || '(sans objet)'}
        </div>
        <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap' }}>
          <Badge tone={sender.tone} style={{ fontSize: 9 }}>{sender.label}</Badge>
          {msg.status !== 'processed' && <Badge tone={statusM.tone} style={{ fontSize: 9 }}>{statusM.label}</Badge>}
          {msg.priority === 'high' && <Badge tone="rouge" style={{ fontSize: 9 }}>Priorité haute</Badge>}
          {msg.attachments?.length > 0 && (
            <span style={{ fontSize: 9, color: 'var(--ink-4)', display: 'flex', alignItems: 'center', gap: 2 }}>
              📎 {msg.attachments.length}
            </span>
          )}
        </div>
      </div>
    </button>
  );
}

// ── Email detail panel ────────────────────────────────────────────────────────

function EmailDetail({ id, onReprocess, onDeleted }) {
  const [email, setEmail] = _cpUseS(null);
  const [loading, setLoading] = _cpUseS(true);
  const [reprocessing, setReprocessing] = _cpUseS(false);
  const [reprocessResult, setReprocessResult] = _cpUseS(null);
  const [actionMsg, setActionMsg] = _cpUseS(null);
  const [deleting, setDeleting] = _cpUseS(false);

  async function doDelete(permanent = false) {
    if (!email) return;
    const confirmMsg = permanent
      ? '⚠ Supprimer DÉFINITIVEMENT cet email (DB locale + serveur IMAP) ?\nIrréversible.'
      : 'Déplacer cet email vers la corbeille (DB locale + serveur IMAP) ?';
    if (!confirm(confirmMsg)) return;
    setDeleting(true); setActionMsg(null);
    try {
      const r = await fetch(API() + `/api/comm/emails/${email.id}`, {
        method: 'DELETE',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ permanent }),
      }).then(r => r.json());
      if (r.ok) {
        setActionMsg('✓ Supprimé' + (r.imap?.ok ? ' (IMAP OK)' : ' (DB seulement)'));
        if (onDeleted) onDeleted(email.id);
        if (onReprocess) onReprocess();
      } else {
        setActionMsg('⚠ ' + (r.error || 'Erreur suppression'));
      }
    } catch (e) { setActionMsg('⚠ ' + e.message); }
    setDeleting(false);
    setTimeout(() => setActionMsg(null), 4000);
  }

  async function doMarkRead(read) {
    if (!email) return;
    try {
      await fetch(API() + `/api/comm/emails/${email.id}`, {
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ read }),
      });
      setActionMsg(read ? '✓ Marqué comme lu' : '✓ Marqué comme non-lu');
    } catch (e) { setActionMsg('⚠ ' + e.message); }
    setTimeout(() => setActionMsg(null), 3000);
  }

  async function doSpam() {
    if (!email) return;
    if (!confirm('Marquer cet email comme spam et le déplacer dans Junk côté IMAP ?')) return;
    try {
      const r = await fetch(API() + `/api/comm/emails/${email.id}/spam`, { method: 'POST' }).then(r => r.json());
      setActionMsg(r.ok ? '✓ Déplacé en spam' : '⚠ ' + (r.imap?.reason || 'Erreur'));
      if (onReprocess) onReprocess();
    } catch (e) { setActionMsg('⚠ ' + e.message); }
    setTimeout(() => setActionMsg(null), 3000);
  }

  async function doReply(replyAll = false) {
    if (!email) return;
    try {
      const r = await fetch(API() + `/api/comm/emails/${email.id}/reply`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ replyAll }),
      }).then(r => r.json());
      if (r.ok && r.draft && window.commComposeRef?.setReply) {
        window.commComposeRef.setReply({
          to: r.draft.to,
          cc: r.draft.cc || '',
          subject: r.draft.subject,
          body: r.draft.body,
        });
        setActionMsg('✓ Brouillon créé — onglet Composer');
      }
    } catch (e) { setActionMsg('⚠ ' + e.message); }
    setTimeout(() => setActionMsg(null), 3000);
  }

  async function doForward() {
    if (!email) return;
    try {
      const r = await fetch(API() + `/api/comm/emails/${email.id}/forward`, { method: 'POST' }).then(r => r.json());
      if (r.ok && r.draft && window.commComposeRef?.setReply) {
        window.commComposeRef.setReply({
          to: r.draft.to, subject: r.draft.subject, body: r.draft.body,
        });
        setActionMsg('✓ Brouillon transfert créé — onglet Composer');
      }
    } catch (e) { setActionMsg('⚠ ' + e.message); }
    setTimeout(() => setActionMsg(null), 3000);
  }

  _cpUseE(() => {
    if (!id) return;
    let cancelled = false;
    setLoading(true);
    setEmail(null);
    fetch(API() + `/api/comm/emails/${id}`).then(r => r.json()).then(d => {
      if (!cancelled) setEmail(d);
    }).catch(() => {}).finally(() => { if (!cancelled) setLoading(false); });
    return () => { cancelled = true; };
  }, [id]);

  async function doReprocess() {
    if (!email) return;
    setReprocessing(true);
    setReprocessResult(null);
    try {
      const r = await fetch(API() + `/api/comm/reprocess/${email.id}`, { method: 'POST' });
      const d = await r.json();
      setReprocessResult(d?.status === 'ok' ? '✓ Retraité' : '⚠ Erreur');
      if (d?.status === 'ok') onReprocess?.();
    } catch (_) {
      setReprocessResult('⚠ Erreur');
    }
    setReprocessing(false);
    setTimeout(() => setReprocessResult(null), 3000);
  }

  if (loading) {
    return (
      <div style={{ border: '1px solid var(--line)', borderRadius: 6, background: 'var(--paper)', padding: 32, textAlign: 'center', color: 'var(--ink-4)' }}>
        <Dot tone="plasma" pulse/> Chargement…
      </div>
    );
  }
  if (!email) {
    return (
      <div style={{ border: '1px solid var(--line)', borderRadius: 6, background: 'var(--paper)', padding: 32, textAlign: 'center', color: 'var(--rouge)' }}>
        Message introuvable.
      </div>
    );
  }

  const sender = SENDER_META[email.senderType] || SENDER_META.unknown;
  const statusM = STATUS_META[email.status] || STATUS_META.pending;

  return (
    <div style={{ border: '1px solid var(--line)', borderRadius: 6, background: 'var(--paper)', overflow: 'hidden' }}>
      {/* Header */}
      <div style={{ padding: '14px 18px', borderBottom: '1px solid var(--hairline)', background: 'var(--paper-2)', display: 'flex', flexDirection: 'column', gap: 8 }}>
        <div style={{ display: 'flex', alignItems: 'flex-start', gap: 12 }}>
          <div>
            <div style={{ fontSize: 16, fontWeight: 600, color: 'var(--ink)', lineHeight: 1.3 }}>{email.subject || '(sans objet)'}</div>
            <div style={{ fontSize: 12, color: 'var(--ink-3)', marginTop: 4 }}>
              <span style={{ fontWeight: 500 }}>{email.fromName || email.fromAddress}</span>
              {email.fromName && <span style={{ color: 'var(--ink-5)' }}> &lt;{email.fromAddress}&gt;</span>}
            </div>
            <div className="mono" style={{ fontSize: 10, color: 'var(--ink-5)', marginTop: 3 }}>
              {email.receivedAt ? new Date(email.receivedAt).toLocaleString('fr-FR') : '—'}
            </div>
          </div>
          <span style={{ flex: 1 }}/>
          <div style={{ display: 'flex', gap: 6, alignItems: 'center', flexWrap: 'wrap' }}>
            <Badge tone={sender.tone}>{sender.label}</Badge>
            <Badge tone={statusM.tone}>{statusM.label}</Badge>
            {(reprocessResult || actionMsg) && (
              <span style={{ fontSize: 10, color: (reprocessResult || actionMsg).startsWith('✓') ? 'var(--signal-deep)' : 'var(--rouge)', fontFamily: 'var(--font-mono)' }}>{reprocessResult || actionMsg}</span>
            )}
          </div>
        </div>
        {/* Actions email — Règle n°5 CRUD complet sur boîte mail */}
        <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', paddingTop: 6 }}>
          <button onClick={() => doReply(false)} style={btnStyle()}>↩ Répondre</button>
          <button onClick={() => doReply(true)} style={btnStyle()}>↩↩ Répondre à tous</button>
          <button onClick={doForward} style={btnStyle()}>➡ Transférer</button>
          <button onClick={() => doMarkRead(true)} style={btnStyle()}>✓ Marquer lu</button>
          <button onClick={() => doMarkRead(false)} style={btnStyle()}>● Non-lu</button>
          <button onClick={doSpam} style={btnStyle('amber')}>🚫 Spam</button>
          <span style={{ flex: 1 }}/>
          <Btn variant="outline" size="sm" onClick={doReprocess} disabled={reprocessing}>
            {reprocessing ? '…' : '↺ Retraiter'}
          </Btn>
          <button onClick={() => doDelete(false)} disabled={deleting} style={btnStyle('red')}>
            {deleting ? '…' : '🗑 Corbeille'}
          </button>
          <button onClick={() => doDelete(true)} disabled={deleting} style={btnStyle('red-strong')} title="Suppression définitive (DB + IMAP)">
            🗑× Suppr. définitive
          </button>
        </div>

        {/* Links to contact / opp */}
        {(email.contactId || email.odooPartnerId || email.contact) && (
          <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', paddingTop: 4 }}>
            {email.contact && (
              <div style={{ fontSize: 11, padding: '3px 8px', background: 'var(--signal-tint)', color: 'var(--signal-deep)', borderRadius: 4, border: '1px solid var(--signal-soft)', display: 'flex', alignItems: 'center', gap: 5 }}>
                👤 <span style={{ fontWeight: 600 }}>{email.contact.firstName} {email.contact.lastName}</span>
                {email.contact.organizationName && <span style={{ color: 'var(--ink-3)' }}>· {email.contact.organizationName}</span>}
              </div>
            )}
            {email.odooPartnerId && (
              <div style={{ fontSize: 11, padding: '3px 8px', background: 'var(--paper-2)', color: 'var(--ink-3)', borderRadius: 4, border: '1px solid var(--line)' }}>
                🔗 Odoo partner #{email.odooPartnerId}
              </div>
            )}
          </div>
        )}
      </div>

      {/* Body */}
      <div style={{ padding: '18px 20px', maxHeight: 360, overflow: 'auto' }}>
        {email.bodyHtml ? (
          <div
            style={{ fontSize: 13, lineHeight: 1.65, color: 'var(--ink-2)' }}
            dangerouslySetInnerHTML={{ __html: email.bodyHtml }}
          />
        ) : email.bodyText ? (
          <pre style={{ fontFamily: 'inherit', fontSize: 13, lineHeight: 1.65, color: 'var(--ink-2)', whiteSpace: 'pre-wrap', wordBreak: 'break-word', margin: 0 }}>
            {email.bodyText}
          </pre>
        ) : (
          <div style={{ color: 'var(--ink-4)', fontSize: 12, fontStyle: 'italic' }}>Corps du message non disponible.</div>
        )}
      </div>

      {/* Attachments */}
      {email.attachments?.length > 0 && (
        <div style={{ padding: '12px 18px', borderTop: '1px solid var(--hairline)', background: 'var(--paper-2)' }}>
          <div style={{ fontSize: 10, fontWeight: 600, textTransform: 'uppercase', letterSpacing: 0.5, color: 'var(--ink-4)', marginBottom: 8 }}>
            Pièces jointes · {email.attachments.length}
          </div>
          <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
            {email.attachments.map(att => (
              <a
                key={att.id}
                href={API() + `/api/comm/emails/${email.id}/attachments/${att.id}/download`}
                download={att.filename}
                style={{
                  display: 'flex', alignItems: 'center', gap: 6,
                  padding: '5px 10px', fontSize: 11, borderRadius: 4,
                  border: '1px solid var(--line)', background: 'var(--paper)',
                  color: 'var(--ink-2)', textDecoration: 'none',
                }}
              >
                <span style={{ fontSize: 14 }}>
                  {att.contentType?.includes('pdf') ? '📄' :
                   att.contentType?.includes('image') ? '🖼️' :
                   att.contentType?.includes('zip') ? '🗜️' : '📎'}
                </span>
                <span style={{ maxWidth: 160, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{att.filename}</span>
                <span className="mono" style={{ fontSize: 9, color: 'var(--ink-5)' }}>{sizeKb(att.size)}</span>
              </a>
            ))}
          </div>
        </div>
      )}

      {/* Footer actions */}
      <div style={{ padding: '10px 18px', borderTop: '1px solid var(--hairline)', display: 'flex', gap: 8 }}>
        <Btn variant="outline" size="sm" icon={<Icon.plus/>} onClick={() => {
          // Reply: ouvre compose avec to pré-rempli
          if (window.commComposeRef) window.commComposeRef.setReply(email);
        }}>Répondre</Btn>
        <Btn variant="ghost" size="sm">Lier à une opportunité</Btn>
        <Btn variant="ghost" size="sm">Créer contact</Btn>
      </div>
    </div>
  );
}

// ── Compose tab ───────────────────────────────────────────────────────────────

function ComposeTab({ onSent }) {
  const [from, setFrom] = _cpUseS('');
  const [to, setTo] = _cpUseS('');
  const [subject, setSubject] = _cpUseS('');
  const [body, setBody] = _cpUseS('');
  const [sending, setSending] = _cpUseS(false);
  const [result, setResult] = _cpUseS(null);
  const [providers, setProviders] = _cpUseS(null);

  _cpUseE(() => {
    fetch(API() + '/api/comm/email-providers/status', { credentials: 'include' })
      .then(r => r.json()).then(setProviders).catch(() => {});
  }, []);

  // expose to reply button in EmailDetail
  window.commComposeRef = { setReply: (email) => {
    setTo(email.fromAddress || '');
    setSubject('Re: ' + (email.subject || ''));
    setBody(`\n\n---\n> ${(email.bodyText || '').slice(0, 300).replace(/\n/g, '\n> ')}`);
  }};

  async function send() {
    if (!to || !subject || !from) { setResult('⚠ Champs To, De et Objet requis.'); return; }
    setSending(true);
    setResult(null);
    try {
      const r = await fetch(API() + '/api/comm/send', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ from, to, subject, body }),
      });
      if (r.ok) {
        setResult('✓ Message envoyé');
        setTimeout(() => onSent?.(), 1500);
      } else {
        const d = await r.json().catch(() => ({}));
        setResult('⚠ ' + (d.error || d.message || `HTTP ${r.status}`));
      }
    } catch (e) {
      setResult('⚠ Réseau : ' + e.message);
    }
    setSending(false);
  }

  return (
    <div style={{ maxWidth: 680 }}>
      <div style={{ border: '1px solid var(--line)', borderRadius: 6, background: 'var(--paper)', padding: 20, display: 'flex', flexDirection: 'column', gap: 14 }}>
        <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--ink)', marginBottom: 2 }}>Nouveau message</div>

        {/* B5 — Statut providers email (live depuis /api/comm/email-providers/status) */}
        {providers && (
          <div style={{ padding: '10px 14px', background: providers.anyReady ? 'var(--signal-tint)' : 'var(--copper-tint)', border: '1px solid ' + (providers.anyReady ? 'var(--signal-soft)' : 'var(--copper)'), borderRadius: 5, fontSize: 11, color: 'var(--ink-2)' }}>
            {providers.anyReady ? (
              <>✓ <strong>Provider email prêt.</strong> Sélectionnez ci-dessous : {[
                providers.gmail.ready && 'Gmail',
                providers.outlook.ready && 'Outlook',
                providers.smtp.ready && 'SMTP',
              ].filter(Boolean).join(' · ')}</>
            ) : (
              <>⚠ <strong>Aucun provider email configuré.</strong> Définissez les variables .env <code>GOOGLE_*</code> ou <code>MS_*</code> ou <code>SMTP_*</code> et redémarrez. L'email sera loggé en attendant.</>
            )}
          </div>
        )}

        <CField label="De">
          <select value={from} onChange={e => setFrom(e.target.value)} style={{ width: '100%', padding: '7px 10px', fontSize: 12, border: '1px solid var(--line-2)', borderRadius: 5, background: 'var(--paper)', color: 'var(--ink)', fontFamily: 'inherit' }}>
            <option value="">— Choisir un compte —</option>
            <option value="gmail" disabled={providers && !providers.gmail.ready}>Gmail (Google Workspace) {providers && (providers.gmail.ready ? '✓' : '✗')}</option>
            <option value="outlook" disabled={providers && !providers.outlook.ready}>Outlook / M365 {providers && (providers.outlook.ready ? '✓' : '✗')}</option>
            <option value="smtp" disabled={providers && !providers.smtp.ready}>SMTP générique {providers && (providers.smtp.ready ? '✓' : '✗')}</option>
          </select>
        </CField>

        <CField label="À">
          <input type="email" value={to} onChange={e => setTo(e.target.value)} placeholder="destinataire@exemple.fr"
            style={{ width: '100%', padding: '7px 10px', fontSize: 12, border: '1px solid var(--line-2)', borderRadius: 5, background: 'var(--paper)', color: 'var(--ink)', fontFamily: 'inherit' }}/>
        </CField>

        <CField label="Objet">
          <input type="text" value={subject} onChange={e => setSubject(e.target.value)} placeholder="Objet du message"
            style={{ width: '100%', padding: '7px 10px', fontSize: 12, border: '1px solid var(--line-2)', borderRadius: 5, background: 'var(--paper)', color: 'var(--ink)', fontFamily: 'inherit' }}/>
        </CField>

        <CField label="Message">
          <textarea value={body} onChange={e => setBody(e.target.value)} rows={10} placeholder="Rédigez votre message…"
            style={{ width: '100%', padding: '7px 10px', fontSize: 12, border: '1px solid var(--line-2)', borderRadius: 5, background: 'var(--paper)', color: 'var(--ink)', fontFamily: 'inherit', resize: 'vertical' }}/>
        </CField>

        <div style={{ display: 'flex', alignItems: 'center', gap: 10, paddingTop: 4 }}>
          <Btn variant="signal" size="sm" icon={<Icon.arrow/>} onClick={send} disabled={sending}>
            {sending ? 'Envoi…' : '📤 Envoyer'}
          </Btn>
          <Btn variant="ghost" size="sm" onClick={() => { setTo(''); setSubject(''); setBody(''); setFrom(''); setResult(null); }}>Effacer</Btn>
          {result && (
            <span style={{ fontSize: 11, color: result.startsWith('✓') ? 'var(--signal-deep)' : 'var(--rouge)', fontFamily: 'var(--font-mono)' }}>
              {result}
            </span>
          )}
        </div>
      </div>
    </div>
  );
}

function CField({ label, children }) {
  return (
    <div>
      <label style={{ display: 'block', fontSize: 10, fontWeight: 700, color: 'var(--ink-4)', textTransform: 'uppercase', letterSpacing: 0.5, marginBottom: 5 }}>{label}</label>
      {children}
    </div>
  );
}

// ── Config tab ────────────────────────────────────────────────────────────────

function ConfigTab({ refreshKey }) {
  const [status, setStatus] = _cpUseS(null);
  const [slackUrl, setSlackUrl] = _cpUseS('');
  const [slackTest, setSlackTest] = _cpUseS(null);

  _cpUseE(() => {
    fetch(API() + '/api/comm/status').then(r => r.json()).then(d => setStatus(d)).catch(() => {});
  }, [refreshKey]);

  async function testSlack() {
    if (!slackUrl) { setSlackTest('⚠ URL vide'); return; }
    setSlackTest('Test…');
    try {
      const r = await fetch(slackUrl, { method: 'POST', body: JSON.stringify({ text: '✓ Test depuis Audits Énergies SaaS' }) });
      setSlackTest(r.ok ? '✓ OK' : '⚠ Erreur ' + r.status);
    } catch (_) {
      setSlackTest('⚠ Réseau');
    }
    setTimeout(() => setSlackTest(null), 3000);
  }

  const imapOk = status?.imap?.connected;
  const emailTotal = status?.counts?.total || 0;
  const emailPending = status?.counts?.pending || 0;
  const emailErrors = status?.counts?.errors || 0;

  return (
    <div style={{ display: 'grid', gap: 16 }}>
      {/* IMAP status */}
      <div style={{ border: '1px solid var(--line)', borderRadius: 6, background: 'var(--paper)', padding: 16 }}>
        <div style={{ fontSize: 11, fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.5, color: 'var(--ink-3)', marginBottom: 12 }}>Statut IMAP</div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
          <Dot tone={imapOk ? 'signal' : 'rouge'} size={8} pulse={imapOk}/>
          <span style={{ fontSize: 13, color: imapOk ? 'var(--signal-deep)' : 'var(--rouge)' }}>
            {imapOk ? 'Connecté' : 'Non connecté'}
          </span>
          {status?.imap?.message && (
            <span style={{ fontSize: 11, color: 'var(--ink-4)' }}>— {status.imap.message}</span>
          )}
          <span style={{ flex: 1 }}/>
          <span className="mono" style={{ fontSize: 11, color: 'var(--ink-4)' }}>
            {emailTotal} mails · {emailPending} en attente · {emailErrors} erreurs
          </span>
        </div>
      </div>

      {/* OAuth providers */}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 14 }}>
        {/* Gmail */}
        <OAuthCard
          color="#EA4335"
          initial="G"
          title="Gmail"
          sub="Google Workspace / Personnel"
          envVars="GOOGLE_CLIENT_ID + GOOGLE_CLIENT_SECRET"
          connected={false}
          onConnect={() => alert('Redirecting to Google OAuth2 consent screen…\nAjoutez GOOGLE_CLIENT_ID et GOOGLE_CLIENT_SECRET dans .env')}
        />
        {/* Outlook */}
        <OAuthCard
          color="#0078D4"
          initial="O"
          title="Outlook / M365"
          sub="Microsoft Azure AD · Exchange"
          envVars="MS_CLIENT_ID + MS_CLIENT_SECRET"
          connected={false}
          onConnect={() => alert('Redirecting to Microsoft OAuth2 consent screen…\nAjoutez MS_CLIENT_ID et MS_CLIENT_SECRET dans .env')}
        />
        {/* Slack */}
        <div style={{ border: '1px solid var(--line)', borderRadius: 6, background: 'var(--paper)', padding: 16 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 12 }}>
            <div style={{ width: 38, height: 38, borderRadius: 10, background: '#4A154B', display: 'grid', placeItems: 'center', fontSize: 18, fontWeight: 800, color: '#fff', flexShrink: 0 }}>S</div>
            <div>
              <div style={{ fontWeight: 700, fontSize: 13 }}>Slack</div>
              <div style={{ fontSize: 11, color: 'var(--ink-4)' }}>Webhook entrant · Notifications</div>
            </div>
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 7 }}>
            <input value={slackUrl} onChange={e => setSlackUrl(e.target.value)} type="url" placeholder="https://hooks.slack.com/services/…"
              style={{ padding: '7px 10px', border: '1px solid var(--line-2)', borderRadius: 5, background: 'var(--paper-2)', color: 'var(--ink)', fontSize: 11, fontFamily: 'inherit', width: '100%', boxSizing: 'border-box' }}/>
            <div style={{ display: 'flex', gap: 6 }}>
              <Btn variant="signal" size="sm" onClick={testSlack}>Tester</Btn>
              <Btn variant="ghost" size="sm">Enregistrer</Btn>
              {slackTest && <span style={{ fontSize: 10, color: slackTest.startsWith('✓') ? 'var(--signal-deep)' : 'var(--rouge)', fontFamily: 'var(--font-mono)' }}>{slackTest}</span>}
            </div>
          </div>
          <div style={{ fontSize: 10, color: 'var(--ink-5)', marginTop: 8 }}>Créez un webhook dans Slack App Directory</div>
        </div>
      </div>

      {/* Instructions */}
      <div style={{ padding: '14px 16px', background: 'var(--paper-2)', border: '1px solid var(--line)', borderRadius: 6, fontSize: 12, color: 'var(--ink-3)', lineHeight: 1.7 }}>
        <div style={{ fontWeight: 700, color: 'var(--ink)', marginBottom: 6 }}>ℹ️ Configuration OAuth2</div>
        <div><strong>Gmail :</strong> créez un projet Google Cloud Console, activez l'API Gmail, ajoutez <code style={{ background: 'var(--paper)', padding: '1px 4px', borderRadius: 2, fontSize: 11 }}>GOOGLE_CLIENT_ID</code> et <code style={{ background: 'var(--paper)', padding: '1px 4px', borderRadius: 2, fontSize: 11 }}>GOOGLE_CLIENT_SECRET</code> dans <code style={{ background: 'var(--paper)', padding: '1px 4px', borderRadius: 2, fontSize: 11 }}>.env</code>.</div>
        <div><strong>Outlook :</strong> créez une app dans Azure Portal, ajoutez <code style={{ background: 'var(--paper)', padding: '1px 4px', borderRadius: 2, fontSize: 11 }}>MS_CLIENT_ID</code> et <code style={{ background: 'var(--paper)', padding: '1px 4px', borderRadius: 2, fontSize: 11 }}>MS_CLIENT_SECRET</code> dans <code style={{ background: 'var(--paper)', padding: '1px 4px', borderRadius: 2, fontSize: 11 }}>.env</code>.</div>
        <div><strong>Slack :</strong> créez un Incoming Webhook dans votre Slack App Directory et collez l'URL ci-dessus.</div>
      </div>
    </div>
  );
}

function OAuthCard({ color, initial, title, sub, envVars, connected, onConnect }) {
  return (
    <div style={{ border: '1px solid var(--line)', borderRadius: 6, background: 'var(--paper)', padding: 16 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 12 }}>
        <div style={{ width: 38, height: 38, borderRadius: 10, background: color, display: 'grid', placeItems: 'center', fontSize: 18, fontWeight: 800, color: '#fff', flexShrink: 0 }}>{initial}</div>
        <div>
          <div style={{ fontWeight: 700, fontSize: 13 }}>{title}</div>
          <div style={{ fontSize: 11, color: 'var(--ink-4)' }}>{sub}</div>
        </div>
      </div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 12 }}>
        <Dot tone={connected ? 'signal' : 'rouge'} size={6}/>
        <span style={{ fontSize: 11, color: connected ? 'var(--signal-deep)' : 'var(--rouge)' }}>
          {connected ? 'Connecté' : 'Non configuré'}
        </span>
      </div>
      <Btn variant="signal" size="sm" style={{ width: '100%', justifyContent: 'center' }} onClick={onConnect}>
        🔑 Connecter via OAuth2
      </Btn>
      <div style={{ fontSize: 10, color: 'var(--ink-5)', marginTop: 8 }}>
        Requiert {envVars} dans .env
      </div>
    </div>
  );
}

// ─── FoldersSidebar — Liste des dossiers IMAP avec compteurs unread ──────
function FoldersSidebar({ onSelect, activeFolder = 'INBOX' }) {
  const [folders, setFolders] = _cpUseS([]);
  const [unread, setUnread] = _cpUseS({});
  const [loading, setLoading] = _cpUseS(true);
  const active = activeFolder;

  _cpUseE(() => {
    let cancelled = false;
    setLoading(true);
    Promise.all([
      fetch(API() + '/api/comm/folders').then(r => r.json()).catch(() => null),
      fetch(API() + '/api/comm/folders/unread').then(r => r.json()).catch(() => null),
    ]).then(([f, u]) => {
      if (cancelled) return;
      setFolders(f?.folders || []);
      setUnread(u?.counts || {});
    }).finally(() => { if (!cancelled) setLoading(false); });
    return () => { cancelled = true; };
  }, []);

  // Mappe specialUse → icône + label FR
  const ICON = {
    '\\Inbox':   { icon: '📥', label: 'Boîte de réception' },
    '\\Sent':    { icon: '📤', label: 'Envoyés' },
    '\\Drafts':  { icon: '📝', label: 'Brouillons' },
    '\\Junk':    { icon: '🚫', label: 'Spam' },
    '\\Trash':   { icon: '🗑', label: 'Corbeille' },
  };
  const fallback = (path) => ({ icon: '📁', label: path.replace(/^INBOX[./]/, '') });

  const handleClick = (path) => {
    onSelect?.(path);
  };

  return (
    <div style={{ border: '1px solid var(--line)', borderRadius: 6, background: 'var(--paper)', overflow: 'hidden', display: 'flex', flexDirection: 'column' }}>
      <div style={{ padding: '8px 12px', borderBottom: '1px solid var(--hairline)', background: 'var(--paper-2)', fontSize: 10, fontWeight: 600, color: 'var(--ink-4)', textTransform: 'uppercase', letterSpacing: 0.5 }}>
        Dossiers IMAP
      </div>
      <div style={{ flex: 1, overflowY: 'auto', padding: 6 }}>
        {loading ? (
          <div style={{ padding: 16, textAlign: 'center', color: 'var(--ink-4)', fontSize: 11 }}>⏳ Chargement…</div>
        ) : folders.length === 0 ? (
          <div style={{ padding: 16, textAlign: 'center', color: 'var(--ink-4)', fontSize: 11 }}>Aucun dossier (IMAP non configuré)</div>
        ) : (
          folders.map(f => {
            const meta = ICON[f.specialUse] || fallback(f.path);
            const u = unread[f.path]?.unseen || 0;
            const total = unread[f.path]?.total || 0;
            const isActive = active === f.path;
            return (
              <button
                key={f.path}
                onClick={() => handleClick(f.path)}
                style={{
                  display: 'flex', alignItems: 'center', gap: 8,
                  width: '100%', padding: '7px 10px', fontSize: 12,
                  background: isActive ? 'var(--paper-2)' : 'transparent',
                  border: '1px solid ' + (isActive ? 'var(--line-2)' : 'transparent'),
                  borderRadius: 4, cursor: 'pointer', textAlign: 'left',
                  color: isActive ? 'var(--ink)' : 'var(--ink-2)',
                  fontWeight: isActive ? 600 : 400,
                }}
                title={`${total} messages, ${u} non-lus`}
              >
                <span>{meta.icon}</span>
                <span style={{ flex: 1, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{meta.label}</span>
                {u > 0 && (
                  <span className="mono" style={{
                    fontSize: 10, padding: '1px 6px',
                    background: 'var(--signal)', color: 'var(--ink)',
                    borderRadius: 10, fontWeight: 700,
                  }}>{u}</span>
                )}
              </button>
            );
          })
        )}
      </div>
      <div style={{ padding: '6px 10px', borderTop: '1px solid var(--hairline)', fontSize: 9, color: 'var(--ink-5)', textAlign: 'center', background: 'var(--paper-2)' }}>
        IMAP {(window.AE_API && window.AE_API.BASE) || 'localhost'}
      </div>
    </div>
  );
}

Object.assign(window, { CommunicationPage });
