// Vocal WhatsApp - Inbox PWA
const { useState, useEffect, useCallback, useRef, useMemo, createContext, useContext } = React;

const CONFIG = {
  API_URL: (() => {
    const p = new URLSearchParams(location.search);
    if (p.get('api')) return p.get('api').replace(/\/$/, '');
    if (location.hostname === 'localhost' || location.hostname === '127.0.0.1') {
      return 'http://127.0.0.1:8787';
    }
    return 'https://api.vocal.ch';
  })(),
};

const STORAGE_TOKEN = 'vocal_wa_token';
const STORAGE_USER  = 'vocal_wa_user';

// ==================== API ====================
const api = {
  token: null,
  async req(path, opts = {}) {
    const headers = { 'Content-Type': 'application/json', ...opts.headers };
    if (this.token) headers.Authorization = `Bearer ${this.token}`;
    const resp = await fetch(`${CONFIG.API_URL}${path}`, { ...opts, headers });
    if (resp.status === 401 && this.token) {
      // Session perdue — purge.
      localStorage.removeItem(STORAGE_TOKEN);
      localStorage.removeItem(STORAGE_USER);
      api.token = null;
      window.dispatchEvent(new CustomEvent('vocal-wa-logout'));
    }
    try { return await resp.json(); } catch { return { error: 'invalid response' }; }
  },
  get(p)         { return api.req(p); },
  post(p, b)     { return api.req(p, { method: 'POST', body: JSON.stringify(b || {}) }); },
};

// ==================== ICONS ====================
const Icons = {
  WA: ({ size = 22 }) => (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="currentColor">
      <path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.435 9.884-9.884 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z" />
    </svg>
  ),
  Back: () => <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M19 12H5M12 19l-7-7 7-7" /></svg>,
  Search: () => <svg width="20" height="20" 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>,
  More: () => <svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="5" r="2" /><circle cx="12" cy="12" r="2" /><circle cx="12" cy="19" r="2" /></svg>,
  Refresh: () => <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="23 4 23 10 17 10" /><polyline points="1 20 1 14 7 14" /><path d="M3.51 9a9 9 0 0114.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0020.49 15" /></svg>,
  Send: () => <svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>,
  Check: () => <svg width="14" height="14" viewBox="0 0 18 18" fill="currentColor"><path d="M14.27 4.31L7.49 11.09 5.62 9.23l-.71.71 2.58 2.58 7.49-7.49z"/></svg>,
  DoubleCheck: () => <svg width="16" height="16" viewBox="0 0 18 18" fill="currentColor"><path d="M11.27 4.31L4.49 11.09 2.62 9.23l-.71.71 2.58 2.58 7.49-7.49z"/><path d="M16.27 4.31L9.49 11.09l-1-1-.71.71 1.71 1.71 7.49-7.49z"/></svg>,
  Logout: () => <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M9 21H5a2 2 0 01-2-2V5a2 2 0 012-2h4M16 17l5-5-5-5M21 12H9" /></svg>,
  Phone: () => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M22 16.92v3a2 2 0 01-2.18 2A19.79 19.79 0 013 4.18 2 2 0 015 2h3a2 2 0 012 1.72c.127.96.361 1.903.7 2.81a2 2 0 01-.45 2.11L8.91 9.91a16 16 0 006 6l1.27-1.27a2 2 0 012.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0122 16.92z" /></svg>,
  Bot: () => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="6" width="18" height="14" rx="3" /><circle cx="9" cy="13" r="1.2" /><circle cx="15" cy="13" r="1.2" /><path d="M12 2v4M8 18h8" /></svg>,
  Chat: () => <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M21 11.5a8.38 8.38 0 01-.9 3.8 8.5 8.5 0 01-7.6 4.7 8.38 8.38 0 01-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 01-.9-3.8 8.5 8.5 0 014.7-7.6 8.38 8.38 0 013.8-.9h.5a8.48 8.48 0 018 8v.5z" /></svg>,
  Archive: () => <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><polyline points="21 8 21 21 3 21 3 8" /><rect x="1" y="3" width="22" height="5" rx="1" /><line x1="10" y1="12" x2="14" y2="12" /></svg>,
  Pencil: () => <svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 20h9" /><path d="M16.5 3.5a2.121 2.121 0 013 3L7 19l-4 1 1-4L16.5 3.5z" /></svg>,
  Status: () => <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="9" strokeDasharray="2 3" /><circle cx="12" cy="12" r="3.5" /></svg>,
  Settings: () => <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="3" /><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 11-2.83 2.83l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09a1.65 1.65 0 00-1-1.51 1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 11-2.83-2.83l.06-.06a1.65 1.65 0 00.33-1.82 1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09a1.65 1.65 0 001.51-1 1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 112.83-2.83l.06.06a1.65 1.65 0 001.82.33h0a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09a1.65 1.65 0 001 1.51h0a1.65 1.65 0 001.82-.33l.06-.06a2 2 0 112.83 2.83l-.06.06a1.65 1.65 0 00-.33 1.82v0a1.65 1.65 0 001.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z" /></svg>,
  ArchiveBox: ({ size = 36 }) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"><polyline points="21 8 21 21 3 21 3 8" /><rect x="1" y="3" width="22" height="5" rx="1" /><line x1="10" y1="12" x2="14" y2="12" /></svg>,
};

