// ============================================================
// Mensagens.jsx v18 — Evolution API · redesign profissional
// ============================================================
// v6: scroll fixado (flex:1+minHeight:0), lookup de nome via Supabase,
//     painel lateral de detalhes do cliente (projetos, fin., reuniões).
// v7: helpers LID/phone, loadContacts (agenda celular), loadMessages
//     multi-format, disconnect via DELETE, auto-refresh 15s.
// v8: sendMessage usa getPhoneFromChat (fix LID), setSendError inline,
//     contacts map aplicado em resolveName, compose desabilitado p/ LID.
// v9: fix null key (React 195 erros), guard loadMessages JID nulo,
//     parse amigável de erro 400 (número não existe no WhatsApp).
// v10-v13: multi-format findMessages, sort timestamp, finalizados, beta banner.
// v14: fix login CSS (adicionado em colors_and_type.css), dedup por telefone
//     em loadChats (remove @lid duplicado), loadMessages combina 2 JIDs +
//     3 formatos de query + deduplica por key.id, limit↑100.
// v15-v17: MediaLoader (lazy load mídia), GroupPanel, sendMedia (📎),
//     fix toggleFinalizar, fix loadContacts @lid, fix loadMessages filter.
// v18: getMediaType() — detecta tipo de mídia em 3 camadas (msg.message,
//     msg.messageType root-level, msg.mediaType) para cobrir todas as
//     versões da Evolution API. Resolve [mídia] exibido ao invés da mídia real.
// ============================================================

/* global React, ICONS */

const EVOAPI_DEFAULTS = {
  url: 'https://evolution-api-production-f5fdd.up.railway.app',
  key: '1613f5f4985f23401ca09e34341376b26b8ee20946aaeaa898bd8a668d7d2b22'
};
const EVOAPI_STORAGE_KEY = 'inbrivvo.evoapi';
const AUTO_STORAGE_KEY   = 'inbrivvo.evoauto';

function getEvoCfg() {
  try { const s = JSON.parse(localStorage.getItem(EVOAPI_STORAGE_KEY) || '{}'); return { ...EVOAPI_DEFAULTS, ...s }; }
  catch { return EVOAPI_DEFAULTS; }
}
function getAutoConfig() {
  try { return JSON.parse(localStorage.getItem(AUTO_STORAGE_KEY) || '{}'); } catch { return {}; }
}
function getInstanceName(userId) {
  if (!userId) return 'inbrivvo-default';
  return 'inbrivvo-' + userId.replace(/-/g, '').slice(0, 16);
}

async function evoFetch(method, endpoint, body) {
  const { url, key } = getEvoCfg();
  if (!url || !key) throw new Error('EVO_NOT_CONFIGURED');
  const opts = { method, headers: { apikey: key, 'Content-Type': 'application/json' } };
  if (body !== undefined) opts.body = JSON.stringify(body);
  const r = await fetch(`${url}${endpoint}`, opts);
  if (!r.ok) { const t = await r.text(); throw new Error(t || String(r.status)); }
  return r.json().catch(() => ({}));
}
const evoGet  = ep      => evoFetch('GET',  ep);
const evoPost = (ep, b) => evoFetch('POST', ep, b);

// ─── Helpers ───────────────────────────────────────────────────
function fmtPhoneDisplay(raw = '') {
  const n = raw.replace(/\D/g, '');
  const local = n.startsWith('55') ? n.slice(2) : n;
  if (local.length === 11) return `(${local.slice(0,2)}) ${local[2]} ${local.slice(3,7)}-${local.slice(7)}`;
  if (local.length === 10) return `(${local.slice(0,2)}) ${local.slice(2,6)}-${local.slice(6)}`;
  if (n.length > 8) return `+${n.slice(0,2)} ${n.slice(2,4)} ${n.slice(4,8)}-${n.slice(8)}`;
  return raw || '';
}
function fmtPhone(p = '') {
  let n = p.replace(/\D/g, '');
  if ((n.length === 11 || n.length === 10) && !n.startsWith('55')) n = '55' + n;
  return n;
}

function isRealName(s) {
  if (!s) return false;
  const t = s.trim();
  if (!t || t.length < 2) return false;
  if (t.includes('@')) return false;
  if (/^[a-f0-9]{16,}$/i.test(t)) return false;
  if (/^[a-z0-9]{10,}$/.test(t)) return false;
  if (/^\+?[\d\s\-\(\)]{7,}$/.test(t)) return false;
  return true;
}

function chatDisplayName(c, override) {
  if (override) return override;
  // Agenda do celular (contacts map passado pelo ConversasTab)
  if (c._contactName) return c._contactName;
  if (isRealName(c.name))               return c.name.trim();
  if (isRealName(c.pushName))           return c.pushName.trim();
  if (isRealName(c.notify))             return c.notify.trim();
  if (isRealName(c.verifiedBizName))    return c.verifiedBizName.trim();
  const jid = c.id || c.remoteJid || '';
  if (jid.includes('@')) {
    const [part, domain] = jid.split('@');
    if (/^\d{7,}$/.test(part)) return fmtPhoneDisplay(part);
    if (domain === 'g.us')        return part.includes('-') ? 'Grupo' : `Grupo ${part.slice(0,6)}`;
    if (domain === 'broadcast')   return 'Status';
    if (domain === 'newsletter')  return 'Canal WA';
    if (domain === 'lid')         return 'Contato';
  }
  return 'Contato';
}

function isGroup(c) { return (c.id || '').includes('@g.us'); }

// Retorna o número de telefone numérico do chat, ou null se LID/grupo
function getPhoneFromChat(c) {
  const candidates = [c.id || '', c.remoteJid || ''];
  for (const j of candidates) {
    const part = j.split('@')[0];
    if (/^\d{7,}$/.test(part)) return part;
  }
  return null;
}

// Verifica se é um contato LID (identificador criptografado do WhatsApp)
function isLidChat(c) {
  return (c.id || '').endsWith('@lid') && !getPhoneFromChat(c);
}

function chatInitials(name = '') {
  const clean = name.replace(/^\+/, '').replace(/[\(\)\-\s]/g, '');
  const w = name.trim().split(/\s+/);
  if (w.length >= 2 && !/^\d/.test(w[0])) return (w[0][0] + w[w.length-1][0]).toUpperCase();
  return clean.slice(0,2).toUpperCase() || '?';
}

const AVATAR_PALETTE = ['#5B8DEF','#9B5DE5','#F15BB5','#00BBF9','#00C9A7','#FF6B6B','#FFA94D','#3A86FF','#8338EC','#06D6A0'];
function avatarColor(str = '') {
  let h = 0; for (let i = 0; i < str.length; i++) h = (h * 31 + str.charCodeAt(i)) >>> 0;
  return AVATAR_PALETTE[h % AVATAR_PALETTE.length];
}

function fmtTime(ts) {
  if (!ts) return '';
  const d = new Date(typeof ts === 'number' && ts < 1e12 ? ts * 1000 : ts);
  if (isNaN(d)) return '';
  const now  = new Date();
  const diff = now - d;
  if (diff < 60000)   return 'agora';
  if (diff < 3600000) return `${Math.floor(diff/60000)}min`;
  if (d.toDateString() === now.toDateString()) return d.toLocaleTimeString('pt-BR', { hour:'2-digit', minute:'2-digit' });
  const yest = new Date(now); yest.setDate(now.getDate()-1);
  if (d.toDateString() === yest.toDateString()) return 'ontem';
  return d.toLocaleDateString('pt-BR', { day:'2-digit', month:'2-digit' });
}

function lastMsgPreview(c) {
  const m = c.lastMessage?.message;
  if (!m) return '';
  return m.conversation || m.extendedTextMessage?.text
    || (m.imageMessage    && 'Foto')
    || (m.audioMessage    && 'Mensagem de áudio') || (m.pttMessage && 'Mensagem de áudio')
    || (m.videoMessage    && 'Vídeo')
    || (m.documentMessage && (m.documentMessage.fileName || 'Documento'))
    || (m.stickerMessage  && 'Sticker')
    || (m.locationMessage && 'Localização')
    || '';
}

function msgText(m) {
  const msg = m?.message;
  if (!msg) return '[mensagem]';
  return msg.conversation
    || msg.extendedTextMessage?.text
    || (msg.imageMessage    && ('Foto' + (msg.imageMessage.caption ? ': '+msg.imageMessage.caption : '')))
    || (msg.audioMessage || msg.pttMessage ? 'Mensagem de áudio' : null)
    || (msg.videoMessage    && ('Vídeo' + (msg.videoMessage.caption ? ': '+msg.videoMessage.caption : '')))
    || (msg.documentMessage && (msg.documentMessage.fileName || 'Documento'))
    || (msg.stickerMessage  && 'Sticker')
    || (msg.locationMessage && 'Localização')
    || (msg.contactMessage  && (msg.contactMessage.displayName || 'Contato'))
    || '[mídia]';
}

// ─── Ícone inline ──────────────────────────────────────────────
function IcoSvg({ name, size = 14, color = 'currentColor', strokeWidth = 1.75 }) {
  if (!ICONS || !ICONS[name]) return null;
  const d = ICONS[name];
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={color}
      strokeWidth={strokeWidth} strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink:0 }}>
      {(Array.isArray(d) ? d : [d]).map((path, i) => <path key={i} d={path}/>)}
    </svg>
  );
}

// ─── Avatar ────────────────────────────────────────────────────
function WaAvatar({ name, size = 40, src, style: extraStyle }) {
  const [imgErr, setImgErr] = React.useState(false);
  if (src && !imgErr) {
    return (
      <img src={src} alt={name} onError={() => setImgErr(true)}
        style={{ width:size, height:size, borderRadius:'50%', objectFit:'cover', flexShrink:0, ...extraStyle }}
      />
    );
  }
  const bg = avatarColor(name);
  const fz = Math.round(size * 0.36);
  return (
    <div style={{ width:size, height:size, borderRadius:'50%', background:bg, flexShrink:0,
      display:'flex', alignItems:'center', justifyContent:'center',
      fontSize:fz, fontWeight:700, color:'#fff', userSelect:'none', letterSpacing:'-0.5px',
      ...extraStyle }}>
      {chatInitials(name)}
    </div>
  );
}