// ==================== ARCHIVES (local) ====================
const STORAGE_ARCHIVED = 'vocal_wa_archived';
const loadArchived = () => {
  try { return new Set(JSON.parse(localStorage.getItem(STORAGE_ARCHIVED) || '[]')); }
  catch { return new Set(); }
};
const saveArchived = (set) => {
  localStorage.setItem(STORAGE_ARCHIVED, JSON.stringify([...set]));
};

// ==================== UTILS ====================
function fmtTime(d) {
  if (!d) return '';
  const dt = new Date(d.includes('T') ? d : d.replace(' ', 'T') + 'Z');
  const now = new Date();
  const sameDay = dt.toDateString() === now.toDateString();
  if (sameDay) return dt.toLocaleTimeString('fr-CH', { hour: '2-digit', minute: '2-digit' });
  const diffDays = Math.floor((now - dt) / (24 * 3600 * 1000));
  if (diffDays < 7) return dt.toLocaleDateString('fr-CH', { weekday: 'short' });
  return dt.toLocaleDateString('fr-CH', { day: '2-digit', month: '2-digit' });
}
function fmtDay(d) {
  if (!d) return '';
  const dt = new Date(d.includes('T') ? d : d.replace(' ', 'T') + 'Z');
  const today = new Date();
  const y = new Date(); y.setDate(y.getDate() - 1);
  if (dt.toDateString() === today.toDateString()) return "Aujourd'hui";
  if (dt.toDateString() === y.toDateString()) return 'Hier';
  return dt.toLocaleDateString('fr-CH', { weekday: 'long', day: 'numeric', month: 'long' });
}
function initials(name) {
  if (!name) return '?';
  const parts = name.trim().split(/\s+/);
  if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
  return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
}
function avatarVariant(s) {
  if (!s) return '';
  let h = 0;
  for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) | 0;
  return ['', 'v1', 'v2', 'v3', 'v4'][Math.abs(h) % 5];
}

// ==================== TOAST ====================
const AppCtx = createContext(null);
const useApp = () => useContext(AppCtx);

function Toast({ msg, type, onDone }) {
  useEffect(() => { const t = setTimeout(onDone, 3500); return () => clearTimeout(t); }, [onDone]);
  return <div className={`toast ${type || ''}`}>{msg}</div>;
}

// ==================== TOP BAR ====================
function TopBarWithLineSelector({ subtitle, right }) {
  const { lines, lineId, setLineId } = useApp();
  const [open, setOpen] = useState(false);
  const ref = useRef(null);

  useEffect(() => {
    if (!open) return;
    const h = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', h);
    document.addEventListener('touchstart', h);
    return () => {
      document.removeEventListener('mousedown', h);
      document.removeEventListener('touchstart', h);
    };
  }, [open]);

  const current = lines.find(l => l.id === lineId) || lines[0] || null;

  // Auto-sélection : si pas de ligne choisie mais des lignes existent, prend la 1ère.
  useEffect(() => {
    if (!lineId && lines.length > 0) setLineId(lines[0].id);
  }, [lines, lineId]);

  const fmtLineSub = (l) => {
    if (!l) return '—';
    return l.label || l.description || l.line_label || 'Ligne WhatsApp';
  };

  return (
    <div className="topbar safe-top" ref={ref}>
      <div className="safe-extend" />
      <button className="topbar-line-btn" onClick={() => setOpen(o => !o)} disabled={lines.length <= 1}>
        <div className="topbar-line-icon"><Icons.WA size={18} /></div>
        <div className="topbar-line-text">
          <div className="topbar-line-num">{current ? (current.phone_number || '—') : 'Aucune ligne'}</div>
          <div className="topbar-line-sub">
            {current ? fmtLineSub(current) : (subtitle || '')}
            {subtitle && current ? ` · ${subtitle}` : ''}
          </div>
        </div>
        {lines.length > 1 && (
          <svg className={`topbar-line-chevron ${open ? 'open' : ''}`} width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
            <polyline points="6 9 12 15 18 9" />
          </svg>
        )}
      </button>
      {right && <div className="topbar-actions">{right}</div>}

      {open && lines.length > 1 && (
        <div className="line-dropdown">
          {lines.map(l => (
            <button key={l.id}
              className={`line-dropdown-item ${l.id === lineId ? 'active' : ''}`}
              onClick={() => { setLineId(l.id); setOpen(false); }}>
              <div className="line-dropdown-icon"><Icons.WA size={16} /></div>
              <div className="line-dropdown-text">
                <div className="line-dropdown-num">{l.phone_number || '—'}</div>
                <div className="line-dropdown-sub">{fmtLineSub(l)}</div>
              </div>
              {l.id === lineId && <div className="line-dropdown-check"><Icons.Check /></div>}
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

// ==================== AUTH ====================
function AuthScreen({ onLogin }) {
  const [step, setStep]     = useState('email');
  const [email, setEmail]   = useState('');
  const [code, setCode]     = useState('');
  const [loading, setLoad]  = useState(false);
  const [error, setError]   = useState('');

  // Magic link ?email=&code=
  useEffect(() => {
    const p = new URLSearchParams(location.search);
    const e = p.get('email'); const c = p.get('code');
    if (e && c) {
      setEmail(e); setLoad(true);
      fetch(`${CONFIG.API_URL}/auth/verify-code`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email: e.toLowerCase(), code: c }),
      }).then(r => r.json()).then(data => {
        if (data.success && data.token) {
          history.replaceState({}, '', location.pathname);
          onLogin(data.token, data.user || { email: e });
        } else { setStep('code'); setError(data.error || 'Lien expiré'); }
      }).catch(() => setError('Erreur réseau')).finally(() => setLoad(false));
    } else if (e) { setEmail(e); setStep('code'); }
  }, []);

  const requestCode = async (e) => {
    e?.preventDefault();
    if (!email.includes('@')) { setError('Email invalide'); return; }
    setLoad(true); setError('');
    try {
      const r = await fetch(`${CONFIG.API_URL}/auth/request-code`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email: email.toLowerCase().trim() }),
      }).then(x => x.json());
      if (r.success) setStep('code');
      else setError(r.error || 'Erreur envoi');
    } catch { setError('Erreur réseau'); }
    finally { setLoad(false); }
  };

  const verify = async (e) => {
    e?.preventDefault();
    if (code.length < 4) return;
    setLoad(true); setError('');
    try {
      const r = await fetch(`${CONFIG.API_URL}/auth/verify-code`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email: email.toLowerCase(), code }),
      }).then(x => x.json());
      if (r.success && r.token) onLogin(r.token, r.user || { email });
      else setError(r.error || 'Code invalide');
    } catch { setError('Erreur réseau'); }
    finally { setLoad(false); }
  };

  return (
    <div className="auth-screen">
      <div className="auth-card">
        <div className="auth-brand">
          <div className="auth-brand-icon"><Icons.WA size={24} /></div>
          <div>
            <div className="auth-brand-title">Vocal WhatsApp</div>
            <div className="auth-brand-sub">Inbox Business</div>
          </div>
        </div>
        {step === 'email' ? (
          <form onSubmit={requestCode}>
            <h1 className="auth-title">Connexion</h1>
            <p className="auth-desc">Entrez votre email Vocal pour recevoir un code.</p>
            <input type="email" value={email} onChange={e => setEmail(e.target.value)}
              placeholder="vous@exemple.com" className="auth-input" autoFocus autoComplete="email" inputMode="email" />
            {error && <div className="auth-error">{error}</div>}
            <button type="submit" disabled={loading || !email.includes('@')} className="btn btn-wa">
              {loading ? 'Envoi…' : 'Recevoir le code'}
            </button>
          </form>
        ) : (
          <form onSubmit={verify}>
            <h1 className="auth-title">Code reçu ?</h1>
            <p className="auth-desc">Code envoyé à <strong>{email}</strong></p>
            <input type="text" value={code} onChange={e => setCode(e.target.value.replace(/\D/g, '').slice(0, 6))}
              placeholder="123456" className="auth-input auth-code" maxLength="6" autoFocus inputMode="numeric" />
            {error && <div className="auth-error">{error}</div>}
            <button type="submit" disabled={loading || code.length < 4} className="btn btn-wa">
              {loading ? 'Vérification…' : 'Se connecter'}
            </button>
            <button type="button" onClick={() => { setStep('email'); setCode(''); setError(''); }} className="btn btn-ghost" style={{ marginTop: '0.5rem' }}>
              Changer d'email
            </button>
          </form>
        )}
        <div className="auth-foot">Compte Vocal partagé avec my.vocal.ch</div>
      </div>
    </div>
  );
}