// ─── Chat Item ─────────────────────────────────────────────────
function ChatItem({ chat, selected, profilePic, nameOverride, onClick }) {
  const name    = chatDisplayName(chat, nameOverride);
  const preview = lastMsgPreview(chat);
  const ts      = chat.lastMessage?.messageTimestamp || chat.updatedAt;
  const unread  = (chat.unreadCount || 0) > 0;

  return (
    <div onClick={onClick} style={{
      display:'flex', alignItems:'center', gap:12,
      padding:'11px 16px', cursor:'pointer',
      background: selected ? 'var(--primary-soft, rgba(29,57,196,.10))' : 'transparent',
      borderLeft: `3px solid ${selected ? 'var(--primary)' : 'transparent'}`,
      transition:'background 120ms',
    }}>
      <WaAvatar name={name} size={42} src={profilePic} />
      <div style={{ flex:1, minWidth:0 }}>
        <div style={{ display:'flex', justifyContent:'space-between', alignItems:'baseline', marginBottom:2 }}>
          <span style={{ fontWeight: unread ? 700 : 500, fontSize:13.5, overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap', maxWidth:160 }}>
            {name}
          </span>
          <span style={{ fontSize:11, color:'var(--fg-muted)', flexShrink:0, marginLeft:6 }}>{fmtTime(ts)}</span>
        </div>
        <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center' }}>
          <span style={{ fontSize:12, color: unread ? 'var(--fg-secondary,var(--fg-primary))' : 'var(--fg-muted)', overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap', maxWidth:180, fontStyle:!preview?'italic':'normal', opacity:!preview?.5:1 }}>
            {preview || 'Sem mensagens'}
          </span>
          {unread && (
            <div style={{ minWidth:18, height:18, borderRadius:999, background:'var(--primary)', color:'#fff', fontSize:10, fontWeight:700, display:'flex', alignItems:'center', justifyContent:'center', flexShrink:0, marginLeft:4, padding:'0 4px' }}>
              {chat.unreadCount > 9 ? '9+' : chat.unreadCount}
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

// ─── Lazy media loader via Evolution API ───────────────────────
function MediaLoader({ msg, instanceName, type }) {
  const [state, setState] = React.useState('idle'); // idle|loading|loaded|error
  const [src,   setSrc]   = React.useState(null);

  async function load() {
    setState('loading');
    try {
      const { url, key } = getEvoCfg();
      const resp = await fetch(`${url}/chat/getBase64FromMediaMessage/${instanceName}`, {
        method:  'POST',
        headers: { apikey: key, 'Content-Type': 'application/json' },
        body:    JSON.stringify({ message: { key: msg.key, message: msg.message }, convertToMp4: false }),
      });
      if (!resp.ok) throw new Error('HTTP ' + resp.status);
      const data   = await resp.json();
      const b64    = data?.base64 || data?.data || '';
      if (!b64) throw new Error('no_data');
      const mime   = getMime();
      setSrc(`data:${mime};base64,${b64}`);
      setState('loaded');
    } catch { setState('error'); }
  }

  function getMime() {
    const m = msg.message;
    if (type === 'image') return m?.imageMessage?.mimetype || 'image/jpeg';
    if (type === 'audio') return m?.audioMessage?.mimetype || m?.pttMessage?.mimetype || 'audio/ogg; codecs=opus';
    if (type === 'video') return m?.videoMessage?.mimetype || 'video/mp4';
    if (type === 'doc')   return m?.documentMessage?.mimetype || 'application/octet-stream';
    return 'application/octet-stream';
  }

  const thumb    = msg.message?.imageMessage?.jpegThumbnail;
  const filename = msg.message?.documentMessage?.fileName || 'Documento';
  const btnStyle = { background:'rgba(255,255,255,.15)', border:'1px solid rgba(255,255,255,.2)', borderRadius:8, padding:'7px 12px', cursor:'pointer', color:'inherit', fontSize:12, fontFamily:'inherit', display:'inline-flex', alignItems:'center', gap:6 };

  // ── Imagem ──
  if (type === 'image') {
    if (state === 'idle') {
      if (thumb) return (
        <div style={{ position:'relative', cursor:'pointer', display:'inline-block', borderRadius:8, overflow:'hidden', maxWidth:220 }} onClick={load}>
          <img src={`data:image/jpeg;base64,${thumb}`} style={{ width:'100%', display:'block', filter:'blur(3px)', transform:'scale(1.05)' }} />
          <div style={{ position:'absolute', inset:0, display:'flex', alignItems:'center', justifyContent:'center', background:'rgba(0,0,0,.35)', gap:6 }}>
            <span style={{ color:'#fff', fontSize:13, fontWeight:600 }}>📷 Ver foto</span>
          </div>
        </div>
      );
      return <button style={btnStyle} onClick={load}>📷 Carregar foto</button>;
    }
    if (state === 'loading') return <span style={{ fontSize:12, opacity:.7 }}>Carregando…</span>;
    if (state === 'error')   return <span style={{ fontSize:12, opacity:.6 }}>📷 Foto (erro)</span>;
    return <img src={src} style={{ maxWidth:240, borderRadius:8, display:'block', cursor:'zoom-in' }} onClick={() => window.open(src,'_blank')} />;
  }

  // ── Áudio / PTT ──
  if (type === 'audio') {
    if (state === 'idle')    return <button style={btnStyle} onClick={load}>🎵 Ouvir áudio</button>;
    if (state === 'loading') return <span style={{ fontSize:12, opacity:.7 }}>Carregando áudio…</span>;
    if (state === 'error')   return <span style={{ fontSize:12, opacity:.6 }}>🎵 Áudio (erro)</span>;
    return <audio controls src={src} style={{ maxWidth:220, height:36, marginTop:2 }} />;
  }

  // ── Vídeo ──
  if (type === 'video') {
    const vthumb = msg.message?.videoMessage?.jpegThumbnail;
    if (state === 'idle') {
      if (vthumb) return (
        <div style={{ position:'relative', cursor:'pointer', display:'inline-block', borderRadius:8, overflow:'hidden', maxWidth:240 }} onClick={load}>
          <img src={`data:image/jpeg;base64,${vthumb}`} style={{ width:'100%', display:'block' }} />
          <div style={{ position:'absolute', inset:0, display:'flex', alignItems:'center', justifyContent:'center', background:'rgba(0,0,0,.4)', borderRadius:8 }}>
            <div style={{ width:44, height:44, borderRadius:'50%', background:'rgba(255,255,255,.9)', display:'flex', alignItems:'center', justifyContent:'center' }}>
              <span style={{ fontSize:18, marginLeft:3 }}>▶</span>
            </div>
          </div>
        </div>
      );
      return <button style={btnStyle} onClick={load}>🎬 Ver vídeo</button>;
    }
    if (state === 'loading') return <span style={{ fontSize:12, opacity:.7 }}>Carregando vídeo…</span>;
    if (state === 'error')   return <span style={{ fontSize:12, opacity:.6 }}>🎬 Vídeo (erro)</span>;
    return <video controls src={src} style={{ maxWidth:280, borderRadius:8, marginTop:2 }} />;
  }

  // ── Documento ──
  if (type === 'doc') {
    if (state === 'idle')    return <button style={btnStyle} onClick={load}>📄 {filename}</button>;
    if (state === 'loading') return <span style={{ fontSize:12, opacity:.7 }}>Preparando documento…</span>;
    if (state === 'error')   return <span style={{ fontSize:12, opacity:.6 }}>📄 {filename} (erro)</span>;
    return (
      <a href={src} download={filename} style={{ ...btnStyle, textDecoration:'none' }}>
        📄 {filename} — Baixar
      </a>
    );
  }

  return <span style={{ fontSize:12 }}>[mídia]</span>;
}

// ─── Detecta tipo de mídia independente do formato da Evolution API ──
function getMediaType(msg) {
  if (!msg) return null;
  const m = msg.message;

  // 1. Campos diretos no msg.message (formato padrão)
  if (m) {
    if (m.imageMessage || m.stickerMessage)                         return 'image';
    if (m.audioMessage || m.pttMessage)                             return 'audio';
    if (m.videoMessage)                                             return 'video';
    if (m.documentMessage || m.documentWithCaptionMessage)          return 'doc';
    // Wrappers aninhados (viewOnce, ephemeral, etc.)
    for (const wrapper of ['viewOnceMessage','ephemeralMessage','documentWithCaptionMessage','viewOnceMessageV2','editedMessage']) {
      const inner = m[wrapper]?.message;
      if (!inner) continue;
      if (inner.imageMessage || inner.stickerMessage)               return 'image';
      if (inner.audioMessage || inner.pttMessage)                   return 'audio';
      if (inner.videoMessage)                                       return 'video';
      if (inner.documentMessage)                                    return 'doc';
    }
  }

  // 2. messageType / type no root (algumas versões da Evolution API)
  const mt = ((msg.messageType || msg.type || '')).toLowerCase().replace(/_/g, '');
  if (mt.includes('image') || mt.includes('sticker'))               return 'image';
  if (mt.includes('audio') || mt.includes('ptt'))                   return 'audio';
  if (mt.includes('video'))                                         return 'video';
  if (mt.includes('document'))                                      return 'doc';

  // 3. mediaType auxiliar
  const aux = ((msg.mediaType || '')).toLowerCase();
  if (aux.includes('image'))                                        return 'image';
  if (aux.includes('audio'))                                        return 'audio';
  if (aux.includes('video'))                                        return 'video';
  if (aux.includes('document') || aux.includes('application'))     return 'doc';

  return null;
}

// ─── Bolha de mensagem ─────────────────────────────────────────
function MsgBubble({ msg, isMe, senderName, instanceName }) {
  const m         = msg?.message;
  const mediaType = getMediaType(msg);
  const hasMid    = !!mediaType;
  const text      = msgText(msg);
  const ts        = msg.messageTimestamp;
  const caption   = m?.imageMessage?.caption || m?.videoMessage?.caption
                  || m?.documentWithCaptionMessage?.message?.imageMessage?.caption || '';

  return (
    <div style={{ display:'flex', flexDirection:'column', alignItems: isMe ? 'flex-end' : 'flex-start', marginBottom:2 }}>
      {!isMe && senderName && (
        <span style={{ fontSize:11, color:'var(--fg-muted)', marginBottom:3, marginLeft:4, fontWeight:600 }}>{senderName}</span>
      )}
      <div style={{
        background: isMe ? 'var(--primary)' : 'var(--bg-elevated)',
        color:      isMe ? '#fff' : 'var(--fg-primary)',
        borderRadius: isMe ? '16px 16px 4px 16px' : '16px 16px 16px 4px',
        padding: hasMid ? '6px' : '10px 14px',
        maxWidth:'68%', fontSize:13.5, lineHeight:1.55,
        boxShadow:'0 1px 4px rgba(0,0,0,.12)', wordBreak:'break-word', overflow:'hidden',
      }}>
        {mediaType === 'image' && <MediaLoader msg={msg} instanceName={instanceName} type="image" />}
        {mediaType === 'audio' && <MediaLoader msg={msg} instanceName={instanceName} type="audio" />}
        {mediaType === 'video' && <MediaLoader msg={msg} instanceName={instanceName} type="video" />}
        {mediaType === 'doc'   && <MediaLoader msg={msg} instanceName={instanceName} type="doc"   />}
        {caption  && <div style={{ marginTop:5, fontSize:13, padding:'0 4px' }}>{caption}</div>}
        {!hasMid  && <span>{text}</span>}
      </div>
      {ts && <span style={{ fontSize:10.5, color:'var(--fg-muted)', marginTop:3, padding:'0 4px' }}>{fmtTime(ts)}</span>}
    </div>
  );
}

// ─── Painel de Detalhes do Cliente ─────────────────────────────
const PROJ_STATUS_COLOR = { andamento:'#3A86FF', ativo:'#3A86FF', revisao:'#FADB14', pausa:'#36CFC9', quase:'#22C55E', concluido:'var(--fg-muted)' };
const PROJ_STATUS_LABEL = { andamento:'Em andamento', ativo:'Em andamento', revisao:'Em revisão', pausa:'Em pausa', quase:'Quase pronto', concluido:'Concluído' };

function WaClientPanel({ chat, resolvedPhone, onClose }) {
  const [loading,      setLoading]      = React.useState(true);
  const [client,       setClient]       = React.useState(null);
  const [projects,     setProjects]     = React.useState([]);
  const [transactions, setTransactions] = React.useState([]);
  const [meetings,     setMeetings]     = React.useState([]);

  // Usa o número resolvido (sem @s.whatsapp.net) passado pelo pai, ou extrai do JID
  const rawJidPart = (chat?.id || '').split('@')[0];
  const phone = resolvedPhone || (/^\d{7,}$/.test(rawJidPart) ? rawJidPart : null);

  React.useEffect(() => {
    setClient(null); setProjects([]); setTransactions([]); setMeetings([]);
    loadData();
  }, [chat?.id]);

  async function loadData() {
    setLoading(true);
    const db = window._supabase || window.sb;
    if (!db || !phone) { setLoading(false); return; }

    const last8 = phone.slice(-8);
    try {
      const { data: rows } = await db
        .from('clients')
        .select('id,name,email,phone,company,segment,origin')
        .ilike('phone', `%${last8}%`)
        .limit(1);

      if (rows?.[0]) {
        const c = rows[0];
        setClient(c);

        const [projRes, txRes] = await Promise.all([
          db.from('projects')
            .select('id,name,status,deadline,progress,budget')
            .ilike('client', `%${c.name}%`)
            .order('created_at', { ascending: false })
            .limit(6),
          db.from('transactions')
            .select('id,date,description,amount,status,type,category')
            .ilike('who', `%${c.name}%`)
            .order('date', { ascending: false })
            .limit(6),
        ]);
        setProjects(projRes.data || []);
        setTransactions(txRes.data || []);

        try {
          const allM = JSON.parse(localStorage.getItem('inbrivvo_meetings') || '[]');
          setMeetings(allM.filter(m => m.clientId === c.id).slice(0, 4));
        } catch {}
      }
    } catch {}
    setLoading(false);
  }

  const totalRecebido = transactions.filter(t => t.type === 'receita' && t.status === 'pago').reduce((s, t) => s + Number(t.amount||0), 0);
  const totalPendente = transactions.filter(t => t.type === 'receita' && t.status !== 'pago').reduce((s, t) => s + Number(t.amount||0), 0);

  return (
    <div style={{ width:272, display:'flex', flexDirection:'column', borderLeft:'1px solid var(--border-subtle)', background:'var(--bg-surface)', overflow:'hidden', flexShrink:0 }}>
      {/* Header */}
      <div style={{ padding:'11px 14px', borderBottom:'1px solid var(--border-subtle)', display:'flex', alignItems:'center', justifyContent:'space-between', flexShrink:0 }}>
        <span style={{ fontSize:11, fontWeight:700, textTransform:'uppercase', letterSpacing:'.07em', color:'var(--fg-muted)' }}>Dados do cliente</span>
        <button onClick={onClose} style={{ background:'none', border:'none', cursor:'pointer', color:'var(--fg-muted)', padding:'2px 6px', borderRadius:5, fontSize:18, lineHeight:1, fontFamily:'inherit' }}>×</button>
      </div>

      {/* Body */}
      <div style={{ flex:1, minHeight:0, overflowY:'auto', padding:'14px' }}>
        {loading ? (
          <div style={{ padding:'28px 0', textAlign:'center' }}>
            <WaSpinner label="Buscando…" />
          </div>
        ) : !client ? (
          <div style={{ textAlign:'center', padding:'28px 0' }}>
            <div style={{ width:48, height:48, borderRadius:12, background:'var(--bg-elevated)', display:'flex', alignItems:'center', justifyContent:'center', margin:'0 auto 10px', opacity:.4 }}>
              <IcoSvg name="users" size={22} />
            </div>
            <div style={{ fontSize:13, fontWeight:600, marginBottom:5 }}>Não cadastrado</div>
            <div style={{ fontSize:11.5, color:'var(--fg-muted)', marginBottom:14, lineHeight:1.65 }}>
              {fmtPhoneDisplay(phone) || phone}<br/>não está na sua base de clientes.
            </div>
            <button
              onClick={() => window.__navigateTo?.('clients')}
              style={{ ...WaS.btnPrimary, fontSize:12, padding:'7px 16px' }}>
              Cadastrar cliente
            </button>
          </div>
        ) : (
          <div style={{ display:'flex', flexDirection:'column', gap:18 }}>

            {/* ── Perfil ── */}
            <div>
              <div style={{ display:'flex', gap:10, alignItems:'center', marginBottom:10 }}>
                <WaAvatar name={client.name} size={46} />
                <div style={{ minWidth:0 }}>
                  <div style={{ fontWeight:700, fontSize:14, overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>{client.name}</div>
                  {client.company && <div style={{ fontSize:11.5, color:'var(--fg-muted)', marginTop:1 }}>{client.company}</div>}
                </div>
              </div>
              <div style={{ display:'flex', flexDirection:'column', gap:4 }}>
                {client.email && (
                  <div style={{ fontSize:12, color:'var(--fg-muted)', display:'flex', gap:6, alignItems:'center' }}>
                    <IcoSvg name="mail" size={11} /> {client.email}
                  </div>
                )}
                {client.phone && (
                  <div style={{ fontSize:12, color:'var(--fg-muted)', display:'flex', gap:6, alignItems:'center' }}>
                    <IcoSvg name="msg" size={11} /> {client.phone}
                  </div>
                )}
                {client.segment && (
                  <div style={{ fontSize:12, color:'var(--fg-muted)', display:'flex', gap:6, alignItems:'center' }}>
                    <IcoSvg name="tag" size={11} /> {client.segment}
                  </div>
                )}
              </div>
            </div>

            {/* ── Financeiro ── */}
            {(totalRecebido > 0 || totalPendente > 0) && (
              <div>
                <div style={WaS.sectionTitle}>Financeiro</div>
                <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:8 }}>
                  <div style={WaS.miniCard}>
                    <div style={{ fontSize:10, color:'var(--fg-muted)', marginBottom:3 }}>Recebido</div>
                    <div style={{ fontSize:14, fontWeight:700, color:'#22C55E' }}>
                      {totalRecebido > 0 ? 'R$ ' + totalRecebido.toLocaleString('pt-BR') : '—'}
                    </div>
                  </div>
                  <div style={WaS.miniCard}>
                    <div style={{ fontSize:10, color:'var(--fg-muted)', marginBottom:3 }}>A receber</div>
                    <div style={{ fontSize:14, fontWeight:700, color: totalPendente > 0 ? '#FADB14' : 'var(--fg-muted)' }}>
                      {totalPendente > 0 ? 'R$ ' + totalPendente.toLocaleString('pt-BR') : '—'}
                    </div>
                  </div>
                </div>
              </div>
            )}

            {/* ── Projetos ── */}
            {projects.length > 0 && (
              <div>
                <div style={WaS.sectionTitle}>
                  Projetos
                  <span style={{ fontWeight:400, marginLeft:4 }}>({projects.length})</span>
                </div>
                <div style={{ display:'flex', flexDirection:'column', gap:6 }}>
                  {projects.map(p => (
                    <div key={p.id} style={{ padding:'9px 10px', borderRadius:8, background:'var(--bg-elevated)', border:'1px solid var(--border-subtle)' }}>
                      <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', gap:6, marginBottom: p.progress ? 5 : 0 }}>
                        <span style={{ fontSize:12.5, fontWeight:600, overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap', flex:1 }}>{p.name}</span>
                        <span style={{ fontSize:10, color: PROJ_STATUS_COLOR[p.status] || 'var(--fg-muted)', fontWeight:600, flexShrink:0 }}>
                          {PROJ_STATUS_LABEL[p.status] || p.status || 'Ativo'}
                        </span>
                      </div>
                      {p.progress != null && p.progress > 0 && (
                        <div style={{ height:3, background:'var(--border-subtle)', borderRadius:2 }}>
                          <div style={{ height:'100%', width:`${Math.min(p.progress,100)}%`, background:'var(--primary)', borderRadius:2 }} />
                        </div>
                      )}
                      {p.deadline && (
                        <div style={{ fontSize:10, color:'var(--fg-muted)', marginTop:4 }}>
                          Prazo: {new Date(p.deadline).toLocaleDateString('pt-BR')}
                        </div>
                      )}
                    </div>
                  ))}
                </div>
              </div>
            )}

            {/* ── Últimas transações ── */}
            {transactions.length > 0 && (
              <div>
                <div style={WaS.sectionTitle}>Últimas transações</div>
                <div style={{ display:'flex', flexDirection:'column', gap:5 }}>
                  {transactions.slice(0,4).map(t => (
                    <div key={t.id} style={{ display:'flex', justifyContent:'space-between', alignItems:'center', padding:'7px 10px', borderRadius:7, background:'var(--bg-elevated)', border:'1px solid var(--border-subtle)', gap:8 }}>
                      <div style={{ minWidth:0 }}>
                        <div style={{ fontSize:12, fontWeight:600, overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>{t.description || t.category || '—'}</div>
                        {t.date && <div style={{ fontSize:10, color:'var(--fg-muted)', marginTop:1 }}>{new Date(t.date+'T00:00:00').toLocaleDateString('pt-BR')}</div>}
                      </div>
                      <div style={{ flexShrink:0, fontWeight:700, fontSize:12, color: t.type === 'receita' ? '#22C55E' : '#FF6B6B' }}>
                        {t.type === 'receita' ? '+' : '-'} R$ {Number(t.amount||0).toLocaleString('pt-BR')}
                      </div>
                    </div>
                  ))}
                </div>
              </div>
            )}

            {/* ── Reuniões ── */}
            {meetings.length > 0 && (
              <div>
                <div style={WaS.sectionTitle}>Reuniões</div>
                <div style={{ display:'flex', flexDirection:'column', gap:5 }}>
                  {meetings.map(m => (
                    <div key={m.id} style={{ padding:'8px 10px', borderRadius:7, background:'var(--bg-elevated)', border:'1px solid var(--border-subtle)' }}>
                      <div style={{ fontSize:12, fontWeight:600, marginBottom:2 }}>{m.title}</div>
                      <div style={{ fontSize:10.5, color:'var(--fg-muted)' }}>
                        {new Date(m.date+'T00:00:00').toLocaleDateString('pt-BR')} · {m.hInicio}–{m.hFim}
                      </div>
                    </div>
                  ))}
                </div>
              </div>
            )}

            {/* Nenhum dado além do cadastro */}
            {projects.length === 0 && transactions.length === 0 && meetings.length === 0 && (
              <div style={{ fontSize:12, color:'var(--fg-muted)', textAlign:'center', lineHeight:1.7, padding:'4px 0' }}>
                Nenhum projeto, transação ou reunião<br/>registrada para este cliente ainda.
              </div>
            )}

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

// ─── Painel de Grupo (membros) ─────────────────────────────────
function GroupPanel({ chat, instanceName, contacts, onClose }) {
  const [members,   setMembers]   = React.useState([]);
  const [groupInfo, setGroupInfo] = React.useState(null);
  const [loading,   setLoading]   = React.useState(true);

  React.useEffect(() => { loadGroup(); }, [chat?.id]);

  async function loadGroup() {
    setLoading(true); setMembers([]); setGroupInfo(null);
    const { url, key } = getEvoCfg();
    const headers = { apikey: key, 'Content-Type': 'application/json' };

    // Tenta buscar info + participantes via findGroupInfos (Evolution v2)
    try {
      const r = await fetch(`${url}/group/findGroupInfos/${instanceName}?groupJid=${encodeURIComponent(chat.id)}`, { headers });
      if (r.ok) {
        const d = await r.json();
        const info = Array.isArray(d) ? d[0] : d;
        if (info) {
          setGroupInfo(info);
          const parts = info.participants || info.members || [];
          if (parts.length > 0) { setMembers(parts); setLoading(false); return; }
        }
      }
    } catch {}

    // Fallback: endpoint /group/participants
    try {
      const r = await fetch(`${url}/group/participants/${instanceName}?groupJid=${encodeURIComponent(chat.id)}`, { headers });
      if (r.ok) {
        const d = await r.json();
        setMembers(Array.isArray(d) ? d : (d?.participants || []));
      }
    } catch {}
    setLoading(false);
  }

  function resolveName(jid = '') {
    const phone = jid.split('@')[0];
    return contacts[jid] || contacts[phone] || fmtPhoneDisplay(phone) || jid;
  }

  const subject = groupInfo?.subject || chat.name || chat.pushName || 'Grupo';

  return (
    <div style={{ width:272, display:'flex', flexDirection:'column', borderLeft:'1px solid var(--border-subtle)', background:'var(--bg-surface)', overflow:'hidden', flexShrink:0 }}>
      <div style={{ padding:'11px 14px', borderBottom:'1px solid var(--border-subtle)', display:'flex', alignItems:'center', justifyContent:'space-between', flexShrink:0 }}>
        <span style={{ fontSize:11, fontWeight:700, textTransform:'uppercase', letterSpacing:'.07em', color:'var(--fg-muted)' }}>Grupo · {members.length} membros</span>
        <button onClick={onClose} style={{ background:'none', border:'none', cursor:'pointer', color:'var(--fg-muted)', padding:'2px 6px', borderRadius:5, fontSize:18, lineHeight:1, fontFamily:'inherit' }}>×</button>
      </div>

      <div style={{ flex:1, minHeight:0, overflowY:'auto', padding:14 }}>
        {/* Info do grupo */}
        <div style={{ padding:'10px 12px', background:'var(--bg-elevated)', borderRadius:10, marginBottom:14 }}>
          <div style={{ fontWeight:700, fontSize:14, marginBottom: groupInfo?.desc ? 4 : 0 }}>{subject}</div>
          {groupInfo?.desc && <div style={{ fontSize:12, color:'var(--fg-muted)', lineHeight:1.55 }}>{groupInfo.desc}</div>}
          {groupInfo?.creation && <div style={{ fontSize:11, color:'var(--fg-muted)', marginTop:4 }}>Criado em {new Date(groupInfo.creation * 1000).toLocaleDateString('pt-BR')}</div>}
        </div>

        {/* Membros */}
        {loading ? (
          <div style={{ padding:'16px 0', textAlign:'center' }}><WaSpinner label="Buscando membros…" /></div>
        ) : members.length === 0 ? (
          <div style={{ fontSize:13, color:'var(--fg-muted)', textAlign:'center', padding:'16px 0' }}>Nenhum membro encontrado.</div>
        ) : (
          <div style={{ display:'flex', flexDirection:'column', gap:5 }}>
            {members.map((m, i) => {
              const jid   = m.id || m.jid || m.participant || m.remoteJid || '';
              const phone = jid.split('@')[0];
              const name  = resolveName(jid);
              const isAdmin   = m.admin === 'admin' || m.admin === 'superadmin' || m.admin === true;
              const isSuperAdmin = m.admin === 'superadmin';
              return (
                <div key={jid || i} style={{ display:'flex', alignItems:'center', gap:9, padding:'7px 10px', borderRadius:8, background:'var(--bg-elevated)', border:'1px solid var(--border-subtle)' }}>
                  <WaAvatar name={name} size={34} />
                  <div style={{ flex:1, minWidth:0 }}>
                    <div style={{ fontSize:13, fontWeight:600, overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>{name}</div>
                    <div style={{ fontSize:11, color:'var(--fg-muted)' }}>{fmtPhoneDisplay(phone)}</div>
                  </div>
                  {isSuperAdmin && <span style={{ fontSize:10, background:'rgba(250,219,20,.15)', color:'#FADB14', borderRadius:5, padding:'2px 6px', fontWeight:700, flexShrink:0 }}>Dono</span>}
                  {isAdmin && !isSuperAdmin && <span style={{ fontSize:10, background:'rgba(29,57,196,.15)', color:'var(--primary)', borderRadius:5, padding:'2px 6px', fontWeight:700, flexShrink:0 }}>Admin</span>}
                </div>
              );
            })}
          </div>
        )}
      </div>
    </div>
  );
}

// ─── QR Connect ────────────────────────────────────────────────
function QRConnectTab({ instanceName, onConnected }) {
  const [status, setStatus] = React.useState('idle');
  const [qrCode, setQrCode] = React.useState(null);
  const [error,  setError]  = React.useState('');
  const pollRef = React.useRef(null);
  const stopPoll = () => { clearInterval(pollRef.current); pollRef.current = null; };
  React.useEffect(() => () => stopPoll(), []);

  async function startConnect() {
    setError(''); setStatus('creating');
    try {
      try { await evoPost('/instance/create', { instanceName, integration:'WHATSAPP-BAILEYS', qrcode:true }); }
      catch (e) { if (!e.message?.includes('already')) throw e; }
      setStatus('qr');
      const d = await evoGet(`/instance/connect/${instanceName}`);
      if (d?.base64) setQrCode(d.base64);
      stopPoll();
      pollRef.current = setInterval(async () => {
        try {
          const st    = await evoGet(`/instance/connectionState/${instanceName}`);
          const state = st?.instance?.state || st?.state || '';
          if (state === 'open') { stopPoll(); setStatus('connected'); onConnected?.(); }
          else { try { const qd = await evoGet(`/instance/connect/${instanceName}`); if (qd?.base64) setQrCode(qd.base64); } catch {} }
        } catch {}
      }, 5000);
    } catch (e) { setStatus('error'); setError(e.message || 'Erro ao conectar'); }
  }

  if (status === 'connected') return (
    <div style={{ display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center', flex:1, gap:16, textAlign:'center', padding:40 }}>
      <div style={{ width:72, height:72, borderRadius:'50%', background:'#22C55E18', display:'flex', alignItems:'center', justifyContent:'center' }}>
        <IcoSvg name="check" size={34} color="#22C55E" />
      </div>
      <div style={{ fontSize:20, fontWeight:700 }}>WhatsApp Conectado!</div>
      <div style={{ color:'var(--fg-muted)', fontSize:14 }}>Seu WhatsApp está pronto para uso no inBrivvo.</div>
    </div>
  );

  return (
    <div style={{ flex:1, overflowY:'auto', display:'flex', flexDirection:'column', alignItems:'center', padding:'52px 20px', gap:28 }}>
      <div style={{ textAlign:'center', maxWidth:360 }}>
        <div style={{ width:72, height:72, borderRadius:18, background:'var(--primary-soft, rgba(29,57,196,.12))', display:'flex', alignItems:'center', justifyContent:'center', margin:'0 auto 18px' }}>
          <IcoSvg name="msg" size={34} color="var(--primary)" strokeWidth={1.5} />
        </div>
        <div style={{ fontSize:22, fontWeight:700, marginBottom:8 }}>Conecte seu WhatsApp</div>
        <div style={{ color:'var(--fg-muted)', fontSize:14, lineHeight:1.7 }}>
          Escaneie o QR Code com seu celular para começar a usar o WhatsApp diretamente no inBrivvo.
        </div>
      </div>
      {(status === 'idle' || status === 'error') && (
        <button onClick={startConnect} style={{ background:'var(--primary)', color:'#fff', border:'none', borderRadius:10, padding:'13px 38px', fontSize:15, fontWeight:600, cursor:'pointer', display:'flex', alignItems:'center', gap:8, fontFamily:'inherit' }}>
          <IcoSvg name="link" size={16} color="#fff" />
          Conectar WhatsApp
        </button>
      )}
      {error && <div style={{ color:'#f44', fontSize:13, maxWidth:320, textAlign:'center' }}>{error}</div>}
      {status === 'creating' && <WaSpinner label="Preparando…" />}
      {(status === 'qr' || status === 'connecting') && (
        <div style={{ display:'flex', flexDirection:'column', alignItems:'center', gap:16 }}>
          {qrCode
            ? <div style={{ background:'#fff', borderRadius:16, padding:20, boxShadow:'0 8px 32px rgba(0,0,0,.18)' }}>
                <img src={qrCode} alt="QR Code" style={{ width:224, height:224, display:'block' }} />
              </div>
            : <div style={{ width:224, height:224, background:'var(--bg-elevated)', borderRadius:16, display:'flex', alignItems:'center', justifyContent:'center', color:'var(--fg-muted)', fontSize:13, border:'2px dashed var(--border)' }}>
                Carregando QR…
              </div>
          }
          <div style={{ color:'var(--fg-muted)', fontSize:13, textAlign:'center', maxWidth:280, lineHeight:1.7 }}>
            WhatsApp → <strong>Menu</strong> → <strong>Dispositivos vinculados</strong> → <strong>Vincular dispositivo</strong>
          </div>
          {status === 'connecting' && <WaSpinner label="Aguardando escaneamento…" color="var(--primary)" />}
        </div>
      )}
      <style>{`@keyframes _waspin { to { transform:rotate(360deg); } }`}</style>
    </div>
  );
}

// ─── Finalizados (localStorage) ────────────────────────────────
const WA_FINAL_KEY = 'inbrivvo.wa.finalizados';
function getFinalizados() {
  try { return new Set(JSON.parse(localStorage.getItem(WA_FINAL_KEY) || '[]')); } catch { return new Set(); }
}
function saveFinalizados(s) {
  try { localStorage.setItem(WA_FINAL_KEY, JSON.stringify([...s])); } catch {}
}

// ─── Conversas ─────────────────────────────────────────────────
function ConversasTab({ instanceName }) {
  const [chats,        setChats]        = React.useState([]);
  const [loading,      setLoading]      = React.useState(true);
  const [selected,     setSelected]     = React.useState(null);
  const [messages,     setMessages]     = React.useState([]);
  const [msgLoading,   setMsgLoading]   = React.useState(false);
  const [newMsg,       setNewMsg]       = React.useState('');
  const [sending,      setSending]      = React.useState(false);
  const [sendError,    setSendError]    = React.useState('');
  const [msgError,     setMsgError]     = React.useState('');
  const [search,       setSearch]       = React.useState('');
  const [profilePics,  setProfilePics]  = React.useState({});
  const [clientNames,  setClientNames]  = React.useState({}); // chatId → name from Supabase (null = buscou, não achou)
  const [contacts,     setContacts]     = React.useState({}); // phone → name from agenda do celular
  const [showClient,   setShowClient]   = React.useState(false);
  const [showGroup,    setShowGroup]    = React.useState(false);
  const [finalizados,  setFinalizados]  = React.useState(() => getFinalizados());
  const [chatFilter,   setChatFilter]   = React.useState('ativos'); // 'ativos' | 'finalizados'
  const [betaBanner,   setBetaBanner]   = React.useState(() => localStorage.getItem('wa.beta.dismissed') !== '1');

  const msgListRef  = React.useRef(null);
  const textareaRef = React.useRef(null);
  const autoRefRef  = React.useRef(null);
  const mediaFileRef= React.useRef(null);

  React.useEffect(() => { loadChats(); }, [instanceName]);

  // Auto-refresh messages a cada 5s quando há conversa aberta
  React.useEffect(() => {
    clearInterval(autoRefRef.current);
    if (selected) {
      autoRefRef.current = setInterval(() => loadMessages(selected), 5000);
    }
    return () => clearInterval(autoRefRef.current);
  }, [selected?.id]);

  React.useEffect(() => { if (selected) loadMessages(selected); }, [selected?.id]);
  React.useEffect(() => {
    if (msgListRef.current) msgListRef.current.scrollTop = msgListRef.current.scrollHeight;
  }, [messages]);

  async function loadChats() {
    setLoading(true);
    try {
      const d   = await evoPost(`/chat/findChats/${instanceName}`, {});
      const arr = Array.isArray(d) ? d : [];
      arr.sort((a, b) => {
        const ta = a.lastMessage?.messageTimestamp || 0;
        const tb = b.lastMessage?.messageTimestamp || 0;
        return tb - ta;
      });
      // Deduplicação: remove contatos @lid duplicados que também aparecem como @s.whatsapp.net
      // Mantém o primeiro encontrado (mais recente) quando o mesmo telefone aparece duas vezes.
      const seenPhones = new Set();
      const deduped = arr.filter(c => {
        const phone = getPhoneFromChat(c);
        if (!phone) return true; // grupos, lid puro, broadcast — manter
        if (seenPhones.has(phone)) return false;
        seenPhones.add(phone);
        return true;
      });
      setChats(deduped.slice(0, 80));
    } catch { setChats([]); }
    setLoading(false);
    // Carrega agenda do celular em paralelo
    loadContacts();
  }

  // Busca os contatos da agenda do celular via Evolution API
  async function loadContacts() {
    try {
      // Tenta endpoint v2 primeiro, depois v1
      let data = null;
      for (const ep of [`/contact/fetchContacts/${instanceName}`, `/contact/findContacts/${instanceName}`]) {
        try {
          const { url, key } = getEvoCfg();
          const r = await fetch(`${url}${ep}`, { method:'POST', headers:{ apikey:key, 'Content-Type':'application/json' }, body:'{}' });
          if (r.ok) { data = await r.json(); break; }
        } catch {}
      }
      if (!data) return;
      const arr = Array.isArray(data) ? data
        : Array.isArray(data?.contacts) ? data.contacts
        : Array.isArray(data?.data) ? data.data : [];
      const map = {};
      arr.forEach(c => {
        const jid   = c.id || c.remoteJid || '';
        const phone = jid.split('@')[0];
        const name  = c.pushName || c.name || c.fullName || c.notify || '';
        if (isRealName(name)) {
          if (phone) map[phone] = name;   // número → nome (ex: 5531...)
          if (jid)   map[jid]   = name;   // JID completo → nome (cobre @lid e @s.whatsapp.net)
        }
      });
      setContacts(map);
    } catch {}
  }

  async function loadMessages(chat) {
    setMsgLoading(true);
    setMsgError('');
    const chatId = chat?.id || chat?.remoteJid || '';
    // Sem JID ou JID LID puro — não faz query no servidor
    if (!chatId || isLidChat(chat)) { setMessages([]); setMsgLoading(false); return; }
    // Usa o JID de telefone se disponível, senão o JID original
    const phone      = getPhoneFromChat(chat);
    const jidToQuery = phone ? `${phone}@s.whatsapp.net` : chatId;

    function parseResp(d) {
      if (!d) return [];
      return Array.isArray(d)                    ? d :
             Array.isArray(d?.messages?.records) ? d.messages.records :
             Array.isArray(d?.messages)          ? d.messages :
             Array.isArray(d?.records)           ? d.records :
             Array.isArray(d?.data?.records)     ? d.data.records :
             Array.isArray(d?.data)              ? d.data : [];
    }

    // Tenta 3 formatos diferentes para o mesmo JID, retorna o primeiro que trouxer dados
    async function fetchWithJid(jid) {
      // Formato 1: key.remoteJid aninhado (Evolution API v2 padrão)
      try {
        const d = await evoPost(`/chat/findMessages/${instanceName}`, {
          where: { key: { remoteJid: jid } }, limit: 100,
        });
        const r = parseResp(d);
        if (r.length > 0) return r;
      } catch {}
      // Formato 2: remoteJid na raiz do where
      try {
        const d = await evoPost(`/chat/findMessages/${instanceName}`, {
          where: { remoteJid: jid }, limit: 100,
        });
        const r = parseResp(d);
        if (r.length > 0) return r;
      } catch {}
      // Formato 3: campo chatId (algumas versões do Evolution API)
      try {
        const d = await evoPost(`/chat/findMessages/${instanceName}`, {
          where: { chatId: jid }, limit: 100,
        });
        return parseResp(d);
      } catch {}
      return [];
    }

    try {
      // Busca com o JID principal (preferencialmente phone@s.whatsapp.net)
      const msgs1 = await fetchWithJid(jidToQuery);
      // Se o JID original é diferente, busca também com ele (catch @lid duplicado)
      const msgs2 = (jidToQuery !== chatId) ? await fetchWithJid(chatId) : [];

      // Merge + deduplicação por key.id
      const seen = new Set();
      const merged = [...msgs1, ...msgs2].filter(m => {
        const uid = m.key?.id || m.id || `${m.messageTimestamp}-${m.key?.fromMe}`;
        if (!uid || seen.has(uid)) return false;
        seen.add(uid);
        return true;
      });

      // Filtra client-side por remoteJid para garantir que só aparecem mensagens
      // desta conversa — necessário porque algumas versões da Evolution API
      // retornam todas as mensagens da instância sem filtrar pelo where.
      const phoneNum   = phone || '';
      const validJids  = new Set([
        chatId,
        jidToQuery,
        phoneNum ? `${phoneNum}@s.whatsapp.net` : null,
        phoneNum ? `${phoneNum}@c.us` : null,
      ].filter(Boolean));

      function jidMatches(jid = '') {
        if (!jid) return true; // sem JID: não filtra (mensagens próprias)
        if (validJids.has(jid)) return true;
        // Compara só o número (remove domínio)
        const n = jid.split('@')[0];
        return phoneNum ? n === phoneNum : false;
      }

      const forThisChat = merged.filter(m => {
        const rjid = m.key?.remoteJid || m.remoteJid || '';
        return jidMatches(rjid);
      });

      // Se o filtro eliminou tudo mas o merge tinha mensagens → API não retornou campo remoteJid
      // Usa o merge completo nesse caso (melhor que nada)
      const toShow = forThisChat.length > 0 ? forThisChat : (merged.length > 0 ? merged : []);

      if (toShow.length === 0) {
        setMessages([]);
      } else {
        // Ordena por timestamp: mais antigo no topo, mais recente no fundo
        const sorted = toShow.sort((a, b) => (a.messageTimestamp || 0) - (b.messageTimestamp || 0));
        setMessages(sorted.slice(-50));
      }
    } catch {
      setMessages([]);
      setMsgError('Não foi possível carregar o histórico. Verifique a conexão.');
    }
    setMsgLoading(false);
  }

  async function fetchProfilePic(chat) {
    const id    = chat.id || '';
    const phone = id.split('@')[0];
    if (profilePics[id] || isGroup(chat) || !/^\d{7,}$/.test(phone)) return;
    try {
      const d = await evoPost(`/misc/getProfilePictureUrl/${instanceName}`, { number: phone });
      const url = d?.profilePictureUrl || d?.picture || '';
      if (url) setProfilePics(p => ({ ...p, [id]: url }));
    } catch {}
  }

  // Lookup do nome no Supabase quando abre uma conversa
  async function lookupClientName(chat) {
    const id    = chat.id || '';
    const phone = id.split('@')[0];
    if (!(/^\d{7,}$/.test(phone))) return;
    if (clientNames[id] !== undefined) return; // já buscou
    const db = window._supabase || window.sb;
    if (!db) return;
    const last8 = phone.slice(-8);
    try {
      const { data } = await db.from('clients').select('name').ilike('phone', `%${last8}%`).limit(1);
      const name = data?.[0]?.name || null;
      setClientNames(prev => ({ ...prev, [id]: name }));
    } catch {}
  }

  function selectChat(chat) {
    setSelected(chat);
    setShowClient(false);
    setShowGroup(false);
    setMessages([]);
    setMsgError('');
    fetchProfilePic(chat);
    lookupClientName(chat);
  }

  async function sendMedia(file) {
    const phone = getPhoneFromChat(selected);
    if (!phone) { setSendError('Identificador criptografado — não é possível enviar mídia.'); return; }
    setSending(true); setSendError('');
    try {
      await new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onload = async ev => {
          try {
            const b64 = ev.target.result.split(',')[1];
            let mediatype = 'document';
            if (file.type.startsWith('image/')) mediatype = 'image';
            else if (file.type.startsWith('audio/')) mediatype = 'audio';
            else if (file.type.startsWith('video/')) mediatype = 'video';
            await evoPost(`/message/sendMedia/${instanceName}`, {
              number: phone, mediatype, media: b64, filename: file.name, caption: '',
            });
            resolve();
          } catch (e) { reject(e); }
        };
        reader.onerror = () => reject(new Error('Falha ao ler arquivo'));
        reader.readAsDataURL(file);
      });
      setTimeout(() => { loadMessages(selected); loadChats(); }, 1500);
    } catch (e) {
      setSendError('Erro ao enviar: ' + (e.message || '').slice(0, 80));
    }
    setSending(false);
  }

  function toggleFinalizar(chatId) {
    const wasAlreadyFinal = finalizados.has(chatId);
    setFinalizados(prev => {
      const n = new Set(prev);
      if (n.has(chatId)) n.delete(chatId); else n.add(chatId);
      saveFinalizados(n);
      return n;
    });
    if (!wasAlreadyFinal) {
      // Finalizando: muda para aba Finalizados para o usuário ver a conversa lá
      setChatFilter('finalizados');
      // Mantém a conversa aberta (não limpa selected/messages)
    } else {
      // Reabrindo: muda para aba Ativos
      setChatFilter('ativos');
    }
  }

  async function sendMessage() {
    if (!selected || !newMsg.trim()) return;
    const phone = getPhoneFromChat(selected);
    if (!phone) {
      setSendError('Identificador criptografado — não é possível enviar mensagem por aqui.');
      return;
    }
    setSendError('');
    setSending(true);
    try {
      await evoPost(`/message/sendText/${instanceName}`, { number: phone, text: newMsg.trim() });
      setNewMsg('');
      // Duas tentativas de reload: 1.2s e 3.5s (API pode demorar a indexar)
      setTimeout(() => loadMessages(selected), 1200);
      setTimeout(() => { loadMessages(selected); loadChats(); }, 3500);
    } catch (e) {
      let errMsg = 'Erro ao enviar.';
      try {
        const parsed = JSON.parse(e.message);
        const msgs = parsed?.response?.message;
        if (Array.isArray(msgs) && msgs[0]?.exists === false) {
          errMsg = 'Número não encontrado no WhatsApp. Verifique o contato.';
        } else if (parsed?.message) {
          errMsg = parsed.message;
        }
      } catch { errMsg = e.message?.length < 120 ? e.message : 'Erro ao enviar. Tente novamente.'; }
      setSendError(errMsg);
    }
    setSending(false);
    textareaRef.current?.focus();
  }

  // Resolve o nome de um chat: Supabase > agenda do celular (JID ou phone) > dados do chat
  // clientNames[id] = string → nome encontrado no Supabase
  // clientNames[id] = null   → já buscou, não achou
  // clientNames[id] = undefined → ainda não buscou
  function resolveName(c) {
    const fromDb = clientNames[c.id];
    if (fromDb != null && fromDb !== undefined) return fromDb;
    // Tenta pelo JID completo primeiro (cobre @lid cujos contatos foram importados)
    if (c.id && contacts[c.id]) return contacts[c.id];
    // Depois tenta só pelo número
    const phone = getPhoneFromChat(c);
    if (phone && contacts[phone]) return contacts[phone];
    return chatDisplayName(c);
  }

  const filtered = chats.filter(c => {
    const isFinal = finalizados.has(c.id || '');
    if (chatFilter === 'finalizados' && !isFinal) return false;
    if (chatFilter === 'ativos'      &&  isFinal) return false;
    if (!search) return true;
    const name = resolveName(c);
    return name.toLowerCase().includes(search.toLowerCase());
  });

  const selName = selected ? resolveName(selected) : '';
  const selPic  = selected ? profilePics[selected.id] : undefined;

  return (
    <div style={{ display:'flex', flex:1, minHeight:0, overflow:'hidden' }}>

      {/* ── Lista ── */}
      <div style={{ width:288, display:'flex', flexDirection:'column', borderRight:'1px solid var(--border-subtle)', flexShrink:0, overflow:'hidden', background:'var(--bg-surface)' }}>
        {/* Banner Beta */}
        {betaBanner && (
          <div style={{ margin:'8px 10px 0', padding:'8px 10px', background:'rgba(250,219,20,.08)', border:'1px solid rgba(250,219,20,.25)', borderRadius:8, fontSize:11.5, display:'flex', gap:7, alignItems:'flex-start', flexShrink:0 }}>
            <span style={{ fontSize:13, flexShrink:0 }}>⚡</span>
            <div style={{ flex:1, color:'var(--fg-secondary,var(--fg-primary))', lineHeight:1.5 }}>
              <strong>Beta</strong> — Módulo em testes. Mensagens podem demorar até 5s para aparecer.
            </div>
            <button onClick={() => { setBetaBanner(false); localStorage.setItem('wa.beta.dismissed','1'); }} style={{ background:'none', border:'none', cursor:'pointer', color:'var(--fg-muted)', fontSize:16, padding:0, lineHeight:1, flexShrink:0 }}>×</button>
          </div>
        )}

        <div style={{ padding:'10px 12px 8px', borderBottom:'1px solid var(--border-subtle)', flexShrink:0 }}>
          {/* Tabs Ativos / Finalizados */}
          <div style={{ display:'flex', marginBottom:8, background:'var(--bg-elevated)', borderRadius:7, padding:2, gap:2 }}>
            {[['ativos','Ativos'], ['finalizados','Finalizados']].map(([id, label]) => (
              <button key={id} onClick={() => { setChatFilter(id); setSelected(null); setMessages([]); }}
                style={{ flex:1, background: chatFilter === id ? 'var(--bg-surface)' : 'transparent',
                  border:'none', padding:'5px 0', cursor:'pointer', fontSize:12,
                  fontWeight: chatFilter === id ? 700 : 400,
                  color: chatFilter === id ? 'var(--fg-primary)' : 'var(--fg-muted)',
                  borderRadius:5, fontFamily:'inherit', transition:'all .15s',
                  display:'flex', alignItems:'center', justifyContent:'center', gap:4 }}>
                {label}
                {id === 'finalizados' && finalizados.size > 0 && (
                  <span style={{ background:'var(--primary)', color:'#fff', fontSize:9, fontWeight:700, padding:'1px 5px', borderRadius:10, minWidth:14, textAlign:'center' }}>{finalizados.size}</span>
                )}
              </button>
            ))}
          </div>

          <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', marginBottom:8 }}>
            <span style={{ fontWeight:600, fontSize:12.5, color:'var(--fg-muted)' }}>
              {filtered.length} conversa{filtered.length !== 1 ? 's' : ''}
            </span>
            <button onClick={loadChats} title="Recarregar" style={{ background:'none', border:'none', cursor:'pointer', color:'var(--fg-muted)', padding:'3px 5px', borderRadius:6, display:'flex', alignItems:'center' }}>
              <IcoSvg name="arrow" size={13} />
            </button>
          </div>
          <div style={{ display:'flex', alignItems:'center', gap:8, background:'var(--bg-elevated)', borderRadius:8, padding:'7px 10px', border:'1px solid var(--border-subtle)' }}>
            <IcoSvg name="search" size={13} />
            <input value={search} onChange={e => setSearch(e.target.value)} placeholder="Buscar…"
              style={{ border:'none', background:'transparent', color:'var(--fg-primary)', fontSize:12.5, outline:'none', flex:1 }}
            />
            {search && <button onClick={() => setSearch('')} style={{ background:'none', border:'none', cursor:'pointer', color:'var(--fg-muted)', fontSize:14, padding:0, lineHeight:1 }}>×</button>}
          </div>
        </div>

        <div style={{ flex:1, minHeight:0, overflowY:'auto' }}>
          {loading && <div style={{ padding:28, textAlign:'center' }}><WaSpinner label="Carregando…" /></div>}
          {!loading && filtered.length === 0 && (
            <div style={{ padding:28, textAlign:'center', color:'var(--fg-muted)', fontSize:13 }}>
              {search ? 'Nenhum resultado.' : 'Sem conversas. Envie ou receba uma mensagem primeiro.'}
            </div>
          )}
          {filtered.map((c, i) => (
            <ChatItem
              key={c.id || c.remoteJid || `chat-${i}`}
              chat={c}
              selected={selected?.id === c.id}
              profilePic={profilePics[c.id]}
              nameOverride={resolveName(c)}
              onClick={() => selectChat(c)}
            />
          ))}
        </div>
      </div>

      {/* ── Chat ── */}
      <div style={{ flex:1, display:'flex', flexDirection:'column', minHeight:0, overflow:'hidden', background:'var(--bg-app)' }}>
        {!selected ? (
          <div style={{ flex:1, display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center', gap:12, color:'var(--fg-muted)' }}>
            <div style={{ width:64, height:64, borderRadius:16, background:'var(--bg-elevated)', display:'flex', alignItems:'center', justifyContent:'center', opacity:.4 }}>
              <IcoSvg name="msg" size={30} />
            </div>
            <div style={{ fontSize:14.5, fontWeight:600 }}>Selecione uma conversa</div>
            <div style={{ fontSize:12.5, opacity:.7 }}>{filtered.length} conversa{filtered.length !== 1 ? 's' : ''} disponível{filtered.length !== 1 ? 'is' : ''}</div>
          </div>
        ) : (
          <>
            {/* Header */}
            <div style={{ padding:'11px 18px', borderBottom:'1px solid var(--border-subtle)', display:'flex', alignItems:'center', gap:12, background:'var(--bg-surface)', flexShrink:0 }}>
              <WaAvatar name={selName} size={38} src={selPic} />
              <div style={{ flex:1, minWidth:0 }}>
                <div style={{ fontWeight:700, fontSize:14.5, overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap', color:'var(--fg-primary)', maxWidth:240 }}>
                  {selName}
                </div>
                <div style={{ fontSize:11.5, color:'var(--fg-muted)', marginTop:1, display:'flex', alignItems:'center', gap:5 }}>
                  <div style={{ width:6, height:6, borderRadius:'50%', background:'#22C55E', boxShadow:'0 0 5px #22C55E80' }}/>
                  {isGroup(selected) ? 'Grupo · WhatsApp' : 'WhatsApp · toque no nome para ver dados'}
                </div>
              </div>
              <div style={{ display:'flex', gap:6, flexShrink:0 }}>
                <button onClick={() => loadMessages(selected)} title="Atualizar" style={{ background:'none', border:'1px solid var(--border-subtle)', borderRadius:8, padding:'6px 10px', cursor:'pointer', color:'var(--fg-muted)', fontSize:12, display:'flex', alignItems:'center', gap:5, fontFamily:'inherit' }}>
                  <IcoSvg name="arrow" size={13} /> Atualizar
                </button>
                {isGroup(selected) ? (
                  <button
                    onClick={() => { setShowGroup(s => !s); setShowClient(false); }}
                    title="Ver membros do grupo"
                    style={{ background: showGroup ? 'var(--primary-soft,rgba(29,57,196,.1))' : 'none', border:`1px solid ${showGroup ? 'var(--primary)' : 'var(--border-subtle)'}`, borderRadius:8, padding:'6px 10px', cursor:'pointer', color: showGroup ? 'var(--primary)' : 'var(--fg-muted)', fontSize:12, display:'flex', alignItems:'center', gap:5, fontFamily:'inherit', fontWeight:600 }}>
                    👥 Membros
                  </button>
                ) : (
                  <button
                    onClick={() => { setShowClient(s => !s); setShowGroup(false); }}
                    title="Ver dados do cliente"
                    style={{ background: showClient ? 'var(--primary-soft,rgba(29,57,196,.1))' : 'none', border:`1px solid ${showClient ? 'var(--primary)':'var(--border-subtle)'}`, borderRadius:8, padding:'6px 10px', cursor:'pointer', color: showClient ? 'var(--primary)':'var(--fg-muted)', fontSize:12, display:'flex', alignItems:'center', gap:5, fontFamily:'inherit' }}>
                    👤 Cliente
                  </button>
                )}
                <button
                  onClick={() => toggleFinalizar(selected.id)}
                  title={finalizados.has(selected.id) ? 'Reabrir conversa' : 'Finalizar atendimento'}
                  style={{ background: finalizados.has(selected.id) ? 'rgba(54,207,201,.12)' : 'rgba(34,197,94,.1)', border:`1px solid ${finalizados.has(selected.id) ? '#36CFC940':'#22C55E40'}`, borderRadius:8, padding:'6px 10px', cursor:'pointer', color: finalizados.has(selected.id) ? '#36CFC9':'#22C55E', fontSize:12, display:'flex', alignItems:'center', gap:5, fontFamily:'inherit', fontWeight:600 }}>
                  <IcoSvg name="check" size={13} color={finalizados.has(selected.id) ? '#36CFC9':'#22C55E'} />
                  {finalizados.has(selected.id) ? 'Reabrir' : 'Finalizar'}
                </button>
              </div>
            </div>

            {/* Mensagens */}
            <div ref={msgListRef} style={{ flex:1, minHeight:0, overflowY:'auto', padding:'16px 20px', display:'flex', flexDirection:'column', gap:8 }}>
              {msgLoading && <div style={{ textAlign:'center', padding:24 }}><WaSpinner label="Carregando mensagens…" /></div>}
              {!msgLoading && msgError && (
                <div style={{ textAlign:'center', color:'var(--fg-muted)', fontSize:13, padding:24, display:'flex', flexDirection:'column', gap:8, alignItems:'center' }}>
                  <IcoSvg name="bell" size={20} color="var(--fg-muted)" />
                  {msgError}
                  <button onClick={() => loadMessages(selected)} style={{ background:'none', border:'1px solid var(--border-subtle)', borderRadius:7, padding:'6px 14px', cursor:'pointer', color:'var(--fg-muted)', fontSize:12, fontFamily:'inherit' }}>
                    Tentar novamente
                  </button>
                </div>
              )}
              {!msgLoading && !msgError && messages.length === 0 && (
                <div style={{ textAlign:'center', color:'var(--fg-muted)', fontSize:13, padding:24 }}>Nenhuma mensagem encontrada.</div>
              )}
              {messages.map((m, i) => (
                <MsgBubble key={`${m.key?.id || m.messageTimestamp || ''}-${i}`} msg={m} isMe={!!m.key?.fromMe}
                  senderName={!m.key?.fromMe && isGroup(selected) ? (m.pushName || '') : null}
                  instanceName={instanceName}
                />
              ))}
            </div>

            {/* Compose */}
            <div style={{ padding:'10px 14px', borderTop:'1px solid var(--border-subtle)', background:'var(--bg-surface)', flexShrink:0 }}>
              {sendError && (
                <div style={{ display:'flex', alignItems:'center', gap:8, background:'#f443360f', border:'1px solid #f4433630', borderRadius:8, padding:'8px 12px', marginBottom:8, fontSize:12.5, color:'#f44336' }}>
                  <IcoSvg name="bell" size={13} color="#f44336" />
                  {sendError}
                  <button onClick={() => setSendError('')} style={{ background:'none', border:'none', cursor:'pointer', color:'#f44336', marginLeft:'auto', padding:'0 2px', fontSize:16, lineHeight:1 }}>×</button>
                </div>
              )}
              {/* Input de arquivo oculto */}
              <input ref={mediaFileRef} type="file" style={{ display:'none' }}
                accept="image/*,audio/*,video/*,.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.zip,.txt"
                onChange={e => { const f = e.target.files?.[0]; if (f) sendMedia(f); e.target.value = ''; }}
              />
              {isLidChat(selected) ? (
                <div style={{ display:'flex', alignItems:'center', gap:8, background:'var(--bg-elevated)', borderRadius:12, padding:'12px 16px', border:'1px solid var(--border-subtle)', color:'var(--fg-muted)', fontSize:13 }}>
                  <span style={{ fontSize:15 }}>🔒</span>
                  Contato com identificador criptografado — não é possível responder por aqui.
                </div>
              ) : (
                <div style={{ display:'flex', gap:8, alignItems:'flex-end', background:'var(--bg-elevated)', borderRadius:12, padding:'8px 12px', border:'1px solid var(--border-subtle)' }}>
                  {/* Botão de anexo */}
                  <button
                    onClick={() => mediaFileRef.current?.click()}
                    disabled={sending}
                    title="Enviar foto, vídeo, áudio ou documento"
                    style={{ width:34, height:34, borderRadius:8, border:'none', background:'transparent', cursor:'pointer', color:'var(--fg-muted)', display:'flex', alignItems:'center', justifyContent:'center', flexShrink:0, opacity:sending?.4:1, transition:'color .15s', fontSize:18, padding:0 }}
                    onMouseEnter={e => e.currentTarget.style.color='var(--primary)'}
                    onMouseLeave={e => e.currentTarget.style.color='var(--fg-muted)'}
                  >
                    📎
                  </button>
                  <textarea
                    ref={textareaRef}
                    value={newMsg}
                    onChange={e => setNewMsg(e.target.value)}
                    onKeyDown={e => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } }}
                    placeholder="Escreva uma mensagem…  (↵ enviar)"
                    rows={1}
                    style={{ flex:1, border:'none', background:'transparent', color:'var(--fg-primary)', fontSize:13.5, resize:'none', outline:'none', fontFamily:'inherit', lineHeight:1.55, maxHeight:120, overflowY:'auto', padding:0 }}
                  />
                  <button onClick={sendMessage} disabled={sending || !newMsg.trim()} style={{
                    width:36, height:36, borderRadius:9, background:'var(--primary)', border:'none',
                    color:'#fff', cursor:'pointer', display:'flex', alignItems:'center', justifyContent:'center',
                    flexShrink:0, opacity:(sending||!newMsg.trim())?.45:1, transition:'opacity .15s'
                  }}>
                    {sending
                      ? <div style={{ width:14, height:14, border:'2px solid rgba(255,255,255,.3)', borderTopColor:'#fff', borderRadius:'50%', animation:'_waspin .8s linear infinite' }} />
                      : <IcoSvg name="send" size={15} color="#fff" />
                    }
                  </button>
                </div>
              )}
            </div>
          </>
        )}
      </div>

      {/* ── Painel de cliente ── */}
      {selected && showClient && !isGroup(selected) && (
        <WaClientPanel chat={selected} resolvedPhone={getPhoneFromChat(selected)} onClose={() => setShowClient(false)} />
      )}

      {/* ── Painel de grupo ── */}
      {selected && showGroup && isGroup(selected) && (
        <GroupPanel chat={selected} instanceName={instanceName} contacts={contacts} onClose={() => setShowGroup(false)} />
      )}
    </div>
  );
}

// ─── Enviar ────────────────────────────────────────────────────
function EnviarTab({ instanceName }) {
  const [phone,   setPhone]   = React.useState('');
  const [msg,     setMsg]     = React.useState('');
  const [sending, setSending] = React.useState(false);
  const [ok,      setOk]      = React.useState(false);
  const [clients, setClients] = React.useState([]);

  React.useEffect(() => {
    const db = window._supabase || window.sb;
    db?.from('clients').select('id,name,phone').order('name').then(({ data }) => { if (data) setClients(data); });
  }, []);

  async function send() {
    if (!phone || !msg.trim()) return;
    setSending(true); setOk(false);
    try {
      await evoPost(`/message/sendText/${instanceName}`, { number: fmtPhone(phone), text: msg.trim() });
      setOk(true); setMsg('');
      setTimeout(() => setOk(false), 3500);
    } catch (e) { alert('Erro: ' + e.message); }
    setSending(false);
  }

  return (
    <div style={{ padding:'32px', maxWidth:560 }}>
      <div style={{ fontSize:17, fontWeight:700, marginBottom:4 }}>Enviar Mensagem</div>
      <div style={{ fontSize:13, color:'var(--fg-muted)', marginBottom:28 }}>Envio avulso para qualquer número WhatsApp.</div>

      {clients.length > 0 && (
        <WaField label="Cliente" style={{ marginBottom:18 }}>
          <select onChange={e => { const c = clients.find(x => x.id === e.target.value); if (c?.phone) setPhone(c.phone); }} style={WaS.input}>
            <option value="">— selecione um cliente —</option>
            {clients.map(c => <option key={c.id} value={c.id}>{c.name}</option>)}
          </select>
        </WaField>
      )}

      <WaField label="Número WhatsApp" style={{ marginBottom:18 }}>
        <input value={phone} onChange={e => setPhone(e.target.value)} placeholder="(11) 9 9999-9999" style={WaS.input} />
      </WaField>

      <WaField label="Mensagem" style={{ marginBottom:28 }}>
        <textarea value={msg} onChange={e => setMsg(e.target.value)} rows={5} placeholder="Digite sua mensagem…" style={{ ...WaS.input, resize:'vertical' }} />
      </WaField>

      <div style={{ display:'flex', gap:14, alignItems:'center' }}>
        <button onClick={send} disabled={sending || !phone || !msg.trim()} style={{ ...WaS.btnPrimary, display:'flex', alignItems:'center', gap:8, opacity:(sending||!phone||!msg.trim())?.5:1 }}>
          <IcoSvg name="send" size={14} color="#fff" />
          {sending ? 'Enviando…' : 'Enviar mensagem'}
        </button>
        {ok && (
          <span style={{ color:'#22C55E', fontWeight:600, fontSize:13, display:'flex', alignItems:'center', gap:6 }}>
            <IcoSvg name="check" size={14} color="#22C55E" /> Enviado!
          </span>
        )}
      </div>
    </div>
  );
}

// ─── Automações ────────────────────────────────────────────────
const AUTO_EVENTS = [
  { key:'orcamento_criado',   label:'Orçamento Criado',   icon:'document', color:'#36CFC9', default:'Olá {cliente}! Seu orçamento *{num}* está pronto.\n{link}' },
  { key:'orcamento_aprovado', label:'Orçamento Aprovado', icon:'check',    color:'#22C55E', default:'Perfeito {cliente}! Orçamento *{num}* aprovado. Vamos começar!' },
  { key:'projeto_criado',     label:'Projeto Iniciado',   icon:'folder',   color:'#3A86FF', default:'Oi {cliente}! O projeto *{projeto}* foi iniciado. Prazo: {prazo}.' },
  { key:'tarefa_concluida',   label:'Tarefa Concluída',   icon:'check',    color:'#8338EC', default:'Atualização: a tarefa *{tarefa}* foi concluída!' },
  { key:'pagamento_recebido', label:'Pagamento Recebido', icon:'dollar',   color:'#FADB14', default:'Recebemos seu pagamento de *R$ {valor}*. Obrigado!' },
  { key:'vencimento_proximo', label:'Vencimento Próximo', icon:'bell',     color:'#FA541C', default:'Lembrete: sua fatura de *R$ {valor}* vence em {dias} dias.' },
];

function AutomacoesTab() {
  const [cfg,     setCfg]     = React.useState(getAutoConfig());
  const [editing, setEditing] = React.useState(null);
  const [editTpl, setEditTpl] = React.useState('');

  function toggle(key) {
    const n = { ...cfg, [key]: { ...cfg[key], enabled: !cfg[key]?.enabled } };
    setCfg(n); localStorage.setItem(AUTO_STORAGE_KEY, JSON.stringify(n));
  }
  function saveEdit() {
    const n = { ...cfg, [editing]: { ...cfg[editing], template: editTpl } };
    setCfg(n); localStorage.setItem(AUTO_STORAGE_KEY, JSON.stringify(n));
    setEditing(null);
  }
  const activeCount = AUTO_EVENTS.filter(e => cfg[e.key]?.enabled).length;

  return (
    <div style={{ padding:'28px 32px', maxWidth:660 }}>
      <div style={{ display:'flex', justifyContent:'space-between', alignItems:'flex-end', marginBottom:22 }}>
        <div>
          <div style={{ fontSize:17, fontWeight:700, marginBottom:3 }}>Automações de Mensagens</div>
          <div style={{ fontSize:13, color:'var(--fg-muted)' }}>Envio automático ao cliente quando eventos ocorrem no inBrivvo.</div>
        </div>
        {activeCount > 0 && (
          <div style={{ fontSize:12, color:'var(--primary)', fontWeight:600, background:'var(--primary-soft,rgba(29,57,196,.1))', padding:'4px 12px', borderRadius:20 }}>
            {activeCount} ativa{activeCount>1?'s':''}
          </div>
        )}
      </div>

      <div style={{ display:'flex', flexDirection:'column', gap:10 }}>
        {AUTO_EVENTS.map(ev => {
          const active = !!cfg[ev.key]?.enabled;
          const tpl    = cfg[ev.key]?.template || ev.default;
          const isEdit = editing === ev.key;
          return (
            <div key={ev.key} style={{ padding:'14px 16px', borderRadius:12, border:`1.5px solid ${active ? 'var(--primary)' : 'var(--border-subtle)'}`, background:'var(--bg-surface)', transition:'border-color .2s' }}>
              <div style={{ display:'flex', alignItems:'center', gap:12 }}>
                <div style={{ width:36, height:36, borderRadius:9, background:ev.color+'22', display:'flex', alignItems:'center', justifyContent:'center', flexShrink:0 }}>
                  <IcoSvg name={ev.icon} size={17} color={ev.color} />
                </div>
                <div style={{ flex:1, minWidth:0 }}>
                  <div style={{ fontWeight:600, fontSize:13.5 }}>{ev.label}</div>
                  {!isEdit && <div style={{ fontSize:12, color:'var(--fg-muted)', marginTop:2, overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap', maxWidth:380 }}>{tpl}</div>}
                </div>
                <div style={{ display:'flex', gap:6, alignItems:'center', flexShrink:0 }}>
                  <button onClick={() => { setEditing(isEdit ? null : ev.key); setEditTpl(tpl); }}
                    style={{ background:'none', border:'none', cursor:'pointer', color:'var(--fg-muted)', padding:'4px 6px', borderRadius:6, display:'flex', alignItems:'center' }} title="Editar">
                    <IcoSvg name="pencil" size={14} />
                  </button>
                  <WaToggle enabled={active} onChange={() => toggle(ev.key)} />
                </div>
              </div>
              {isEdit && (
                <div style={{ marginTop:14 }}>
                  <textarea value={editTpl} onChange={e => setEditTpl(e.target.value)} rows={3}
                    style={{ ...WaS.input, marginBottom:8, resize:'vertical' }}
                  />
                  <div style={{ fontSize:11, color:'var(--fg-muted)', marginBottom:10, lineHeight:1.8 }}>
                    Variáveis: {['{cliente}','{num}','{valor}','{link}','{projeto}','{tarefa}','{prazo}','{dias}'].map(v => (
                      <code key={v} style={{ background:'var(--bg-elevated)', padding:'1px 5px', borderRadius:4, marginRight:4, fontSize:11 }}>{v}</code>
                    ))}
                  </div>
                  <div style={{ display:'flex', gap:8 }}>
                    <button onClick={saveEdit} style={WaS.btnPrimary}>Salvar</button>
                    <button onClick={() => setEditing(null)} style={WaS.btnSecondary}>Cancelar</button>
                  </div>
                </div>
              )}
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ─── Config ────────────────────────────────────────────────────
function ConfigTab({ instanceName, onDisconnect }) {
  const [status, setStatus] = React.useState('checking');
  const [info,   setInfo]   = React.useState({});
  const cfg = getEvoCfg();

  React.useEffect(() => { checkStatus(); }, [instanceName]);

  async function checkStatus() {
    setStatus('checking');
    try {
      const d     = await evoGet(`/instance/connectionState/${instanceName}`);
      const state = d?.instance?.state || d?.state || '';
      setStatus(state === 'open' ? 'connected' : 'disconnected');
      setInfo({ name: d?.instance?.profileName || '', phone: d?.instance?.phone || '' });
    } catch { setStatus('disconnected'); setInfo({}); }
  }

  async function disconnect() {
    if (!confirm('Desconectar o WhatsApp? Será necessário escanear o QR novamente.')) return;
    try { await evoFetch('DELETE', `/instance/logout/${instanceName}`); setStatus('disconnected'); setInfo({}); onDisconnect?.(); }
    catch (e) { alert('Erro: ' + e.message); }
  }

  const SC = { connected:'#22C55E', disconnected:'#f44336', checking:'var(--fg-muted)' };
  const SL = { connected:'Conectado', disconnected:'Desconectado', checking:'Verificando…' };

  return (
    <div style={{ padding:'28px 32px', maxWidth:520 }}>
      <div style={{ fontSize:17, fontWeight:700, marginBottom:22 }}>Configurações WhatsApp</div>
      <div style={{ display:'flex', flexDirection:'column', gap:14 }}>
        <WaInfoCard label="Status">
          <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center' }}>
            <div style={{ display:'flex', alignItems:'center', gap:8 }}>
              <div style={{ width:8, height:8, borderRadius:'50%', background:SC[status], boxShadow:status==='connected'?`0 0 6px ${SC[status]}`:'' }} />
              <span style={{ fontWeight:600, fontSize:14, color:SC[status] }}>{SL[status]}</span>
            </div>
            <button onClick={checkStatus} style={{ ...WaS.btnSecondary, display:'flex', alignItems:'center', gap:6 }}>
              <IcoSvg name="arrow" size={13} /> Atualizar
            </button>
          </div>
          {(info.name || info.phone) && <div style={{ fontSize:13, color:'var(--fg-muted)', marginTop:8 }}>{info.name}{info.phone ? ` · ${info.phone}` : ''}</div>}
        </WaInfoCard>
        <WaInfoCard label="Instância">
          <code style={{ fontSize:12.5, color:'var(--fg-primary)', wordBreak:'break-all' }}>{instanceName}</code>
        </WaInfoCard>
        <WaInfoCard label="Servidor">
          <div style={{ fontSize:12.5, color:'var(--fg-muted)', wordBreak:'break-all' }}>{cfg.url}</div>
        </WaInfoCard>
        {status === 'connected' && (
          <button onClick={disconnect} style={{ ...WaS.btnSecondary, color:'#f44336', borderColor:'#f4433644', marginTop:4, display:'flex', alignItems:'center', gap:8 }}>
            <IcoSvg name="logout" size={14} color="#f44336" /> Desconectar WhatsApp
          </button>
        )}
      </div>
    </div>
  );
}

// ─── Mensagens Internas (Supabase Realtime) ────────────────────
function InternalMessages({ user }) {
  const db      = window._supabase || window.sb;
  const myName  = user?.user_metadata?.full_name || user?.user_metadata?.name
               || (user?.email ? user.email.split('@')[0] : 'Você');
  const myEmail = user?.email  || '';
  const myId    = user?.id     || null;

  const [teamChs,    setTeamChs]    = React.useState([]);
  const [projects,   setProjects]   = React.useState([]);
  const [clients,    setClients]    = React.useState([]);
  const [projChMap,  setProjChMap]  = React.useState({});
  const [cliChMap,   setCliChMap]   = React.useState({});
  const [selected,   setSelected]   = React.useState(null);
  const [messages,   setMessages]   = React.useState([]);
  const [msgLoading, setMsgLoading] = React.useState(false);
  const [newMsg,     setNewMsg]     = React.useState('');
  const [sending,    setSending]    = React.useState(false);
  const [showCtx,    setShowCtx]    = React.useState(false);
  const [ctxData,    setCtxData]    = React.useState(null);

  const msgListRef  = React.useRef(null);
  const inputRef    = React.useRef(null);
  const realtimeRef = React.useRef(null);

  React.useEffect(() => {
    loadInitialData();
    return () => { if (realtimeRef.current) db?.removeChannel(realtimeRef.current); };
  }, []);

  React.useEffect(() => {
    if (msgListRef.current) msgListRef.current.scrollTop = msgListRef.current.scrollHeight;
  }, [messages]);

  React.useEffect(() => {
    if (!selected?.id) return;
    setMessages([]); setCtxData(null); setShowCtx(false);
    loadMessages(selected.id);
    subscribeChannel(selected.id);
    if (selected.project_id) loadProjectCtx(selected.project_id);
    if (selected.client_id)  loadClientCtx(selected.client_id);
    return () => {
      if (realtimeRef.current) { db?.removeChannel(realtimeRef.current); realtimeRef.current = null; }
    };
  }, [selected?.id]);

  async function loadInitialData() {
    if (!db) return;
    try {
      const { data } = await db.from('internal_channels').select('*').eq('type','team').eq('archived',false).order('created_at');
      const list = data || [];
      setTeamChs(list);
      if (list.length > 0) setSelected(list[0]);
    } catch {}
    try {
      const { data } = await db.from('projects').select('id,name,status,deadline,progress').order('created_at',{ascending:false}).limit(25);
      setProjects(data || []);
    } catch {}
    try {
      const { data } = await db.from('clients').select('id,name,company,phone,email').order('name').limit(25);
      setClients(data || []);
    } catch {}
    // Mapas de canais já criados (project_id → channelId, client_id → channelId)
    try {
      const { data } = await db.from('internal_channels').select('id,type,project_id,client_id').neq('type','team');
      const pc = {}, cc = {};
      (data||[]).forEach(ch => {
        if (ch.project_id) pc[ch.project_id] = ch.id;
        if (ch.client_id)  cc[ch.client_id]  = ch.id;
      });
      setProjChMap(pc); setCliChMap(cc);
    } catch {}
  }

  async function loadMessages(channelId) {
    if (!db) return;
    setMsgLoading(true);
    try {
      const { data } = await db.from('internal_messages').select('*')
        .eq('channel_id', channelId).eq('deleted', false)
        .order('created_at', { ascending: true }).limit(80);
      setMessages(data || []);
    } catch { setMessages([]); }
    setMsgLoading(false);
  }

  function subscribeChannel(channelId) {
    if (!db) return;
    if (realtimeRef.current) db.removeChannel(realtimeRef.current);
    const ch = db.channel(`int_msgs_${channelId}`)
      .on('postgres_changes', { event:'INSERT', schema:'public', table:'internal_messages', filter:`channel_id=eq.${channelId}` },
        payload => setMessages(prev => prev.find(m => m.id === payload.new.id) ? prev : [...prev, payload.new])
      ).subscribe();
    realtimeRef.current = ch;
  }

  async function openOrCreateChannel(type, refId, name, emoji, extra) {
    if (!db) return;
    const map    = type === 'project' ? projChMap : cliChMap;
    const setMap = type === 'project' ? setProjChMap : setCliChMap;
    let chId = map[refId];
    if (!chId) {
      try {
        const { data } = await db.from('internal_channels').insert({ type, name, emoji, ...extra }).select('*').single();
        chId = data.id;
        setMap(prev => ({ ...prev, [refId]: chId }));
        setSelected(data);
      } catch {}
    } else {
      try {
        const { data } = await db.from('internal_channels').select('*').eq('id', chId).single();
        setSelected(data);
      } catch {}
    }
  }

  async function loadProjectCtx(projectId) {
    if (!db) return;
    try { const { data } = await db.from('projects').select('*').eq('id', projectId).single(); setCtxData({ type:'project', ...data }); } catch {}
  }

  async function loadClientCtx(clientId) {
    if (!db) return;
    try { const { data } = await db.from('clients').select('*').eq('id', clientId).single(); setCtxData({ type:'client', ...data }); } catch {}
  }

  async function sendMessage() {
    if (!selected || !newMsg.trim() || !db) return;
    const content = newMsg.trim();
    setNewMsg('');
    setSending(true);
    try {
      await db.from('internal_messages').insert({
        channel_id: selected.id, user_id: myId, user_name: myName, user_email: myEmail, content,
      });
      setTimeout(() => loadMessages(selected.id), 500);
    } catch { setNewMsg(content); }
    setSending(false);
    inputRef.current?.focus();
  }

  // Agrupa mensagens consecutivas do mesmo usuário (≤5 min entre elas)
  function groupMsgs(msgs) {
    return msgs.map((m, i) => {
      const prev = msgs[i-1];
      const sameUser = prev && (prev.user_id === m.user_id || prev.user_email === m.user_email) && prev.user_name === m.user_name;
      const near     = prev && (new Date(m.created_at) - new Date(prev.created_at)) < 300000;
      return { ...m, grouped: !!(sameUser && near) };
    });
  }

  function fmtMsgTime(ts) {
    if (!ts) return '';
    const d = new Date(ts), now = new Date();
    if (d.toDateString() === now.toDateString())
      return d.toLocaleTimeString('pt-BR', { hour:'2-digit', minute:'2-digit' });
    return d.toLocaleDateString('pt-BR', { day:'2-digit', month:'2-digit' })
         + ' ' + d.toLocaleTimeString('pt-BR', { hour:'2-digit', minute:'2-digit' });
  }

  function initColor(str = '') {
    let h = 0; for (let i = 0; i < str.length; i++) h = (h * 31 + str.charCodeAt(i)) >>> 0;
    return ['#5B8DEF','#9B5DE5','#F15BB5','#00BBF9','#00C9A7','#FF6B6B','#FFA94D','#3A86FF'][h % 8];
  }

  const grouped = groupMsgs(messages);
  const selName = selected?.name || '';
  const PS_COLORS = { andamento:'#3A86FF', ativo:'#3A86FF', revisao:'#FADB14', pausa:'#36CFC9', concluido:'var(--fg-muted)', quase:'#22C55E' };

  return (
    <div style={{ display:'flex', flex:1, minHeight:0, overflow:'hidden' }}>

      {/* ── Sidebar de canais ── */}
      <div style={{ width:236, display:'flex', flexDirection:'column', borderRight:'1px solid var(--border-subtle)', background:'var(--bg-surface)', flexShrink:0, overflow:'hidden' }}>
        <div style={{ flex:1, minHeight:0, overflowY:'auto', padding:'8px 0 16px' }}>

          {/* Equipe */}
          <div style={{ padding:'8px 14px 4px', fontSize:10, fontWeight:700, color:'var(--fg-muted)', textTransform:'uppercase', letterSpacing:'.08em' }}>Equipe</div>
          {teamChs.map(ch => {
            const isActive = selected?.id === ch.id;
            return (
              <div key={ch.id} onClick={() => setSelected(ch)} style={{ display:'flex', alignItems:'center', gap:8, padding:'6px 14px', cursor:'pointer', borderRadius:6, margin:'1px 6px', background: isActive ? 'var(--primary-soft,rgba(29,57,196,.1))' : 'transparent', color: isActive ? 'var(--primary)' : 'var(--fg-secondary,var(--fg-primary))', fontWeight: isActive ? 700 : 400, fontSize:13.5, transition:'all .12s', userSelect:'none' }}>
                <span style={{ fontSize:14, flexShrink:0 }}>{ch.emoji || '#'}</span>
                <span style={{ overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>{ch.name}</span>
              </div>
            );
          })}

          {/* Projetos */}
          {projects.length > 0 && (
            <>
              <div style={{ padding:'14px 14px 4px', fontSize:10, fontWeight:700, color:'var(--fg-muted)', textTransform:'uppercase', letterSpacing:'.08em' }}>Projetos</div>
              {projects.map(p => {
                const isActive = selected?.project_id === p.id;
                return (
                  <div key={p.id} onClick={() => openOrCreateChannel('project', p.id, p.name, '📁', { project_id:p.id, description:`Chat do projeto ${p.name}` })} style={{ display:'flex', alignItems:'center', gap:8, padding:'6px 14px', cursor:'pointer', borderRadius:6, margin:'1px 6px', background: isActive ? 'var(--primary-soft,rgba(29,57,196,.1))' : 'transparent', color: isActive ? 'var(--primary)' : 'var(--fg-secondary,var(--fg-primary))', fontWeight: isActive ? 700 : 400, fontSize:13, transition:'all .12s', userSelect:'none' }}>
                    <span style={{ fontSize:13, flexShrink:0 }}>📁</span>
                    <span style={{ overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap', flex:1 }}>{p.name}</span>
                    {projChMap[p.id] && <div style={{ width:6, height:6, borderRadius:'50%', background:'var(--primary)', flexShrink:0, opacity:.55 }} />}
                  </div>
                );
              })}
            </>
          )}

          {/* Clientes */}
          {clients.length > 0 && (
            <>
              <div style={{ padding:'14px 14px 4px', fontSize:10, fontWeight:700, color:'var(--fg-muted)', textTransform:'uppercase', letterSpacing:'.08em' }}>Clientes</div>
              {clients.map(c => {
                const isActive = selected?.client_id === c.id;
                return (
                  <div key={c.id} onClick={() => openOrCreateChannel('client', c.id, c.name, '👤', { client_id:c.id, description:`Canal de comunicação · ${c.name}` })} style={{ display:'flex', alignItems:'center', gap:8, padding:'6px 14px', cursor:'pointer', borderRadius:6, margin:'1px 6px', background: isActive ? 'var(--primary-soft,rgba(29,57,196,.1))' : 'transparent', color: isActive ? 'var(--primary)' : 'var(--fg-secondary,var(--fg-primary))', fontWeight: isActive ? 700 : 400, fontSize:13, transition:'all .12s', userSelect:'none' }}>
                    <span style={{ fontSize:13, flexShrink:0 }}>👤</span>
                    <span style={{ overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap', flex:1 }}>{c.name}</span>
                  </div>
                );
              })}
            </>
          )}
        </div>
      </div>

      {/* ── Thread principal ── */}
      <div style={{ flex:1, display:'flex', flexDirection:'column', minHeight:0, overflow:'hidden' }}>
        {!selected ? (
          <div style={{ flex:1, display:'flex', alignItems:'center', justifyContent:'center', color:'var(--fg-muted)', fontSize:14, flexDirection:'column', gap:10 }}>
            <span style={{ fontSize:40, opacity:.3 }}>💬</span>
            <span>Selecione um canal para começar</span>
          </div>
        ) : (
          <>
            {/* Header */}
            <div style={{ padding:'12px 18px', borderBottom:'1px solid var(--border-subtle)', display:'flex', alignItems:'center', gap:10, background:'var(--bg-surface)', flexShrink:0 }}>
              <span style={{ fontSize:20 }}>{selected.emoji || '💬'}</span>
              <div style={{ flex:1, minWidth:0 }}>
                <div style={{ fontWeight:700, fontSize:14.5, overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>{selName}</div>
                {selected.description && <div style={{ fontSize:11.5, color:'var(--fg-muted)', marginTop:1 }}>{selected.description}</div>}
              </div>
              {(selected.project_id || selected.client_id) && (
                <button onClick={() => setShowCtx(s => !s)} style={{ background: showCtx ? 'var(--primary-soft,rgba(29,57,196,.1))' : 'none', border:'1px solid var(--border-subtle)', borderRadius:8, padding:'5px 11px', cursor:'pointer', color: showCtx ? 'var(--primary)' : 'var(--fg-muted)', fontSize:11.5, fontFamily:'inherit', display:'flex', alignItems:'center', gap:5, fontWeight:600, transition:'all .15s' }}>
                  <IcoSvg name="info" size={13} color={showCtx ? 'var(--primary)' : 'var(--fg-muted)'} />
                  Detalhes
                </button>
              )}
            </div>

            {/* Body row */}
            <div style={{ flex:1, display:'flex', minHeight:0, overflow:'hidden' }}>

              {/* Mensagens */}
              <div ref={msgListRef} style={{ flex:1, minHeight:0, overflowY:'auto', padding:'20px 22px', display:'flex', flexDirection:'column' }}>
                {msgLoading && (
                  <div style={{ textAlign:'center', padding:28 }}><WaSpinner label="Carregando…" /></div>
                )}
                {!msgLoading && messages.length === 0 && (
                  <div style={{ flex:1, display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center', color:'var(--fg-muted)', gap:10, paddingTop:60 }}>
                    <span style={{ fontSize:40, opacity:.3 }}>{selected.emoji || '💬'}</span>
                    <div style={{ fontSize:14.5, fontWeight:600 }}>Início do canal <strong>{selName}</strong></div>
                    <div style={{ fontSize:12.5, opacity:.7 }}>Seja o primeiro a enviar uma mensagem.</div>
                  </div>
                )}
                {grouped.map(m => {
                  const isMe      = m.user_email === myEmail || (myId && m.user_id === myId);
                  const initials  = (m.user_name || '?').split(' ').map(w => w[0]).join('').slice(0, 2).toUpperCase();
                  const color     = initColor(m.user_name || '');
                  return (
                    <div key={m.id} style={{ display:'flex', gap:10, marginBottom: m.grouped ? 1 : 10, alignItems:'flex-start', flexDirection: isMe ? 'row-reverse' : 'row' }}>
                      {!m.grouped
                        ? <div style={{ width:32, height:32, borderRadius:'50%', background:color, display:'flex', alignItems:'center', justifyContent:'center', fontSize:11, fontWeight:700, color:'#fff', flexShrink:0, marginTop:2 }}>{initials}</div>
                        : <div style={{ width:32, flexShrink:0 }} />
                      }
                      <div style={{ maxWidth:'70%', display:'flex', flexDirection:'column', alignItems: isMe ? 'flex-end' : 'flex-start' }}>
                        {!m.grouped && (
                          <div style={{ fontSize:11, color:'var(--fg-muted)', marginBottom:3, display:'flex', gap:8, alignItems:'baseline', flexDirection: isMe ? 'row-reverse' : 'row' }}>
                            <span style={{ fontWeight:700, color:'var(--fg-secondary,var(--fg-primary))' }}>{isMe ? 'Você' : m.user_name}</span>
                            <span>{fmtMsgTime(m.created_at)}</span>
                          </div>
                        )}
                        <div style={{ background: isMe ? 'var(--primary)' : 'var(--bg-elevated)', color: isMe ? '#fff' : 'var(--fg-primary)', borderRadius: isMe ? '16px 4px 16px 16px' : '4px 16px 16px 16px', padding:'9px 13px', fontSize:13.5, lineHeight:1.55, wordBreak:'break-word', border: isMe ? 'none' : '1px solid var(--border-subtle)', maxWidth:'100%' }}>
                          {m.content}
                        </div>
                      </div>
                    </div>
                  );
                })}
              </div>

              {/* Painel de contexto (projeto/cliente) */}
              {showCtx && ctxData && (
                <div style={{ width:260, borderLeft:'1px solid var(--border-subtle)', background:'var(--bg-surface)', flexShrink:0, overflow:'hidden', display:'flex', flexDirection:'column' }}>
                  <div style={{ padding:'12px 14px', borderBottom:'1px solid var(--border-subtle)', display:'flex', justifyContent:'space-between', alignItems:'center', flexShrink:0 }}>
                    <span style={{ fontSize:10, fontWeight:700, textTransform:'uppercase', letterSpacing:'.07em', color:'var(--fg-muted)' }}>
                      {ctxData.type === 'project' ? 'Projeto' : 'Cliente'}
                    </span>
                    <button onClick={() => setShowCtx(false)} style={{ background:'none', border:'none', cursor:'pointer', color:'var(--fg-muted)', fontSize:18, lineHeight:1, padding:'0 4px', fontFamily:'inherit' }}>×</button>
                  </div>
                  <div style={{ flex:1, overflowY:'auto', padding:16 }}>
                    {ctxData.type === 'project' ? (
                      <div style={{ display:'flex', flexDirection:'column', gap:12 }}>
                        <div style={{ fontWeight:700, fontSize:14 }}>{ctxData.name}</div>
                        {ctxData.status && (
                          <div style={{ display:'flex', gap:7, alignItems:'center' }}>
                            <div style={{ width:8, height:8, borderRadius:'50%', background: PS_COLORS[ctxData.status] || 'var(--fg-muted)', flexShrink:0 }} />
                            <span style={{ fontSize:12, color:'var(--fg-muted)', textTransform:'capitalize' }}>{ctxData.status}</span>
                          </div>
                        )}
                        {ctxData.deadline && (
                          <div style={{ fontSize:12, color:'var(--fg-muted)' }}>
                            📅 Prazo: {new Date(ctxData.deadline + 'T00:00:00').toLocaleDateString('pt-BR')}
                          </div>
                        )}
                        {ctxData.progress > 0 && (
                          <div>
                            <div style={{ fontSize:11, color:'var(--fg-muted)', marginBottom:5 }}>Progresso</div>
                            <div style={{ height:6, borderRadius:3, background:'var(--bg-elevated)', overflow:'hidden', border:'1px solid var(--border-subtle)' }}>
                              <div style={{ height:'100%', width:`${ctxData.progress}%`, background:'var(--primary)', borderRadius:3, transition:'width .4s' }} />
                            </div>
                            <div style={{ fontSize:11, color:'var(--fg-muted)', marginTop:4 }}>{ctxData.progress}%</div>
                          </div>
                        )}
                        {ctxData.budget > 0 && (
                          <div style={{ fontSize:12, color:'var(--fg-muted)' }}>
                            💰 Budget: R$ {Number(ctxData.budget).toLocaleString('pt-BR')}
                          </div>
                        )}
                        <button onClick={() => window.__navigateTo?.('projects')} style={{ ...WaS.btnSecondary, fontSize:11.5, padding:'7px 12px', display:'flex', alignItems:'center', gap:5 }}>
                          <IcoSvg name="folder" size={11} /> Ver projeto
                        </button>
                      </div>
                    ) : (
                      <div style={{ display:'flex', flexDirection:'column', gap:10 }}>
                        <WaAvatar name={ctxData.name} size={44} style={{ marginBottom:4 }} />
                        <div style={{ fontWeight:700, fontSize:14 }}>{ctxData.name}</div>
                        {ctxData.company && <div style={{ fontSize:12, color:'var(--fg-muted)' }}>🏢 {ctxData.company}</div>}
                        {ctxData.email && <div style={{ fontSize:12, color:'var(--fg-muted)' }}>✉️ {ctxData.email}</div>}
                        {ctxData.phone && <div style={{ fontSize:12, color:'var(--fg-muted)' }}>📱 {ctxData.phone}</div>}
                        {ctxData.segment && <div style={{ fontSize:12, color:'var(--fg-muted)' }}>🏷️ {ctxData.segment}</div>}
                        <button onClick={() => window.__navigateTo?.('clients')} style={{ ...WaS.btnSecondary, fontSize:11.5, padding:'7px 12px', display:'flex', alignItems:'center', gap:5, marginTop:4 }}>
                          <IcoSvg name="users" size={11} /> Ver cliente
                        </button>
                      </div>
                    )}
                  </div>
                </div>
              )}
            </div>

            {/* Compose */}
            <div style={{ padding:'10px 14px', borderTop:'1px solid var(--border-subtle)', background:'var(--bg-surface)', flexShrink:0 }}>
              <div style={{ display:'flex', gap:10, alignItems:'flex-end', background:'var(--bg-elevated)', borderRadius:12, padding:'8px 12px', border:'1px solid var(--border-subtle)' }}>
                <textarea
                  ref={inputRef}
                  value={newMsg}
                  onChange={e => setNewMsg(e.target.value)}
                  onKeyDown={e => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } }}
                  placeholder={`Mensagem em ${selected.emoji || '#'}${selName}…  (↵ enviar)`}
                  rows={1}
                  style={{ flex:1, border:'none', background:'transparent', color:'var(--fg-primary)', fontSize:13.5, resize:'none', outline:'none', fontFamily:'inherit', lineHeight:1.55, maxHeight:120, overflowY:'auto', padding:0 }}
                />
                <button onClick={sendMessage} disabled={sending || !newMsg.trim()} style={{ width:36, height:36, borderRadius:9, background:'var(--primary)', border:'none', color:'#fff', cursor:'pointer', display:'flex', alignItems:'center', justifyContent:'center', flexShrink:0, opacity:(sending || !newMsg.trim()) ? .45 : 1, transition:'opacity .15s' }}>
                  <IcoSvg name="send" size={15} color="#fff" />
                </button>
              </div>
            </div>
          </>
        )}
      </div>
    </div>
  );
}

// ─── Componente Principal ──────────────────────────────────────
const MSG_TABS = [
  { id:'conectar',   label:'Conectar',   icon:'link',     always:true  },
  { id:'conversas',  label:'Conversas',  icon:'msg',      always:false },
  { id:'enviar',     label:'Enviar',     icon:'send',     always:false },
  { id:'automacoes', label:'Automações', icon:'bolt',     always:true  },
  { id:'config',     label:'Config',     icon:'settings', always:true  },
];

function Messages({ user }) {
  const [source,     setSource]     = React.useState('interno'); // 'interno' | 'whatsapp'
  const [tab,        setTab]        = React.useState('conectar');
  const [connected,  setConnected]  = React.useState(false);
  const [instName]                  = React.useState(() => getInstanceName(user?.id || 'demo'));
  const cfg = getEvoCfg();

  React.useEffect(() => {
    if (!cfg.url || !cfg.key) return;
    evoGet(`/instance/connectionState/${instName}`)
      .then(d => { const s = d?.instance?.state || d?.state || ''; if (s === 'open') { setConnected(true); setTab('conversas'); } })
      .catch(() => {});
  }, []);

  const tabs = MSG_TABS.filter(t => t.always || connected);

  return (
    <div style={{ display:'flex', flexDirection:'column', flex:1, minHeight:0, overflow:'hidden' }}>

      {/* ── Barra de fonte: Interno / WhatsApp ── */}
      <div style={{ display:'flex', alignItems:'stretch', padding:'0 20px', borderBottom:'1px solid var(--border-subtle)', background:'var(--bg-surface)', flexShrink:0, overflowX:'auto' }}>
        {[['interno','💬 Interno'], ['whatsapp','📱 WhatsApp']].map(([id, label]) => (
          <button key={id} onClick={() => setSource(id)} style={{ background:'none', border:'none', padding:'13px 18px 12px', cursor:'pointer', fontSize:13.5, fontFamily:'inherit', fontWeight: source === id ? 700 : 400, color: source === id ? 'var(--primary)' : 'var(--fg-muted)', borderBottom: source === id ? '2.5px solid var(--primary)' : '2.5px solid transparent', whiteSpace:'nowrap', transition:'color .15s', marginRight:2 }}>
            {label}
          </button>
        ))}
        {/* Sub-tabs do WhatsApp, visíveis apenas quando WhatsApp está ativo */}
        {source === 'whatsapp' && (
          <div style={{ display:'flex', gap:0, borderLeft:'1px solid var(--border-subtle)', marginLeft:6, paddingLeft:6 }}>
            {tabs.map(t => (
              <button key={t.id} onClick={() => setTab(t.id)} style={{ background:'none', border:'none', padding:'13px 13px 12px', cursor:'pointer', fontSize:12.5, display:'flex', alignItems:'center', gap:6, fontFamily:'inherit', fontWeight: tab === t.id ? 700 : 400, color: tab === t.id ? 'var(--primary)' : 'var(--fg-muted)', borderBottom: tab === t.id ? '2.5px solid var(--primary)' : '2.5px solid transparent', whiteSpace:'nowrap', transition:'color .15s' }}>
                <IcoSvg name={t.icon} size={13} color={tab === t.id ? 'var(--primary)' : 'var(--fg-muted)'} />
                {t.label}
              </button>
            ))}
          </div>
        )}
      </div>

      {/* ── Conteúdo ── */}
      <div style={{ flex:1, minHeight:0, overflow:'hidden', display:'flex', flexDirection:'column' }}>
        {source === 'interno' && <InternalMessages user={user} />}
        {source === 'whatsapp' && (
          <>
            {(!cfg.url || !cfg.key) ? (
              <div style={{ padding:40, flex:1, overflowY:'auto' }}>
                <div style={{ maxWidth:480, margin:'0 auto' }}>
                  <div style={{ fontSize:20, fontWeight:700, marginBottom:8 }}>Módulo WhatsApp</div>
                  <div style={{ color:'var(--fg-muted)', marginBottom:24, fontSize:14 }}>Configure o servidor para ativar o WhatsApp no inBrivvo.</div>
                  <AdminSetupForm onSaved={() => window.location.reload()} />
                </div>
              </div>
            ) : (
              <>
                {tab === 'conectar'   && <QRConnectTab instanceName={instName} onConnected={() => { setConnected(true); setTimeout(() => setTab('conversas'), 1200); }} />}
                {tab === 'conversas'  && <ConversasTab instanceName={instName} />}
                {tab === 'enviar'     && <div style={{ flex:1, overflowY:'auto' }}><EnviarTab instanceName={instName} /></div>}
                {tab === 'automacoes' && <div style={{ flex:1, overflowY:'auto' }}><AutomacoesTab /></div>}
                {tab === 'config'     && <div style={{ flex:1, overflowY:'auto' }}><ConfigTab instanceName={instName} onDisconnect={() => { setConnected(false); setTab('conectar'); }} /></div>}
              </>
            )}
          </>
        )}
      </div>
    </div>
  );
}

// ─── Admin Setup Form ──────────────────────────────────────────
function AdminSetupForm({ onSaved }) {
  const [url,     setUrl]     = React.useState('');
  const [key,     setKey]     = React.useState('');
  const [testing, setTesting] = React.useState(false);
  const [testOk,  setTestOk]  = React.useState(null);

  async function testConn() {
    setTesting(true); setTestOk(null);
    try { const r = await fetch(url.replace(/\/$/,'') + '/instance/fetchInstances', { headers:{ apikey: key } }); setTestOk(r.ok); }
    catch { setTestOk(false); }
    setTesting(false);
  }
  function save() { localStorage.setItem(EVOAPI_STORAGE_KEY, JSON.stringify({ url: url.replace(/\/$/,''), key })); onSaved?.(); }

  return (
    <div style={{ display:'flex', flexDirection:'column', gap:16 }}>
      <WaField label="URL do Servidor (Railway)"><input value={url} onChange={e => setUrl(e.target.value)} placeholder="https://xxxx.railway.app" style={WaS.input} /></WaField>
      <WaField label="API Key"><input type="password" value={key} onChange={e => setKey(e.target.value)} placeholder="Chave de autenticação" style={WaS.input} /></WaField>
      <div style={{ display:'flex', gap:12, alignItems:'center', flexWrap:'wrap' }}>
        <button onClick={testConn} disabled={testing||!url||!key} style={WaS.btnSecondary}>{testing ? 'Testando…' : 'Testar conexão'}</button>
        <button onClick={save} disabled={!url||!key||testOk!==true} style={{ ...WaS.btnPrimary, opacity:(!url||!key||testOk!==true)?.5:1 }}>Salvar e Ativar</button>
        {testOk===true  && <span style={{ color:'#22C55E', fontWeight:600, fontSize:13, display:'flex', alignItems:'center', gap:5 }}><IcoSvg name="check" size={14} color="#22C55E"/> Conexão OK</span>}
        {testOk===false && <span style={{ color:'#f44',    fontWeight:600, fontSize:13 }}>Falhou — verifique URL e chave</span>}
      </div>
    </div>
  );
}

// ─── Micro-componentes ─────────────────────────────────────────
function WaSpinner({ label, color = 'var(--fg-muted)' }) {
  return (
    <div style={{ display:'flex', gap:8, alignItems:'center', color, fontSize:13 }}>
      <div style={{ width:15, height:15, border:'2px solid var(--border-subtle)', borderTopColor:color, borderRadius:'50%', animation:'_waspin .8s linear infinite', flexShrink:0 }} />
      {label}
    </div>
  );
}
function WaToggle({ enabled, onChange }) {
  return (
    <div onClick={onChange} style={{ width:40, height:22, borderRadius:11, background:enabled?'var(--primary)':'var(--bg-elevated)', cursor:'pointer', position:'relative', transition:'background .2s', border:'1px solid var(--border-subtle)', flexShrink:0 }}>
      <div style={{ position:'absolute', top:2, left:enabled?19:2, width:16, height:16, borderRadius:'50%', background:enabled?'#fff':'var(--fg-muted)', transition:'left .2s', boxShadow:'0 1px 3px rgba(0,0,0,.2)' }} />
    </div>
  );
}
function WaField({ label, children, style: extra }) {
  return (
    <div style={extra}>
      <label style={{ display:'block', fontSize:11, fontWeight:700, color:'var(--fg-muted)', textTransform:'uppercase', letterSpacing:'.06em', marginBottom:6 }}>{label}</label>
      {children}
    </div>
  );
}
function WaInfoCard({ label, children }) {
  return (
    <div style={{ padding:'14px 16px', borderRadius:10, border:'1px solid var(--border-subtle)', background:'var(--bg-surface)' }}>
      <div style={{ fontSize:10, fontWeight:700, color:'var(--fg-muted)', textTransform:'uppercase', letterSpacing:'.07em', marginBottom:8 }}>{label}</div>
      {children}
    </div>
  );
}

const WaS = {
  input:        { width:'100%', padding:'9px 12px', borderRadius:8, border:'1px solid var(--border-subtle)', background:'var(--bg-surface)', color:'var(--fg-primary)', fontSize:13.5, boxSizing:'border-box', outline:'none', fontFamily:'inherit' },
  btnPrimary:   { background:'var(--primary)', color:'#fff', border:'none', borderRadius:8, padding:'9px 22px', fontSize:13, fontWeight:600, cursor:'pointer', fontFamily:'inherit' },
  btnSecondary: { background:'none', border:'1px solid var(--border-subtle)', borderRadius:8, padding:'9px 18px', fontSize:12, color:'var(--fg-muted)', cursor:'pointer', fontFamily:'inherit' },
  sectionTitle: { fontSize:10, fontWeight:700, color:'var(--fg-muted)', textTransform:'uppercase', letterSpacing:'.07em', marginBottom:8 },
  miniCard:     { padding:'10px 11px', borderRadius:8, background:'var(--bg-elevated)', border:'1px solid var(--border-subtle)' },
};

// ─── Globais ───────────────────────────────────────────────────
window.__evoSend = async function(phone, message, instanceName) {
  const inst = instanceName || getInstanceName(window._supabaseUser?.id || 'demo');
  return evoPost(`/message/sendText/${inst}`, { number: fmtPhone(phone), text: message });
};
window.__evoAuto = async function(eventKey, vars = {}) {
  const cfg = getAutoConfig();
  const ev  = AUTO_EVENTS.find(e => e.key === eventKey);
  if (!ev || !cfg[eventKey]?.enabled) return;
  const text = (cfg[eventKey]?.template || ev.default).replace(/\{(\w+)\}/g, (_, k) => vars[k] || '');
  if (!vars.phone) return;
  return window.__evoSend(vars.phone, text);
};

window.Messages = Messages;