// ==================== CONV LIST ITEM ====================
function ConvList({ items, onOpen, onArchive, archiveLabel }) {
  const [menuFor, setMenuFor] = useState(null);
  const longPressTimer = useRef(null);

  const startPress = (id) => {
    longPressTimer.current = setTimeout(() => setMenuFor(id), 500);
  };
  const cancelPress = () => {
    if (longPressTimer.current) { clearTimeout(longPressTimer.current); longPressTimer.current = null; }
  };

  return (
    <div className="inbox-list">
      {items.map(c => {
        const name = c.contact_name || c.contact_number;
        const variant = avatarVariant(c.contact_number || c.contact_name);
        const unread = c.unread_count > 0;
        return (
          <div key={c.id} className="conv-row-wrap">
            <button className="conv-row"
              onClick={() => onOpen(c)}
              onContextMenu={(e) => { e.preventDefault(); setMenuFor(c.id); }}
              onTouchStart={() => startPress(c.id)}
              onTouchEnd={cancelPress}
              onTouchMove={cancelPress}
              onMouseDown={() => startPress(c.id)}
              onMouseUp={cancelPress}
              onMouseLeave={cancelPress}>
              <div className={`conv-avatar ${variant}`}>{initials(name)}</div>
              <div className="conv-main">
                <div className="conv-top">
                  <div className="conv-name">{name}</div>
                  <div className={`conv-time ${unread ? 'unread' : ''}`}>{fmtTime(c.last_message_at)}</div>
                </div>
                <div className="conv-bottom">
                  <div className="conv-preview">
                    {c.bot_enabled === 1 && <span className="conv-bot">BOT</span>}
                    {c.last_message_body || (c.contact_number || '—')}
                  </div>
                  {unread > 0 && <span className="unread-badge">{c.unread_count}</span>}
                </div>
              </div>
            </button>
          </div>
        );
      })}
      {menuFor && (
        <>
          <div className="drawer-overlay" onClick={() => setMenuFor(null)} />
          <div className="drawer safe-bottom">
            <div className="drawer-handle" />
            <button className="drawer-row" onClick={() => { onArchive(menuFor); setMenuFor(null); }}>
              <div className="drawer-row-icon"><Icons.Archive /></div>
              <div className="drawer-row-text">
                <div className="drawer-row-label">{archiveLabel}</div>
              </div>
            </button>
            <button className="drawer-row" onClick={() => setMenuFor(null)}>
              <div className="drawer-row-icon"><Icons.More /></div>
              <div className="drawer-row-text">
                <div className="drawer-row-label">Annuler</div>
              </div>
            </button>
          </div>
        </>
      )}
    </div>
  );
}

// ==================== INBOX LIST ====================
function InboxView({ onOpen, mode = 'discussions' }) {
  const { notify, lineId } = useApp();
  const [convs, setConvs]       = useState([]);
  const [loading, setLoading]   = useState(true);
  const [search, setSearch]     = useState('');
  const [refreshing, setRefresh]= useState(false);
  const [archived, setArchivedSet] = useState(() => loadArchived());

  const toggleArchive = (id) => {
    const next = new Set(archived);
    if (next.has(id)) next.delete(id); else next.add(id);
    setArchivedSet(next);
    saveArchived(next);
    notify(next.has(id) ? 'Conversation archivée' : 'Conversation désarchivée', 'success');
  };

  const load = useCallback(async (showSpin = true) => {
    if (showSpin) setLoading(true); else setRefresh(true);
    try {
      const q = lineId ? `?line_id=${lineId}` : '';
      const r = await api.get(`/my/whatsapp/conversations${q}`);
      if (r?.error) notify(r.error, 'error');
      else setConvs(r.conversations || []);
    } catch { notify('Erreur réseau', 'error'); }
    finally { setLoading(false); setRefresh(false); }
  }, [lineId, notify]);

  useEffect(() => { load(); }, [load]);

  useEffect(() => {
    const t = setInterval(() => load(false), 20000);
    return () => clearInterval(t);
  }, [load]);

  const filtered = useMemo(() => {
    const isArchiveTab = mode === 'archives';
    let list = convs.filter(c => isArchiveTab ? archived.has(c.id) : !archived.has(c.id));
    if (search.trim()) {
      const s = search.toLowerCase();
      list = list.filter(c =>
        (c.contact_name || '').toLowerCase().includes(s) ||
        (c.contact_number || '').includes(s) ||
        (c.last_message_body || '').toLowerCase().includes(s)
      );
    }
    return list;
  }, [convs, search, archived, mode]);

  const archivedCount = useMemo(() => convs.filter(c => archived.has(c.id)).length, [convs, archived]);
  const isArchiveTab = mode === 'archives';

  return (
    <div className="shell">
      <TopBarWithLineSelector
        subtitle={isArchiveTab ? `${archivedCount} archivée${archivedCount > 1 ? 's' : ''}` : 'Discussions'}
        right={(
          <button className="topbar-icon-btn" onClick={() => load(false)} disabled={refreshing}>
            <span className={refreshing ? 'spin' : ''} style={{ display: 'inline-flex' }}><Icons.Refresh /></span>
          </button>
        )} />

      <div className="shell-main">
        <div className="search-bar">
          <input className="search-input" type="search" placeholder="Rechercher…"
            value={search} onChange={e => setSearch(e.target.value)} />
        </div>

        {loading ? (
          <div className="empty-state">
            <div className="loading-spinner" style={{ width: 32, height: 32 }} />
          </div>
        ) : filtered.length === 0 ? (
          <div className="empty-state">
            <div className="empty-icon"><Icons.WA size={36} /></div>
            <div className="empty-title">
              {search ? 'Aucun résultat' : convs.length === 0 ? 'Aucune conversation' : 'Aucune conversation filtrée'}
            </div>
            <div className="empty-desc">
              {convs.length === 0
                ? 'Les conversations WhatsApp Business apparaîtront ici dès qu’un client vous écrit.'
                : 'Essayez un autre filtre ou une autre ligne.'}
            </div>
          </div>
        ) : (
          <ConvList items={filtered} onOpen={onOpen}
            onArchive={toggleArchive} archiveLabel={isArchiveTab ? 'Désarchiver' : 'Archiver'} />
        )}
        {!isArchiveTab && archivedCount > 0 && filtered.length > 0 && (
          <div className="inbox-foot">
            <Icons.ArchiveBox size={16} /> {archivedCount} conversation{archivedCount > 1 ? 's' : ''} archivée{archivedCount > 1 ? 's' : ''}
          </div>
        )}
      </div>

    </div>
  );
}

// ==================== STATUT (activité) ====================
function StatusView() {
  const { lineId, lines } = useApp();
  const [convs, setConvs] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    setLoading(true);
    const q = lineId ? `?line_id=${lineId}` : '';
    api.get(`/my/whatsapp/conversations${q}`)
      .then(r => setConvs(r?.conversations || []))
      .finally(() => setLoading(false));
  }, [lineId]);

  const todayStr = new Date().toISOString().slice(0, 10);
  const todayConvs = convs.filter(c => (c.last_message_at || '').startsWith(todayStr));
  const totalUnread = convs.reduce((s, c) => s + (c.unread_count || 0), 0);
  const botActive = convs.filter(c => c.bot_enabled === 1).length;

  return (
    <div className="shell">
      <TopBarWithLineSelector subtitle="Statut" />
      <div className="shell-main scroll-y">
        {loading ? (
          <div className="empty-state"><div className="loading-spinner" style={{ width: 28, height: 28 }} /></div>
        ) : (
          <div className="status-wrap">
            <div className="status-grid">
              <div className="status-card">
                <div className="status-card-val">{todayConvs.length}</div>
                <div className="status-card-lbl">Conversations actives aujourd'hui</div>
              </div>
              <div className="status-card">
                <div className="status-card-val">{totalUnread}</div>
                <div className="status-card-lbl">Messages non lus</div>
              </div>
              <div className="status-card">
                <div className="status-card-val">{botActive}</div>
                <div className="status-card-lbl">Bots actifs</div>
              </div>
              <div className="status-card">
                <div className="status-card-val">{convs.length}</div>
                <div className="status-card-lbl">Conversations totales</div>
              </div>
            </div>

            <div className="status-section-title">Activité récente</div>
            {todayConvs.length === 0 && (
              <div className="status-empty">Aucune conversation aujourd'hui.</div>
            )}
            {todayConvs.slice(0, 8).map(c => {
              const name = c.contact_name || c.contact_number;
              return (
                <div key={c.id} className="status-row">
                  <div className={`conv-avatar ${avatarVariant(c.contact_number || name)}`} style={{ width: 40, height: 40, fontSize: '0.85rem' }}>
                    {initials(name)}
                  </div>
                  <div className="status-row-main">
                    <div className="status-row-name">{name}</div>
                    <div className="status-row-prev">{c.last_message_body || '—'}</div>
                  </div>
                  <div className="status-row-time">{fmtTime(c.last_message_at)}</div>
                </div>
              );
            })}

          </div>
        )}
      </div>
    </div>
  );
}

// ==================== RÉGLAGES ====================
function SettingsView() {
  const { user, logout } = useApp();
  return (
    <div className="shell">
      <TopBarWithLineSelector subtitle="Réglages" />
      <div className="shell-main scroll-y">
        <div className="settings-profile">
          <div className="settings-avatar">{initials(user?.name || user?.email)}</div>
          <div className="settings-profile-info">
            <div className="settings-name">{user?.name || user?.email}</div>
            <div className="settings-mail">{user?.email}</div>
          </div>
        </div>

        <div className="settings-group">
          <button className="drawer-row" onClick={() => window.open('https://my.vocal.ch', '_blank')}>
            <div className="drawer-row-icon"><Icons.Phone /></div>
            <div className="drawer-row-text">
              <div className="drawer-row-label">Mon espace Vocal</div>
              <div className="drawer-row-desc">my.vocal.ch — lignes, factures, templates</div>
            </div>
          </button>
          <button className="drawer-row" onClick={() => {
            localStorage.removeItem(STORAGE_ARCHIVED);
            location.reload();
          }}>
            <div className="drawer-row-icon"><Icons.Archive /></div>
            <div className="drawer-row-text">
              <div className="drawer-row-label">Vider les archives</div>
              <div className="drawer-row-desc">Désarchiver toutes les conversations</div>
            </div>
          </button>
        </div>

        <div className="settings-group">
          <button className="drawer-row danger" onClick={logout}>
            <div className="drawer-row-icon"><Icons.Logout /></div>
            <div className="drawer-row-text">
              <div className="drawer-row-label">Déconnexion</div>
              <div className="drawer-row-desc">{user?.email}</div>
            </div>
          </button>
        </div>

        <div className="settings-foot">Vocal WhatsApp · v1.0</div>
      </div>
    </div>
  );
}

// ==================== NEW MESSAGE (selector + dialer) ====================
function NewMessageView({ onClose, onOpenConv }) {
  const { notify, lineId } = useApp();
  const [convs, setConvs] = useState([]);
  const [loading, setLoading] = useState(true);
  const [search, setSearch] = useState('');

  useEffect(() => {
    const q = lineId ? `?line_id=${lineId}` : '';
    api.get(`/my/whatsapp/conversations${q}`).then(r => {
      setConvs(r?.conversations || []);
    }).finally(() => setLoading(false));
  }, [lineId]);

  const filtered = useMemo(() => {
    if (!search.trim()) return convs;
    const s = search.toLowerCase();
    return convs.filter(c =>
      (c.contact_name || '').toLowerCase().includes(s) ||
      (c.contact_number || '').includes(s)
    );
  }, [convs, search]);

  return (
    <div className="shell">
      <div className="topbar safe-top">
        <div className="safe-extend" />
        <button className="topbar-back" onClick={onClose}><Icons.Back /></button>
        <div className="topbar-title">
          <div className="topbar-name">Nouveau message</div>
          <div className="topbar-sub">Choisir un contact</div>
        </div>
      </div>
      <div className="shell-main">
        <div className="search-bar">
          <input className="search-input" type="search" placeholder="Rechercher un contact…"
            value={search} onChange={e => setSearch(e.target.value)} autoFocus />
        </div>
        {loading ? (
          <div className="empty-state"><div className="loading-spinner" style={{ width: 28, height: 28 }} /></div>
        ) : filtered.length === 0 ? (
          <div className="empty-state">
            <div className="empty-icon"><Icons.WA size={36} /></div>
            <div className="empty-title">Aucun contact</div>
            <div className="empty-desc">L'envoi vers un nouveau numéro nécessite un template WhatsApp approuvé. Utilisez my.vocal.ch pour gérer vos templates.</div>
          </div>
        ) : (
          <ConvList items={filtered} onOpen={(c) => { onClose(); onOpenConv(c); }} onArchive={() => {}} archiveLabel="" />
        )}
      </div>
    </div>
  );
}

// ==================== BOTTOM NAV ====================
function BottomNav({ tab, onTab, onCompose }) {
  return (
    <div className="bottom-nav safe-bottom">
      <button className={`nav-tab ${tab === 'discussions' ? 'active' : ''}`} onClick={() => onTab('discussions')}>
        <Icons.Chat />
        <span>Discussions</span>
      </button>
      <button className={`nav-tab ${tab === 'archives' ? 'active' : ''}`} onClick={() => onTab('archives')}>
        <Icons.Archive />
        <span>Archives</span>
      </button>
      <button className="nav-fab" onClick={onCompose} aria-label="Nouveau message">
        <Icons.Pencil />
      </button>
      <button className={`nav-tab ${tab === 'status' ? 'active' : ''}`} onClick={() => onTab('status')}>
        <Icons.Status />
        <span>Statut</span>
      </button>
      <button className={`nav-tab ${tab === 'settings' ? 'active' : ''}`} onClick={() => onTab('settings')}>
        <Icons.Settings />
        <span>Réglages</span>
      </button>
    </div>
  );
}

// ==================== THREAD ====================
function ThreadView({ conv, onBack }) {
  const { notify } = useApp();
  const [msgs, setMsgs]       = useState([]);
  const [loading, setLoading] = useState(true);
  const [draft, setDraft]     = useState('');
  const [sending, setSending] = useState(false);
  const [botEnabled, setBot]  = useState(conv.bot_enabled === 1);
  const [data, setData]       = useState(conv);
  const [windowOpen, setWindow] = useState(true);
  const scrollRef = useRef(null);

  const load = useCallback(async () => {
    setLoading(true);
    try {
      const r = await api.get(`/my/whatsapp/conversations/${conv.id}/messages`);
      if (r?.error) notify(r.error, 'error');
      else {
        setMsgs(r.messages || []);
        if (r.conversation) {
          setData(r.conversation);
          setBot(r.conversation.bot_enabled === 1);
          // Fenêtre 24h : si dernier message inbound > 24h on le notifie.
          if (r.conversation.last_inbound_at) {
            const last = new Date(r.conversation.last_inbound_at.replace(' ', 'T') + 'Z');
            setWindow((Date.now() - last.getTime()) < 24 * 3600 * 1000);
          }
        }
      }
    } catch { notify('Erreur réseau', 'error'); }
    finally { setLoading(false); }
  }, [conv.id, notify]);

  useEffect(() => { load(); }, [load]);

  useEffect(() => {
    const t = setInterval(load, 15000);
    return () => clearInterval(t);
  }, [load]);

  useEffect(() => {
    requestAnimationFrame(() => {
      if (scrollRef.current) scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
    });
  }, [msgs, loading]);

  const send = async () => {
    const text = draft.trim();
    if (!text || sending) return;
    setSending(true);
    try {
      const r = await api.post(`/my/whatsapp/conversations/${conv.id}/reply`,
        { body: text, take_over: botEnabled ? 1 : 0 });
      if (r?.error) notify(r.error, 'error');
      else {
        setDraft('');
        setBot(false);
        load();
      }
    } catch { notify('Erreur envoi', 'error'); }
    finally { setSending(false); }
  };

  const toggleBot = async () => {
    const next = !botEnabled;
    setBot(next);
    try {
      const r = await api.post(`/my/whatsapp/conversations/${conv.id}/bot`, { enabled: next ? 1 : 0 });
      if (r?.error) { setBot(!next); notify(r.error, 'error'); }
      else notify(next ? 'Bot activé pour ce contact' : 'Bot désactivé — vous prenez la main', 'success');
    } catch { setBot(!next); notify('Erreur', 'error'); }
  };

  const name = data.contact_name || data.contact_number;
  const variant = avatarVariant(data.contact_number || name);

  // Group msgs by day
  const grouped = useMemo(() => {
    const out = [];
    let lastDay = null;
    msgs.forEach(m => {
      const day = (m.created_at || '').slice(0, 10);
      if (day !== lastDay) {
        out.push({ kind: 'day', day, key: 'd' + day + Math.random() });
        lastDay = day;
      }
      out.push({ kind: 'msg', m, key: m.id });
    });
    return out;
  }, [msgs]);

  return (
    <div className="thread-screen">
      <div className="topbar safe-top">
        <div className="safe-extend" />
        <button className="topbar-back" onClick={onBack}><Icons.Back /></button>
        <div className={`topbar-avatar ${variant}`} style={{ width: 36, height: 36, fontSize: '0.85rem' }}>
          {initials(name)}
        </div>
        <div className="topbar-title">
          <div className="topbar-name">{name}</div>
          <div className="topbar-sub">
            {data.contact_number}
            {data.line_phone ? ` · via ${data.line_phone}` : ''}
          </div>
        </div>
        <div className="topbar-actions">
          <button className="topbar-icon-btn" onClick={toggleBot}
            title={botEnabled ? 'Bot actif (cliquer pour reprendre)' : 'Bot inactif (cliquer pour activer)'}
            style={{ background: botEnabled ? 'rgba(255,255,255,0.18)' : 'transparent' }}>
            <Icons.Bot />
          </button>
        </div>
      </div>

      {!windowOpen && (
        <div className="thread-banner">
          Fenêtre 24h dépassée — vous devez utiliser un template approuvé.
        </div>
      )}

      <div className="thread-bg" ref={scrollRef}>
        {loading && (
          <div className="empty-state">
            <div className="loading-spinner" style={{ width: 28, height: 28 }} />
          </div>
        )}
        {!loading && grouped.length === 0 && (
          <div className="empty-state">
            <div className="empty-desc">Aucun message encore.</div>
          </div>
        )}
        {grouped.map(item => {
          if (item.kind === 'day') {
            return <div key={item.key} className="day-sep"><span>{fmtDay(item.day)}</span></div>;
          }
          const m = item.m;
          const out = m.direction === 'outbound';
          let media = null;
          if (m.media_urls) {
            try {
              const urls = JSON.parse(m.media_urls);
              media = urls.map((u, i) => (
                <a key={i} className="msg-media-link" href={u} target="_blank" rel="noreferrer">📎 Pièce jointe {i + 1}</a>
              ));
            } catch {}
          }
          const isRead = m.status === 'read';
          const isDelivered = m.status === 'delivered' || isRead;
          return (
            <div key={item.key} className={`msg-row ${out ? 'out' : 'in'}`}>
              <div className="msg-bubble">
                <div className="msg-text">{m.body || (m.num_media > 0 ? '📎 Média' : '')}</div>
                {media}
                <span className="msg-foot">
                  {m.sent_by === 'bot' && <span className="msg-by-bot">bot</span>}
                  {fmtTime(m.created_at)}
                  {out && (
                    <span className={`msg-status ${isRead ? 'read' : ''}`}>
                      {isDelivered ? <Icons.DoubleCheck /> : <Icons.Check />}
                    </span>
                  )}
                </span>
              </div>
            </div>
          );
        })}
      </div>

      <div className="compose-wrap">
        <div className="compose-input-wrap">
          <textarea className="compose-input" rows={1}
            placeholder={windowOpen ? 'Message' : 'Fenêtre fermée — utilisez un template'}
            value={draft}
            onChange={e => setDraft(e.target.value)}
            onKeyDown={e => {
              if (e.key === 'Enter' && !e.shiftKey && !e.metaKey) {
                e.preventDefault();
                send();
              }
            }} />
        </div>
        <button className="compose-send" disabled={sending || !draft.trim()} onClick={send}>
          <Icons.Send />
        </button>
      </div>
    </div>
  );
}

// ==================== ROOT ====================
function AppProvider({ children, value }) {
  return <AppCtx.Provider value={value}>{children}</AppCtx.Provider>;
}

const STORAGE_LINE = 'vocal_wa_line';

function App() {
  const [user, setUser]   = useState(null);
  const [booting, setBoot]= useState(true);
  const [toast, setToast] = useState(null);
  const [selected, setSelected] = useState(null);
  const [tab, setTab]     = useState('discussions');
  const [composing, setComposing] = useState(false);
  const [lines, setLines] = useState([]);
  const [lineId, setLineIdState] = useState(() => {
    const v = localStorage.getItem(STORAGE_LINE);
    return v ? parseInt(v) : null;
  });
  const setLineId = useCallback((id) => {
    setLineIdState(id);
    if (id) localStorage.setItem(STORAGE_LINE, String(id));
    else localStorage.removeItem(STORAGE_LINE);
  }, []);

  const notify = useCallback((msg, type) => setToast({ msg, type }), []);

  const handleLogin = useCallback((token, u) => {
    api.token = token;
    localStorage.setItem(STORAGE_TOKEN, token);
    localStorage.setItem(STORAGE_USER, JSON.stringify(u));
    setUser(u);
  }, []);

  const logout = useCallback(() => {
    api.token = null;
    localStorage.removeItem(STORAGE_TOKEN);
    localStorage.removeItem(STORAGE_USER);
    setUser(null); setSelected(null);
    notify('Déconnecté', 'success');
  }, [notify]);

  useEffect(() => {
    const t = localStorage.getItem(STORAGE_TOKEN);
    const u = localStorage.getItem(STORAGE_USER);
    if (t && u) {
      api.token = t;
      try { setUser(JSON.parse(u)); } catch {}
      api.get('/auth/me').then(r => {
        if (r?.user) setUser(prev => ({ ...prev, ...r.user }));
        else if (r?.error) { api.token = null; localStorage.removeItem(STORAGE_TOKEN); localStorage.removeItem(STORAGE_USER); setUser(null); }
      }).catch(() => {});
    }
    setBoot(false);
    const onLogout = () => { setUser(null); setSelected(null); };
    window.addEventListener('vocal-wa-logout', onLogout);
    return () => window.removeEventListener('vocal-wa-logout', onLogout);
  }, []);

  // Charge les lignes WA du client
  useEffect(() => {
    if (!user) { setLines([]); return; }
    api.get('/my/lines').then(r => {
      const list = (r?.lines || []).filter(l => l.whatsapp_enabled !== 0);
      setLines(list);
    }).catch(() => {});
  }, [user]);

  if (booting) {
    return <div className="app-loading"><div className="loading-spinner" /></div>;
  }

  const showBottomNav = user && !selected && !composing;

  return (
    <AppProvider value={{ user, notify, logout, lines, lineId, setLineId }}>
      <div className={`app-root ${showBottomNav ? 'has-bottom-nav' : ''}`}>
        {!user ? (
          <AuthScreen onLogin={handleLogin} />
        ) : selected ? (
          <ThreadView conv={selected} onBack={() => setSelected(null)} />
        ) : composing ? (
          <NewMessageView onClose={() => setComposing(false)} onOpenConv={setSelected} />
        ) : tab === 'discussions' ? (
          <InboxView mode="discussions" onOpen={setSelected} />
        ) : tab === 'archives' ? (
          <InboxView mode="archives" onOpen={setSelected} />
        ) : tab === 'status' ? (
          <StatusView />
        ) : tab === 'settings' ? (
          <SettingsView />
        ) : null}
        {showBottomNav && (
          <BottomNav tab={tab} onTab={setTab} onCompose={() => setComposing(true)} />
        )}
      </div>
      {toast && <Toast msg={toast.msg} type={toast.type} onDone={() => setToast(null)} />}
    </AppProvider>
  );
}

ReactDOM.createRoot(document.getElementById('app')).render(<App />);
