/* Ensaio temporário: chamada real a partir do PC, transcrição sem guardar áudio. */

let ctEmailActual = '';

function ctEmail() {
  return ctEmailActual || window.currentUser?.email || '';
}

async function ctApi(url, options = {}) {
  const p = new URLSearchParams(window.location.search || '');
  const email = ctEmail();
  if (email && !p.get('email')) p.set('email', email);
  const full = url + (url.includes('?') ? '&' : '?') + p.toString();
  const res = await fetch(full, options.body instanceof FormData
    ? { ...options }
    : { headers: { 'Content-Type': 'application/json' }, ...options });
  const data = await res.json().catch(() => ({}));
  if (!res.ok) throw new Error(data.error || `HTTP ${res.status}`);
  return data;
}

function ctLoadTwilio() {
  if (window.Twilio && window.Twilio.Device) return Promise.resolve();
  return new Promise((resolve, reject) => {
    const s = document.createElement('script');
    s.src = '/js/vendor/twilio-voice.min.js?v=ct1';
    s.onload = () => (window.Twilio && window.Twilio.Device) ? resolve() : reject(new Error('Cliente de voz incompleto'));
    s.onerror = () => reject(new Error('Não foi possível carregar o cliente de voz'));
    document.head.appendChild(s);
  });
}

function ctWav(samples, sampleRate) {
  const n = samples.length;
  const buf = new ArrayBuffer(44 + n * 2);
  const v = new DataView(buf);
  const str = (o, s) => { for (let i = 0; i < s.length; i++) v.setUint8(o + i, s.charCodeAt(i)); };
  str(0, 'RIFF');
  v.setUint32(4, 36 + n * 2, true);
  str(8, 'WAVE');
  str(12, 'fmt ');
  v.setUint32(16, 16, true);
  v.setUint16(20, 1, true);
  v.setUint16(22, 1, true);
  v.setUint32(24, sampleRate, true);
  v.setUint32(28, sampleRate * 2, true);
  v.setUint16(32, 2, true);
  v.setUint16(34, 16, true);
  str(36, 'data');
  v.setUint32(40, n * 2, true);
  let o = 44;
  for (let i = 0; i < n; i++) {
    const s = Math.max(-1, Math.min(1, samples[i]));
    v.setInt16(o, s < 0 ? s * 0x8000 : s * 0x7fff, true);
    o += 2;
  }
  return new Blob([buf], { type: 'audio/wav' });
}

function ctDownsample(samples, from, to) {
  if (from === to) return samples;
  const ratio = from / to;
  const outLen = Math.floor(samples.length / ratio);
  const out = new Float32Array(outLen);
  for (let i = 0; i < outLen; i++) {
    const a = Math.floor(i * ratio);
    const b = Math.min(samples.length, Math.floor((i + 1) * ratio));
    let sum = 0;
    let n = 0;
    for (let j = a; j < b; j++) { sum += samples[j]; n++; }
    out[i] = n ? sum / n : 0;
  }
  return out;
}

function ctRms(samples) {
  let s = 0;
  for (let i = 0; i < samples.length; i++) s += samples[i] * samples[i];
  return Math.sqrt(s / Math.max(1, samples.length));
}

function ctCaptura(stream, onPcm) {
  const ctx = new AudioContext();
  const source = ctx.createMediaStreamSource(stream);
  const proc = ctx.createScriptProcessor(4096, 1, 1);
  const gain = ctx.createGain();
  gain.gain.value = 0;
  const partes = [];
  let total = 0;
  proc.onaudioprocess = (e) => {
    const data = e.inputBuffer.getChannelData(0);
    partes.push(new Float32Array(data));
    total += data.length;
    if (total >= ctx.sampleRate * 4) {
      const merged = new Float32Array(total);
      let o = 0;
      for (const p of partes) { merged.set(p, o); o += p.length; }
      partes.length = 0;
      total = 0;
      onPcm(merged, ctx.sampleRate);
    }
  };
  source.connect(proc);
  proc.connect(gain);
  gain.connect(ctx.destination);
  return {
    flush() {
      if (total < ctx.sampleRate * 0.6) { partes.length = 0; total = 0; return; }
      const merged = new Float32Array(total);
      let o = 0;
      for (const p of partes) { merged.set(p, o); o += p.length; }
      partes.length = 0;
      total = 0;
      onPcm(merged, ctx.sampleRate);
    },
    stop() {
      try { proc.disconnect(); source.disconnect(); gain.disconnect(); ctx.close(); } catch (e) {}
    },
  };
}

function ctDigiLer() {
  try { return JSON.parse(sessionStorage.getItem('ct-digi') || 'null'); } catch (e) { return null; }
}

function ctDigiGuardar(item) {
  try { sessionStorage.setItem('ct-digi', JSON.stringify(item)); } catch (e) {}
}

function ctDigiLimpar() {
  try { sessionStorage.removeItem('ct-digi'); } catch (e) {}
}

function ctFmtTel(t) {
  const n = String(t || '');
  if (n.startsWith('+351') && n.length === 13) return `+351 ${n.slice(4, 7)} ${n.slice(7, 10)} ${n.slice(10)}`;
  return n;
}

function ctBool(v) {
  if (v === true) return 'Sim';
  if (v === false) return 'Não';
  return '';
}

function ctValor(v) {
  if (v === true || v === false) return v;
  if (v === null || v === undefined) return null;
  const s = String(v).trim();
  return s ? s : null;
}

function ctEscolher(antes, depois, nome, changed) {
  const d = ctValor(depois);
  if (d === null) return ctValor(antes);
  if (ctValor(antes) !== d) changed.push(nome);
  return d;
}

function ctFundir(prev, next) {
  const p = prev || {};
  const n = next || {};
  const changed = [];
  const q = n.qualificacao || {};
  const pq = p.qualificacao || {};
  const pessoa = n.pessoa || {};
  const pp = p.pessoa || {};
  const bant = n.bant || {};
  const pb = p.bant || {};
  return {
    changed,
    merged: {
      resumo: n.resumo ? ctEscolher(p.resumo, n.resumo, 'Resumo', changed) : (p.resumo || ''),
      sugestoes: Array.isArray(n.sugestoes) && n.sugestoes.length ? n.sugestoes : (p.sugestoes || []),
      resumo_topicos: Array.isArray(n.resumo_topicos) && n.resumo_topicos.length ? n.resumo_topicos : (p.resumo_topicos || []),
      empresa: ctEscolher(p.empresa, n.empresa, 'Empresa', changed),
      bant: {
        budget: ctEscolher(pb.budget, bant.budget, 'Budget', changed),
        authority: ctEscolher(pb.authority, bant.authority, 'Authority', changed),
        need: ctEscolher(pb.need, bant.need, 'Need', changed),
        timeline: ctEscolher(pb.timeline, bant.timeline, 'Timeline', changed),
      },
      pessoa: {
        nome_completo: ctEscolher(pp.nome_completo, pessoa.nome_completo, 'Nome', changed),
        cargo: ctEscolher(pp.cargo, pessoa.cargo, 'Cargo', changed),
        email: ctEscolher(pp.email, pessoa.email, 'Email', changed),
        telemovel: ctEscolher(pp.telemovel, pessoa.telemovel, 'Telemóvel', changed),
      },
      qualificacao: {
        cliente_final: ctEscolher(pq.cliente_final, q.cliente_final, 'Cliente final', changed),
        aplicacao: ctEscolher(pq.aplicacao, q.aplicacao, 'Aplicação', changed),
        grafica_habitual: ctEscolher(pq.grafica_habitual, q.grafica_habitual, 'Gráfica habitual', changed),
        autorizacao_filmagem: ctEscolher(pq.autorizacao_filmagem, q.autorizacao_filmagem, 'Autorização para filmar', changed),
        proximo_passo: ctEscolher(pq.proximo_passo, q.proximo_passo, 'Próximo passo', changed),
        data_proximo_passo: ctEscolher(pq.data_proximo_passo, q.data_proximo_passo, 'Data', changed),
        nif: ctEscolher(pq.nif, q.nif, 'NIF', changed),
        stage: ctEscolher(pq.stage, q.stage, 'Stage', changed),
        nota_crm: ctEscolher(pq.nota_crm, q.nota_crm, 'Nota CRM', changed),
      },
      notas: n.notas ? ctEscolher(p.notas, n.notas, 'Notas', changed) : (p.notas || ''),
      avaliacao: n.avaliacao && typeof n.avaliacao === 'object' ? n.avaliacao : (p.avaliacao || null),
      transcricao: n.transcricao || p.transcricao || '',
    },
  };
}

const CT_STAGES = {
  '01': '01 Lead',
  '02': '02 Interesse',
  '03': '03 Evento',
  '04': '04 Demo',
  '05': '05 Ag. decisão',
  '06': '06 Ag. financeiro',
  '07': '07 Aprovado',
};

function ctPct(v) {
  if (v === null || v === undefined || Number.isNaN(v)) return '—';
  return Math.round(v * 100) + '%';
}

function ctFracao(cumpridos, total) {
  if (cumpridos === null || cumpridos === undefined || !total) return '—';
  return cumpridos + '/' + total;
}

const CT_AVALIACAO = [
  ['abertura', 'Abertura', 'Pelo cliente ou pela campanha'],
  ['campanha', 'Campanha', 'Peça concreta pedida'],
  ['foco', 'Foco', 'Sem preço nem máquinas'],
  ['escuta', 'Escuta', 'Pergunta e deixa falar'],
  ['fecho', 'Fecho', 'Dia ou hora'],
  ['filmagem', 'Filmagem', 'Pede autorização'],
];

function ctTempo(seg) {
  if (seg === null || seg === undefined || Number.isNaN(Number(seg))) return '—';
  const s = Math.max(0, Math.round(Number(seg)));
  const m = Math.floor(s / 60);
  return m ? m + 'm ' + String(s % 60).padStart(2, '0') + 's' : s + 's';
}

function ctQuando(iso) {
  if (!iso) return '';
  const d = new Date(iso);
  if (Number.isNaN(d.getTime())) return '';
  return d.toLocaleString('pt-PT', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' });
}

const CT_ESTADO = {
  conversa: 'Conversa',
  atendida: 'Atendida',
  sem_resposta: 'Sem resposta',
  ocupado: 'Ocupado',
  falhou: 'Falhou',
  cancelada: 'Cancelada',
  sem_relatorio: 'Sem relatório',
  marcada: 'Marcada',
};

function CtKpi({ label, value, hint, accent }) {
  return (
    <div className="card" style={{ flex: '1 1 140px', minWidth: 0, padding: '12px 16px 0', display: 'flex', flexDirection: 'column', gap: 4, overflow: 'hidden' }}>
      <div style={{ fontSize: 9.5, fontWeight: 600, letterSpacing: '0.10em', textTransform: 'uppercase', color: 'var(--text-muted)', fontFamily: 'var(--font-mono)' }}>{label}</div>
      <div style={{ fontSize: 22, fontWeight: 700, fontFamily: 'var(--font-display)', color: accent, lineHeight: 1, paddingTop: 4 }}>{value}</div>
      <div style={{ fontSize: 11, color: 'var(--text-muted)', minHeight: 16, paddingBottom: 8 }}>{hint}</div>
      <div style={{ height: 3, background: 'var(--border)', margin: '0 -16px' }}><div style={{ height: '100%', width: '100%', background: accent }} /></div>
    </div>
  );
}

function CtKpis({ kpis }) {
  const q = kpis?.quant || {};
  const u = kpis?.qual || {};
  const lista = Array.isArray(kpis?.chamadas) ? kpis.chamadas : [];
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 18 }}>
      <div>
        <div style={{ fontSize: 10, fontWeight: 600, letterSpacing: '0.07em', textTransform: 'uppercase', color: 'var(--text-muted)', fontFamily: 'var(--font-mono)', marginBottom: 8 }}>Quantitativo</div>
        <div style={{ display: 'flex', gap: 12, flexWrap: 'wrap' }}>
          <CtKpi label="Marcações" value={q.marcacoes || 0} hint="Chamadas feitas" accent="#3859D0" />
          <CtKpi label="Atendidas" value={q.atendidas || 0} hint="Quem atendeu" accent="#7c3aed" />
          <CtKpi label="Contacto" value={ctPct(q.contacto)} hint="Conversas ÷ marcações · ref. 8–15%" accent="#b45309" />
          <CtKpi label="Próximo passo" value={ctPct(q.conversa_passo)} hint="Das conversas · ref. 10–20%" accent="#0891b2" />
          <CtKpi label="Tempo médio" value={ctTempo(q.duracao_media)} hint="Duração das conversas" accent="#15803d" />
          <CtKpi label="Fala do SDR" value={q.racio_sdr == null ? '—' : ctPct(q.racio_sdr)} hint="Palavras do SDR · ref. ~43%" accent="#0f766e" />
        </div>
      </div>
      <div>
        <div style={{ fontSize: 10, fontWeight: 600, letterSpacing: '0.07em', textTransform: 'uppercase', color: 'var(--text-muted)', fontFamily: 'var(--font-mono)', marginBottom: 8 }}>Qualitativo · a avaliação do fecho{u.n ? ' · ' + u.n + (u.n === 1 ? ' conversa' : ' conversas') : ''}</div>
        <div style={{ display: 'flex', gap: 12, flexWrap: 'wrap' }}>
          <CtKpi label="BANT" value={ctPct(u.bant)} hint="4 critérios sim ÷ conversas" accent="#3859D0" />
          {CT_AVALIACAO.map(([id, label, hint]) => (
            <CtKpi key={id} label={label} value={ctPct(u[id])} hint={hint} accent={id === 'filmagem' ? '#0f766e' : id === 'fecho' ? '#15803d' : id === 'escuta' ? '#0891b2' : id === 'foco' ? '#b45309' : '#7c3aed'} />
          ))}
        </div>
      </div>
      <div className="card" style={{ padding: 16 }}>
        <div style={{ fontSize: 10, fontWeight: 600, letterSpacing: '0.07em', textTransform: 'uppercase', color: 'var(--text-muted)', fontFamily: 'var(--font-mono)' }}>Relatório das chamadas</div>
        <div style={{ fontSize: 15, fontWeight: 700, fontFamily: 'var(--font-display)', margin: '6px 0 10px' }}>Twilio e transcrição</div>
        {lista.length === 0 ? (
          <div style={{ fontSize: 13, color: 'var(--text-muted)' }}>O relatório entra quando uma chamada termina. A Twilio dá atendimento e duração. A avaliação do fecho dá o BANT e as práticas.</div>
        ) : (
          <div style={{ display: 'grid' }}>
            {lista.map(c => (
              <div key={c.id} style={{ display: 'grid', gridTemplateColumns: '108px 1fr 120px 72px 48px', gap: 8, alignItems: 'center', padding: '8px 0', borderTop: '1px solid var(--border)', fontSize: 13 }}>
                <div style={{ color: 'var(--text-muted)', fontFamily: 'var(--font-mono)', fontSize: 12 }}>{ctQuando(c.quando)}</div>
                <div style={{ minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{c.contacto || c.empresa || 'Sem contacto'}</div>
                <div style={{ color: 'var(--text-muted)' }}>{CT_ESTADO[c.estado] || c.estado}</div>
                <div style={{ fontFamily: 'var(--font-mono)', fontSize: 12 }}>{ctTempo(c.duracao_seg)}</div>
                <div style={{ fontWeight: 700, textAlign: 'right' }} title={c.bant === true ? 'BANT qualificado' : c.bant === false ? 'BANT por fechar' : ''}>{ctFracao(c.cumpridos, c.total)}</div>
              </div>
            ))}
          </div>
        )}
      </div>
    </div>
  );
}

function CtPessoaCampo({ pessoas, value, onChange, disabled, vazio }) {
  const [open, setOpen] = React.useState(false);
  const [q, setQ] = React.useState('');
  const box = React.useRef(null);
  React.useEffect(() => {
    if (!open) return undefined;
    const onDoc = (e) => { if (box.current && !box.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', onDoc);
    return () => document.removeEventListener('mousedown', onDoc);
  }, [open]);
  const escolhido = pessoas.find(p => p.id === value);
  const filtered = pessoas.filter(p => {
    if (!q.trim()) return true;
    return `${p.nome || ''} ${p.email || ''} ${p.cargo || ''} ${p.numero || ''}`.toLowerCase().includes(q.trim().toLowerCase());
  });
  const escolher = (id) => {
    onChange(id === value ? '' : id);
    setOpen(false);
  };
  return (
    <div ref={box} style={{ position: 'relative' }}>
      <button type="button" disabled={disabled} onClick={() => { if (!disabled) setOpen(v => !v); }} style={{ width: '100%', textAlign: 'left', background: '#fff', color: escolhido ? '#112954' : '#64748b', border: '1px solid #E3E6EB', borderRadius: 8, padding: '8px 10px', fontSize: 13, fontFamily: 'inherit', cursor: disabled ? 'not-allowed' : 'pointer', opacity: disabled ? 0.55 : 1 }}>
        {escolhido ? escolhido.nome : (vazio || (pessoas.length ? 'Escolhe um colaborador' : 'A carregar colaboradores'))}
      </button>
      {open && (
        <div style={{ position: 'absolute', left: 0, top: 'calc(100% + 8px)', zIndex: 40, width: 320, maxWidth: 'min(320px, 80vw)', boxSizing: 'border-box', background: '#fff', border: '1px solid #E3E6EB', borderRadius: 10, boxShadow: '0 12px 32px rgba(17,41,84,.14)', padding: 12 }}>
          <input value={q} onChange={e => setQ(e.target.value)} placeholder="Procurar nome ou email…" autoFocus style={{ display: 'block', width: '100%', boxSizing: 'border-box', border: '1px solid #E3E6EB', borderRadius: 8, padding: '8px 10px', fontSize: 12, marginBottom: 8, fontFamily: 'inherit' }} />
          <div className="scrollbar" style={{ maxHeight: 220, overflowY: 'auto', border: '1px solid #E3E6EB', borderRadius: 8 }}>
            {filtered.length === 0 && <div style={{ padding: 10, fontSize: 12, color: '#64748b' }}>{pessoas.length ? 'Sem resultados.' : 'A carregar contas do portal…'}</div>}
            {filtered.map(p => {
              const on = p.id === value;
              return (
                <label key={p.id} style={{ display: 'flex', gap: 8, alignItems: 'flex-start', padding: '7px 8px', borderBottom: '1px solid #F1F5F9', cursor: 'pointer', background: on ? 'rgba(56,89,208,0.06)' : 'transparent' }}>
                  <input type="checkbox" checked={on} onChange={() => escolher(p.id)} style={{ marginTop: 2 }} />
                  <span style={{ minWidth: 0 }}>
                    <span style={{ display: 'block', fontSize: 12, fontWeight: 600, color: '#112954' }}>{p.nome}</span>
                    {p.email && p.email !== p.numero && <span style={{ display: 'block', fontSize: 10, color: '#64748b' }}>{p.email}</span>}
                    {p.numero && <span style={{ display: 'block', fontSize: 10, color: '#64748b' }}>{p.numero}</span>}
                  </span>
                </label>
              );
            })}
          </div>
        </div>
      )}
    </div>
  );
}

function ctDigitos(raw) {
  return String(raw || '').replace(/\D/g, '');
}

function ctNumeroPt(raw) {
  const d = ctDigitos(raw);
  if (d.length === 9) return true;
  return d.length === 12 && d.startsWith('351');
}

function ctRotuloNumero(raw) {
  const d = ctDigitos(raw);
  const n = d.length === 12 && d.startsWith('351') ? d.slice(3) : d;
  if (n.length !== 9) return String(raw || '').trim();
  return `${n.slice(0, 3)} ${n.slice(3, 6)} ${n.slice(6)}`;
}

function ctMostrar(valor, stage) {
  if (valor === true) return 'Sim';
  if (valor === false) return 'Não';
  if (stage && CT_STAGES[valor]) return CT_STAGES[valor];
  if (valor === null || valor === undefined || valor === '') return '';
  return String(valor);
}

const MktChamadaTesteScreen = ({ userEmail }) => {
  ctEmailActual = userEmail || window.currentUser?.email || ctEmailActual;
  const [estado, setEstado] = React.useState(null);
  const [sdrs, setSdrs] = React.useState([]);
  const [sdrId, setSdrId] = React.useState('');
  const [dossier, setDossier] = React.useState(null);
  const [colaboradores, setColaboradores] = React.useState([]);
  const [colaboradorId, setColaboradorId] = React.useState('');
  const [telefone, setTelefone] = React.useState('');
  const [empresa, setEmpresa] = React.useState('');
  const [contacto, setContacto] = React.useState('');
  const [fase, setFase] = React.useState('pronto');
  const [erro, setErro] = React.useState('');
  const [linhas, setLinhas] = React.useState([]);
  const [fecho, setFecho] = React.useState(null);
  const [novos, setNovos] = React.useState([]);
  const [aLer, setALer] = React.useState(false);
  const [guardado, setGuardado] = React.useState(null);
  const [canalLead, setCanalLead] = React.useState('');
  const [kpis, setKpis] = React.useState(null);
  const [origem, setOrigem] = React.useState('');
  const callRef = React.useRef(null);
  const digiRef = React.useRef(null);
  const digiPoll = React.useRef(null);
  const callSidRef = React.useRef('');
  const deviceRef = React.useRef(null);
  const capsRef = React.useRef([]);
  const filaRef = React.useRef(Promise.resolve());
  const linhasRef = React.useRef([]);
  const paraRef = React.useRef('');
  const vivoLock = React.useRef(false);
  const vivoDirty = React.useRef(false);
  const transRef = React.useRef(null);
  const fechoRef = React.useRef(null);
  const faseRef = React.useRef('pronto');
  const empresaRef = React.useRef('');
  const contactoRef = React.useRef('');
  const novosTimer = React.useRef(null);

  React.useEffect(() => { faseRef.current = fase; }, [fase]);
  React.useEffect(() => { empresaRef.current = empresa; }, [empresa]);
  React.useEffect(() => { contactoRef.current = contacto; }, [contacto]);

  React.useEffect(() => {
    let activo = true;
    const url = colaboradorId
      ? '/api/marketing/chamada-teste/dossier?colaboradorId=' + encodeURIComponent(colaboradorId)
      : '/api/marketing/chamada-teste/dossier';
    ctApi(url)
      .then(d => { if (activo) setDossier(d); })
      .catch(e => { if (activo) { setErro(e.message); setDossier({ erro: e.message }); } });
    return () => { activo = false; };
  }, [colaboradorId]);

  React.useEffect(() => {
    let activo = true;
    (async () => {
      try {
        const me = await fetch('/api/me', { credentials: 'same-origin' }).then(r => r.ok ? r.json() : null);
        const email = me?.user?.email || userEmail || '';
        if (email) ctEmailActual = email;
        const [est, nums, cols, kpi] = await Promise.all([
          ctApi('/api/marketing/chamada-teste/estado'),
          ctApi('/api/marketing/chamada-teste/numeros'),
          ctApi('/api/marketing/chamada-teste/colaboradores'),
          ctApi('/api/marketing/chamada-teste/kpis').catch(() => null),
        ]);
        if (!activo) return;
        setEstado(est);
        if (kpi) setKpis(kpi);
        const pessoas = Array.isArray(cols.colaboradores) ? cols.colaboradores : [];
        setColaboradores(pessoas);
        const validados = Array.isArray(nums.sdrs) ? nums.sdrs : [];
        setSdrs(validados);
        setSdrId(validados.some(s => s.id === nums.preferido) ? nums.preferido : (validados[0]?.id || ''));
        if (!ctDigiLer()) {
          setCanalLead('A abrir a última chamada');
          const ultima = await ctApi('/api/marketing/chamada-teste/ultimo').catch(() => null);
          if (!activo) return;
          setCanalLead('');
          if (ultima?.transcricao) {
            const falas = Array.isArray(ultima.linhas) ? ultima.linhas : [];
            linhasRef.current = falas;
            setLinhas(falas);
            const resto = { ...ultima };
            delete resto.linhas;
            fechoRef.current = resto;
            setFecho(resto);
            if (ultima.callSid) callSidRef.current = ultima.callSid;
            if (ultima.contacto) setContacto(ultima.contacto);
            if (ultima.empresa) setEmpresa(ultima.empresa);
            setOrigem(ultima.origem === 'digi' ? 'digi' : '');
            setFase('fecho');
          }
        }
      } catch (e) {
        if (activo) setErro(e.message);
      }
    })();
    return () => {
      activo = false;
      if (digiPoll.current) window.clearInterval(digiPoll.current);
      try { callRef.current?.disconnect(); } catch (e) {}
      try { deviceRef.current?.destroy(); } catch (e) {}
      capsRef.current.forEach(c => c.stop());
    };
  }, [userEmail]);

  React.useEffect(() => {
    const el = transRef.current;
    if (el) el.scrollTop = el.scrollHeight;
  }, [linhas]);

  const aplicar = (out, transcricao) => {
    const { merged, changed } = ctFundir(
      { ...fechoRef.current, transcricao },
      { ...out, transcricao },
    );
    fechoRef.current = merged;
    setFecho(merged);
    if (changed.length) {
      setNovos(changed);
      window.clearTimeout(novosTimer.current);
      novosTimer.current = window.setTimeout(() => setNovos([]), 1600);
    }
    if (!empresaRef.current && merged.empresa) setEmpresa(merged.empresa);
    if (!contactoRef.current && merged.pessoa?.nome_completo) setContacto(merged.pessoa.nome_completo);
  };

  const extrairVivo = () => {
    if (faseRef.current !== 'em-chamada' && faseRef.current !== 'a-fechar') return;
    const transcricao = linhasRef.current.map(l => (l.lado === 'lead' ? 'Lead' : 'SDR') + ': ' + l.text).join('\n');
    if (transcricao.trim().length < 12) return;
    if (vivoLock.current) { vivoDirty.current = true; return; }
    vivoLock.current = true;
    setALer(true);
    ctApi('/api/marketing/chamada-teste/extrair', {
      method: 'POST',
      body: JSON.stringify({ email: ctEmail(), empresa: empresaRef.current, contacto: contactoRef.current, transcricao }),
    }).then(out => {
      if (!out?.vazio) aplicar(out, transcricao);
    }).catch(() => {}).finally(() => {
      vivoLock.current = false;
      setALer(false);
      if (vivoDirty.current) {
        vivoDirty.current = false;
        extrairVivo();
      }
    });
  };

  React.useEffect(() => {
    if (fase !== 'em-chamada') return undefined;
    const t = window.setTimeout(extrairVivo, 1600);
    return () => window.clearTimeout(t);
  }, [linhas, fase]);

  const acrescentar = (lado, text) => {
    const t = String(text || '').trim();
    if (!t) return;
    const row = {
      lado,
      text: t,
      id: Date.now() + Math.random(),
      hora: new Date().toLocaleTimeString('pt-PT', { hour: '2-digit', minute: '2-digit', second: '2-digit' }),
    };
    linhasRef.current = [...linhasRef.current, row];
    setLinhas(linhasRef.current);
  };

  const enviarPcm = (lado, samples, rate) => {
    if (ctRms(samples) < 0.012) return;
    const wav = ctWav(ctDownsample(samples, rate, 16000), 16000);
    filaRef.current = filaRef.current.then(async () => {
      const fd = new FormData();
      fd.append('audio', wav, lado + '.wav');
      fd.append('lado', lado);
      fd.append('email', ctEmail());
      const data = await ctApi('/api/marketing/chamada-teste/trecho', { method: 'POST', body: fd });
      acrescentar(data.lado || lado, data.text);
    }).catch(e => setErro(e.message));
  };

  const ouvir = (call) => {
    const ligar = (stream, lado) => {
      if (!stream) return;
      capsRef.current.push(ctCaptura(stream, (samples, rate) => enviarPcm(lado, samples, rate)));
    };
    ligar(typeof call.getLocalStream === 'function' ? call.getLocalStream() : null, 'sdr');
    const remoto = typeof call.getRemoteStream === 'function' ? call.getRemoteStream() : null;
    if (remoto) {
      ligar(remoto, 'lead');
      setCanalLead('Lead no canal remoto');
    } else {
      setCanalLead('À espera do áudio de quem atendeu');
      setTimeout(() => {
        const r2 = typeof call.getRemoteStream === 'function' ? call.getRemoteStream() : null;
        if (r2) {
          ligar(r2, 'lead');
          setCanalLead('Lead no canal remoto');
        } else setCanalLead('O áudio de quem atendeu não chegou ao browser');
      }, 1200);
    }
  };

  const pararCaptura = () => {
    capsRef.current.forEach(c => { c.flush(); c.stop(); });
    capsRef.current = [];
  };

  const lerSid = (call) => {
    const sid = call?.parameters?.CallSid || '';
    if (/^CA[0-9a-f]{32}$/i.test(sid)) callSidRef.current = sid;
  };

  const relatar = async (fechoExtra) => {
    const transcricao = linhasRef.current.map(l => (l.lado === 'lead' ? 'Lead' : 'SDR') + ': ' + l.text).join('\n');
    try {
      const data = await ctApi('/api/marketing/chamada-teste/relato', {
        method: 'POST',
        body: JSON.stringify({
          email: ctEmail(),
          callSid: callSidRef.current,
          sdrId,
          empresa: empresaRef.current,
          contacto: contactoRef.current,
          transcricao,
          fecho: fechoExtra || null,
        }),
      });
      if (data?.kpis) setKpis(data.kpis);
      if (data?.id) callSidRef.current = callSidRef.current || '';
    } catch (e) {
      if (!/não tem relatório|não está registada/i.test(e.message || '')) setErro(e.message);
    }
  };

  const fecharChamada = async () => {
    setFase('a-fechar');
    try {
      await filaRef.current;
    } catch (e) {}
    const transcricao = linhasRef.current.map(l => (l.lado === 'lead' ? 'Lead' : 'SDR') + ': ' + l.text).join('\n');
    if (transcricao.trim().length < 8) {
      await relatar(null);
      setErro('A transcrição ficou curta. A marcação entra no relatório na mesma.');
      setFase('pronto');
      return;
    }
    try {
      const out = await ctApi('/api/marketing/chamada-teste/fechar', {
        method: 'POST',
        body: JSON.stringify({ email: ctEmail(), empresa, contacto, transcricao, callSid: callSidRef.current }),
      });
      aplicar(out, transcricao);
      await relatar(out);
      setFase('fecho');
    } catch (e) {
      await relatar(null);
      setErro(e.message);
      setFase('pronto');
    }
  };

  const pararPollDigi = () => {
    if (digiPoll.current) window.clearInterval(digiPoll.current);
    digiPoll.current = null;
  };

  const mostrarFechoDigi = async (d) => {
    if (!d?.conversationId) {
      setFase('pronto');
      setErro('A chamada terminou sem transcrição');
      return;
    }
    setOrigem('digi');
    setFase('a-fechar');
    setCanalLead('A fechar a transcrição');
    try {
      const out = await ctApi('/api/marketing/chamada-teste/digi/fechar', {
        method: 'POST',
        body: JSON.stringify({
          email: ctEmail(),
          conversationId: d.conversationId,
          callSid: d.callSid,
          empresa: empresaRef.current,
          contacto: contactoRef.current,
        }),
      });
      const falas = Array.isArray(out.linhas) ? out.linhas : [];
      linhasRef.current = falas;
      setLinhas(falas);
      const resto = { ...out };
      delete resto.linhas;
      aplicar(resto, out.transcricao || '');
      if (out.callSid) callSidRef.current = out.callSid;
      setCanalLead('');
      setFase('fecho');
    } catch (e) {
      setErro(e.message);
      setFase('pronto');
    }
  };

  const acompanharDigi = () => {
    pararPollDigi();
    digiPoll.current = window.setInterval(async () => {
      const d = digiRef.current;
      if (!d) return;
      try {
        const st = await ctApi('/api/marketing/chamada-teste/digi/estado?callSid=' + encodeURIComponent(d.callSid || '') + '&conversationId=' + encodeURIComponent(d.conversationId || ''));
        if (Array.isArray(st.linhas) && st.linhas.length) {
          linhasRef.current = st.linhas;
          setLinhas(st.linhas);
          setOrigem('digi');
        }
        if (st.fase === 'em-chamada') {
          setFase('em-chamada');
          setCanalLead(st.mensagem || 'A Digi está em chamada');
        } else if (st.fase === 'a-chamar') {
          setCanalLead(st.mensagem || 'A Digi está a chamar');
        } else if (st.fase === 'terminada' || st.fase === 'falhou') {
          pararPollDigi();
          digiRef.current = null;
          ctDigiLimpar();
          if (st.fase === 'falhou') {
            setCanalLead('');
            setErro(st.mensagem || 'A chamada terminou');
            setFase('pronto');
          } else {
            mostrarFechoDigi(d);
          }
        }
      } catch (e) {}
    }, 2000);
  };

  React.useEffect(() => {
    const guardado = ctDigiLer();
    if (!guardado || guardado.email !== ctEmail()) return undefined;
    if (!guardado.callSid && !guardado.conversationId) return undefined;
    digiRef.current = guardado;
    setFase('a-chamar');
    setCanalLead('A Digi está a ligar do número dela');
    acompanharDigi();
    return () => pararPollDigi();
  }, []);

  const ligar = async () => {
    setErro('');
    setFecho(null);
    fechoRef.current = null;
    setNovos([]);
    setGuardado(null);
    setLinhas([]);
    linhasRef.current = [];
    callSidRef.current = '';
    setCanalLead('');
    setOrigem('');
    setFase('a-ligar');
    const sdr = sdrs.find(s => s.id === sdrId);
    const destino = {
      email: ctEmail(),
      sdrId,
      telefone: ctNumeroPt(telefone) ? telefone : '',
      colaboradorId: ctNumeroPt(telefone) ? '' : colaboradorId,
      contacto,
      empresa,
    };
    if (sdr?.agente) {
      try {
        const sess = await ctApi('/api/marketing/chamada-teste/digi', {
          method: 'POST',
          body: JSON.stringify(destino),
        });
        const activa = {
          email: ctEmail(),
          conversationId: sess.conversationId || '',
          callSid: sess.callSid || '',
        };
        digiRef.current = activa;
        ctDigiGuardar(activa);
        callSidRef.current = activa.callSid;
        setOrigem('digi');
        setCanalLead('A Digi está a ligar do número dela');
        setFase('a-chamar');
        acompanharDigi();
      } catch (e) {
        setErro(e.message);
        setFase('pronto');
      }
      return;
    }
    try {
      await ctLoadTwilio();
      const sess = await ctApi('/api/marketing/chamada-teste/token', {
        method: 'POST',
        body: JSON.stringify(destino),
      });
      paraRef.current = sess.para;
      const device = new window.Twilio.Device(sess.token, { closeProtection: true });
      deviceRef.current = device;
      device.on('error', err => setErro(err.message || 'Erro na chamada'));
      const call = await device.connect({
        params: { chamadaId: sess.chamadaId },
        rtcConstraints: { audio: { echoCancellation: true, noiseSuppression: true, autoGainControl: true } },
      });
      callRef.current = call;
      call.on('ringing', () => { lerSid(call); setFase('a-chamar'); });
      call.on('accept', () => { lerSid(call); setFase('em-chamada'); ouvir(call); });
      call.on('disconnect', () => {
        lerSid(call);
        pararCaptura();
        callRef.current = null;
        fecharChamada();
      });
      call.on('cancel', () => {
        lerSid(call);
        setFase('pronto');
        relatar(null).then(() => setErro('Chamada cancelada'));
      });
      call.on('error', err => setErro(err.message || 'Erro na chamada'));
      setFase('a-chamar');
    } catch (e) {
      setErro(e.message);
      setFase('pronto');
    }
  };

  const desligar = async () => {
    const d = digiRef.current;
    if (d) {
      try {
        await ctApi('/api/marketing/chamada-teste/digi/desligar', {
          method: 'POST',
          body: JSON.stringify({ email: ctEmail(), callSid: d.callSid, conversationId: d.conversationId }),
        });
        pararPollDigi();
        digiRef.current = null;
        ctDigiLimpar();
        mostrarFechoDigi(d);
      } catch (e) {
        setErro(e.message);
      }
      return;
    }
    try { callRef.current?.disconnect(); } catch (e) {}
  };

  const guardar = async () => {
    if (!fecho) return;
    setErro('');
    try {
      const row = await ctApi('/api/marketing/chamada-teste/guardar', {
        method: 'POST',
        body: JSON.stringify({
          email: ctEmail(),
          telefone: paraRef.current || telefone,
          empresa, contacto,
          resumo: fecho.resumo,
          sugestoes: fecho.sugestoes,
          bant: fecho.bant,
          pessoa: fecho.pessoa,
          qualificacao: fecho.qualificacao,
          notas: fecho.notas,
          transcricao: fecho.transcricao,
          callSid: callSidRef.current,
        }),
      });
      setGuardado(row.id);
    } catch (e) { setErro(e.message); }
  };

  const pronta = estado?.pronta;
  const ocupado = fase === 'a-ligar' || fase === 'a-chamar' || fase === 'em-chamada' || fase === 'a-fechar';
  const labelFase = {
    'a-ligar': 'A preparar o headset',
    'a-chamar': 'A chamar',
    'em-chamada': 'Em chamada',
    'a-fechar': 'A fechar a transcrição',
  }[fase];

  const q = fecho?.qualificacao || {};
  const grupos = [
    {
      id: 'rpc',
      titulo: 'RPC SDR',
      sub: 'BANT da campanha, NIF e notas',
      campos: [
        ['Budget', fecho?.bant?.budget, 'A campanha do cliente já tem produção marcada'],
        ['Authority', fecho?.bant?.authority, 'Quem propõe na agência e quem aprova na marca'],
        ['Need', fecho?.bant?.need, 'CO₂ para o cliente, ou o caso no portfólio'],
        ['Timeline', fecho?.bant?.timeline, 'Data da campanha ou da conversa seguinte'],
        ['NIF', q.nif, ''],
        ['Notas', fecho?.notas, ''],
      ],
    },
    {
      id: 'crm',
      titulo: 'CRM',
      sub: 'Pessoa, empresa e nota',
      campos: [
        ['Empresa', fecho?.empresa || empresa, ''],
        ['Nome', fecho?.pessoa?.nome_completo, ''],
        ['Cargo', fecho?.pessoa?.cargo, ''],
        ['Email', fecho?.pessoa?.email, ''],
        ['Telemóvel', fecho?.pessoa?.telemovel, ''],
        ['Nota', q.nota_crm, ''],
        ['Próxima data', q.data_proximo_passo, ''],
        ['Stage', q.stage, ''],
      ],
    },
    {
      id: 'comercial',
      titulo: 'Comercial',
      sub: 'Qualificação Biond Real Works — Agências',
      campos: [
        ['Cliente final', q.cliente_final, ''],
        ['Aplicação', q.aplicacao, ''],
        ['Gráfica habitual', q.grafica_habitual, ''],
        ['Autorização para filmar', q.autorizacao_filmagem, ''],
        ['Próximo passo', q.proximo_passo, ''],
        ['Data', q.data_proximo_passo, ''],
      ],
    },
  ];

  const sdrEscolhido = sdrs.find(s => s.id === sdrId);
  const digiEscolhida = Boolean(sdrEscolhido?.agente);
  const numeroMarcado = ctNumeroPt(telefone);
  const quem = numeroMarcado ? null : colaboradores.find(c => c.id === colaboradorId);
  const nomeLead = quem?.nome || (numeroMarcado ? (contacto || ctRotuloNumero(telefone)) : 'Quem atende');
  const mesmoNumero = numeroMarcado && ctDigitos(sdrEscolhido?.numero).slice(-9) === ctDigitos(telefone).slice(-9);
  const ctInp = { width: '100%', boxSizing: 'border-box', padding: '8px 10px', borderRadius: 6, border: '1px solid var(--border)', background: 'var(--bg)', color: 'var(--text)', fontSize: 13, fontFamily: 'inherit' };
  const ctLbl = { fontSize: 10, fontWeight: 600, letterSpacing: '0.07em', textTransform: 'uppercase', color: 'var(--text-muted)', fontFamily: 'var(--font-mono)' };
  const estadoChamada = labelFase || (fase === 'fecho' ? 'Chamada terminada' : 'Pronta');

  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100%', minHeight: 0, background: 'var(--bg)' }}>
      <div style={{ background: 'var(--bg-elev)', borderBottom: '1px solid var(--border)', padding: '20px 40px 16px', flexShrink: 0 }}>
        <div style={{ fontSize: 11, fontWeight: 600, letterSpacing: '0.07em', color: 'var(--text-muted)', textTransform: 'uppercase', marginBottom: 6, fontFamily: 'var(--font-mono)' }}>MARKETING · CHAMADA TESTE</div>
        <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 16 }}>
          <div>
            <h1 style={{ margin: 0, fontSize: 21, fontWeight: 700, color: 'var(--text)', fontFamily: 'var(--font-display)', letterSpacing: '-0.01em', lineHeight: 1.2 }}>Chamada teste</h1>
            <div style={{ fontSize: 12, color: 'var(--text-muted)', marginTop: 6, display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
              <span style={{ color: 'var(--ai-500)', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.06em', fontSize: 10, fontFamily: 'var(--font-mono)' }}>Biond</span>
              <span>· Biond Real Works — Agências PT</span>
              <span>· O áudio não fica guardado</span>
            </div>
          </div>
          <span className={fase === 'em-chamada' ? 'badge badge-success' : 'badge'}>{estadoChamada}</span>
        </div>
      </div>
      <div className="scrollbar" style={{ overflowY: 'auto', flex: 1, minHeight: 0, padding: '28px 40px 64px', display: 'flex', flexDirection: 'column', gap: 18 }}>
      <CtKpis kpis={kpis} />
      {erro && <div style={{ marginBottom: 12, padding: '8px 12px', borderRadius: 6, background: 'color-mix(in oklch, var(--danger) 12%, transparent)', color: 'var(--danger)', fontSize: 13 }}>{erro}</div>}

      {estado && !pronta && (
        <div className="card" style={{ padding: 16, marginBottom: 16, maxWidth: 760 }}>
          <div style={{ fontWeight: 600, marginBottom: 6 }}>A chamada real ainda não pode sair</div>
          <div style={{ fontSize: 13, color: 'var(--text-muted)', marginBottom: 8 }}>Faltam estas variáveis no contentor de Marketing:</div>
          <div style={{ fontFamily: 'var(--font-mono)', fontSize: 12, lineHeight: 1.7 }}>{(estado.falta || []).join(' · ')}</div>
          <div style={{ fontSize: 12, color: 'var(--text-muted)', marginTop: 10 }}>
            Na TwiML App, o pedido de voz em POST é <span style={{ fontFamily: 'var(--font-mono)' }}>{estado.webhook}</span>. O número de saída é o TWILIO_CALLER_ID.
          </div>
        </div>
      )}

      <div className="card" style={{ padding: 16, display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(180px, 1fr))', gap: 12, alignItems: 'end' }}>
        <div style={{ display: 'grid', gap: 6 }}>
          <span style={ctLbl}>Ligar de</span>
          <CtPessoaCampo pessoas={sdrs} value={sdrId} disabled={ocupado || sdrs.length === 0} vazio={!estado ? 'A carregar colaboradores' : (sdrs.length ? 'Escolhe um SDR' : 'Sem número validado')} onChange={setSdrId} />
        </div>
        <div style={{ display: 'grid', gap: 6 }}>
          <span style={ctLbl}>Ligar para</span>
          <CtPessoaCampo pessoas={colaboradores} value={numeroMarcado ? '' : colaboradorId} disabled={ocupado} onChange={id => {
            setTelefone('');
            setColaboradorId(id);
            const pessoa = colaboradores.find(c => c.id === id);
            if (pessoa && !contacto) setContacto(pessoa.nome);
          }} />
        </div>
        <label style={{ display: 'grid', gap: 6 }}>
          <span style={ctLbl}>Número</span>
          <input value={telefone} onChange={e => { setTelefone(e.target.value); if (ctDigitos(e.target.value)) setColaboradorId(''); }} disabled={ocupado} placeholder="910 000 000" inputMode="tel" autoComplete="off" style={ctInp} />
        </label>
        <label style={{ display: 'grid', gap: 6 }}>
          <span style={ctLbl}>Empresa</span>
          <input value={empresa} onChange={e => setEmpresa(e.target.value)} disabled={ocupado} style={ctInp} />
        </label>
        <label style={{ display: 'grid', gap: 6 }}>
          <span style={ctLbl}>Contacto</span>
          <input value={contacto} onChange={e => setContacto(e.target.value)} disabled={ocupado} style={ctInp} />
        </label>
        <div style={{ display: 'flex', gap: 8, alignItems: 'center', paddingBottom: 2 }}>
          <button className="btn btn-primary" onClick={ligar} disabled={!pronta || ocupado || !sdrId || (!colaboradorId && !numeroMarcado) || (!numeroMarcado && sdrId === colaboradorId) || mesmoNumero || (digiEscolhida && estado && !estado.digi)}>Ligar</button>
          <button className="btn" onClick={desligar} disabled={fase !== 'em-chamada' && fase !== 'a-chamar'}>Desligar</button>
        </div>
        {digiEscolhida && estado && !estado.digi && (
          <div style={{ gridColumn: '1 / -1', fontSize: 12, color: 'var(--text-muted)' }}>A Digi ainda não pode ligar: falta a chave da ElevenLabs.</div>
        )}
      </div>
      <div className="card" style={{ padding: 16 }}>
        <div style={ctLbl}>Dossier da Digi · ensaio</div>
        <div style={{ fontSize: 15, fontWeight: 700, fontFamily: 'var(--font-display)', margin: '6px 0 8px' }}>Contexto para ela ligar do número dela</div>
        {!dossier && <div style={{ fontSize: 13, color: 'var(--text-muted)' }}>A montar o contexto…</div>}
        {dossier?.erro && <div style={{ fontSize: 13, color: 'var(--danger, #b42318)' }}>{dossier.erro}</div>}
        {dossier && !dossier.erro && (
          <div style={{ display: 'grid', gap: 14 }}>
            <div style={{ fontSize: 12, color: 'var(--text-muted)', lineHeight: 1.5 }}>{dossier.ensaio_nota}</div>
            <div>
              <div style={{ ...ctLbl, marginBottom: 8 }}>Campanhas no ar</div>
              <div style={{ display: 'grid', gap: 8 }}>
                {(dossier.campanhas_no_ar || []).map(c => (
                  <div key={c.titulo} style={{ padding: 10, borderRadius: 8, border: '1px solid var(--border)' }}>
                    <div style={{ fontSize: 13, fontWeight: 700 }}>{c.marca} · {c.titulo}</div>
                    <div style={{ fontSize: 12, color: 'var(--text-muted)', marginTop: 2 }}>{(c.canais || []).join(' · ') || 'Sem canal activo'}</div>
                    {c.mensagem && <div style={{ fontSize: 12, lineHeight: 1.45, marginTop: 6 }}>{c.mensagem}</div>}
                  </div>
                ))}
                {!(dossier.campanhas_no_ar || []).length && <div style={{ fontSize: 13, color: 'var(--text-muted)' }}>Não há campanhas no ar.</div>}
              </div>
            </div>
            <div style={{ fontSize: 12, color: 'var(--text-muted)', lineHeight: 1.5 }}>{dossier.nota_envio}</div>
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(240px, 1fr))', gap: 12 }}>
              <div style={{ padding: 12, borderRadius: 8, border: '1px solid var(--border)', background: 'var(--bg)' }}>
                <div style={ctLbl}>Pessoa</div>
                <div style={{ fontSize: 14, fontWeight: 700, marginTop: 6 }}>{numeroMarcado ? (contacto || ctRotuloNumero(telefone)) : (dossier.pessoa?.nome || 'Escolhe em Ligar para')}</div>
                <div style={{ fontSize: 12, color: 'var(--text-muted)', marginTop: 4 }}>{numeroMarcado ? ctRotuloNumero(telefone) : ([dossier.pessoa?.cargo, dossier.pessoa?.departamento, dossier.pessoa?.empresa].filter(Boolean).join(' · ') || 'Sem cargo nem empresa')}</div>
                <div style={{ fontSize: 12, marginTop: 6 }}>{dossier.pessoa?.email || ''}</div>
                <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', marginTop: 8 }}>
                  {(dossier.pessoa?.fontes || []).map(f => <span key={f} className="badge">{f === 'crm' ? 'CRM' : f === 'prospeccao' ? 'Prospeção' : 'Colaborador'}</span>)}
                  {numeroMarcado && <span className="badge">Número marcado</span>}
                  {!numeroMarcado && !(dossier.pessoa?.fontes || []).length && <span className="badge">Sem ficha</span>}
                </div>
                {dossier.pessoa?.crm?.empresa && <div style={{ fontSize: 12, marginTop: 8 }}>CRM: {dossier.pessoa.crm.empresa}</div>}
                {dossier.pessoa?.crm_externo?.empresa && <div style={{ fontSize: 12, marginTop: 4 }}>CRM: {dossier.pessoa.crm_externo.empresa}{dossier.pessoa.crm_externo.cargo ? ` · ${dossier.pessoa.crm_externo.cargo}` : ''}</div>}
                {dossier.pessoa?.crm_externo?.notas && <div style={{ fontSize: 12, marginTop: 4, whiteSpace: 'pre-wrap' }}>{dossier.pessoa.crm_externo.notas}</div>}
                {dossier.pessoa?.prospeccao?.empresa && <div style={{ fontSize: 12, marginTop: 4 }}>Prospeção: {dossier.pessoa.prospeccao.empresa}{dossier.pessoa.prospeccao.cidade ? ` · ${dossier.pessoa.prospeccao.cidade}` : ''}</div>}
                {(dossier.pessoa?.avisos || []).map(a => <div key={a} style={{ fontSize: 12, marginTop: 6, color: 'var(--text-muted)' }}>{a}</div>)}
              </div>
              <div style={{ padding: 12, borderRadius: 8, border: '1px solid var(--border)', background: 'var(--bg)' }}>
                <div style={ctLbl}>{dossier.ficha?.fonte === 'briefing' ? 'Briefing da campanha' : 'Ficha outbound'}</div>
                <div style={{ fontSize: 13, fontWeight: 700, marginTop: 6 }}>{dossier.campanha}</div>
                <div style={{ fontSize: 12, color: 'var(--text-muted)', margin: '4px 0 8px' }}>{dossier.ficha?.fonte === 'briefing' ? 'One shot' : `${dossier.ficha?.versao ? `Versão ${dossier.ficha.versao}` : 'Sem versão'}${dossier.ficha?.activa ? ' · activa' : ''}`}</div>
                {dossier.ficha?.nota && <div style={{ fontSize: 12, marginBottom: 8 }}>{dossier.ficha.nota}</div>}
                <div style={{ fontSize: 12, lineHeight: 1.45, whiteSpace: 'pre-wrap', maxHeight: 180, overflow: 'auto' }}>{dossier.ficha?.outbound || 'A campanha ainda não tem ficha outbound.'}</div>
              </div>
            </div>
            <div>
              <div style={{ ...ctLbl, marginBottom: 8 }}>Emails desta one shot {dossier.envio_workflow ? `· workflow em ${ctQuando(dossier.envio_workflow)}` : '· sem envio de workflow para esta pessoa'}</div>
              <div style={{ display: 'grid', gap: 8 }}>
                {(dossier.emails || []).map((em, i) => (
                  <div key={i} style={{ padding: 10, borderRadius: 8, border: '1px solid var(--border)' }}>
                    <div style={{ fontSize: 13, fontWeight: 700 }}>{em.titulo}</div>
                    <div style={{ fontSize: 12, color: 'var(--text-muted)', marginTop: 2 }}>{em.assunto}</div>
                    {em.preheader && <div style={{ fontSize: 12, marginTop: 4 }}>{em.preheader}</div>}
                    <div style={{ fontSize: 12, lineHeight: 1.45, marginTop: 6, whiteSpace: 'pre-wrap' }}>{em.corpo}</div>
                  </div>
                ))}
                {!(dossier.emails || []).length && <div style={{ fontSize: 13, color: 'var(--text-muted)' }}>Esta campanha não tem emails em produção.</div>}
              </div>
            </div>
            <div style={{ padding: 12, borderRadius: 8, background: 'var(--bg)', border: '1px solid var(--border)' }}>
              <div style={ctLbl}>Comportamento do agente</div>
              <div style={{ fontSize: 12, lineHeight: 1.5, whiteSpace: 'pre-wrap', marginTop: 8 }}>{dossier.comportamento}</div>
            </div>
          </div>
        )}
      </div>

      {(canalLead || aLer) && (
        <div style={{ display: 'flex', gap: 8 }}>
          {canalLead && <span className="badge">{canalLead}</span>}
          {aLer && <span className="badge badge-info">A preencher os campos</span>}
        </div>
      )}

      <div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1.55fr) minmax(280px, 0.85fr)', gap: 18, alignItems: 'start' }}>
        <div className="card" style={{ padding: 0, display: 'flex', flexDirection: 'column', minHeight: 460 }}>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, padding: '14px 16px', borderBottom: '1px solid var(--border)' }}>
            <div>
              <div style={ctLbl}>Conversa</div>
              <div style={{ fontSize: 15, fontWeight: 700, fontFamily: 'var(--font-display)', marginTop: 4 }}>{nomeLead}</div>
            </div>
            <span className="badge">{linhas.length ? `${linhas.length} falas` : 'À espera'}</span>
          </div>
          <div ref={transRef} className="scrollbar" style={{ flex: 1, maxHeight: 520, overflowY: 'auto', padding: 16, background: 'var(--bg)', display: 'flex', flexDirection: 'column', gap: 10 }}>
            {linhas.length === 0 && <div style={{ fontSize: 13, color: 'var(--text-muted)', textAlign: 'center', marginTop: 48 }}>{(origem === 'digi' || digiEscolhida) ? 'A Digi fica à direita. Quem atende fica à esquerda. O resumo aparece aqui ao lado quando a chamada termina.' : 'O SDR fica à direita. Quem atende fica à esquerda.'}</div>}
            {linhas.map(l => {
              const sdr = l.lado !== 'lead';
              return (
                <div key={l.id} style={{ display: 'flex', justifyContent: sdr ? 'flex-end' : 'flex-start' }}>
                  <div style={{ maxWidth: '78%', background: sdr ? 'color-mix(in oklch, var(--ai-500) 12%, var(--bg-elev))' : 'var(--bg-elev)', border: '1px solid var(--border)', borderRadius: sdr ? '12px 4px 12px 12px' : '4px 12px 12px 12px', padding: '8px 10px' }}>
                    <div style={{ ...ctLbl, marginBottom: 4 }}>{sdr ? ((origem === 'digi' || digiEscolhida) ? 'Digi' : 'SDR') : nomeLead}</div>
                    <div style={{ fontSize: 13, lineHeight: 1.5, whiteSpace: 'pre-wrap', color: 'var(--text)' }}>{l.text}</div>
                    <div style={{ fontSize: 10, color: 'var(--text-dim)', textAlign: 'right', marginTop: 6, fontFamily: 'var(--font-mono)' }}>{l.hora}</div>
                  </div>
                </div>
              );
            })}
          </div>
        </div>

        <div style={{ display: 'grid', gap: 18 }}>
          <div className="card" style={{ padding: 16 }}>
            <div style={ctLbl}>Dicas para o SDR</div>
            <div style={{ fontSize: 13, fontWeight: 700, fontFamily: 'var(--font-display)', margin: '6px 0 8px' }}>Cold call e a campanha</div>
            <div style={{ fontSize: 12, color: 'var(--text-muted)', marginBottom: 10 }}>A mesma avaliação que alimenta os KPIs de qualidade.</div>
            {fecho?.avaliacao && (
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6, marginBottom: 10 }}>
                {CT_AVALIACAO.map(([id, label]) => (
                  <div key={id} style={{ fontSize: 12, padding: '6px 8px', borderRadius: 6, background: 'var(--bg)', border: '1px solid var(--border)', color: fecho.avaliacao[id] === true ? 'var(--success)' : 'var(--text-muted)' }}>
                    {fecho.avaliacao[id] === true ? 'Sim' : fecho.avaliacao[id] === false ? 'Não' : '—'} · {label}
                  </div>
                ))}
              </div>
            )}
            {(fecho?.sugestoes || []).length === 0 ? (
              <div style={{ fontSize: 13, color: 'var(--text-muted)' }}>As dicas aparecem quando a chamada termina.</div>
            ) : (
              <div style={{ display: 'grid', gap: 8 }}>
                {fecho.sugestoes.map((s, i) => (
                  <div key={i} style={{ fontSize: 13, lineHeight: 1.5, padding: '8px 10px', borderRadius: 6, background: 'var(--bg)', border: '1px solid var(--border)' }}>{s}</div>
                ))}
              </div>
            )}
          </div>
          <div className="card" style={{ padding: 16 }}>
            <div style={ctLbl}>Resumo</div>
            <div style={{ fontSize: 13, fontWeight: 700, fontFamily: 'var(--font-display)', margin: '6px 0 10px' }}>Por tópicos</div>
            {(fecho?.resumo_topicos || []).length === 0 ? (
              <div style={{ fontSize: 13, color: 'var(--text-muted)', lineHeight: 1.5 }}>{fecho?.resumo || 'O resumo aparece quando a chamada termina.'}</div>
            ) : (
              <div style={{ display: 'grid', gap: 10 }}>
                {fecho.resumo_topicos.map((t, i) => (
                  <div key={i} style={{ paddingBottom: 10, borderBottom: i < fecho.resumo_topicos.length - 1 ? '1px solid var(--border)' : 'none' }}>
                    <div style={{ fontSize: 12, fontWeight: 700 }}>{t.titulo}</div>
                    <div style={{ fontSize: 13, lineHeight: 1.5, color: 'var(--text)', marginTop: 2 }}>{t.texto}</div>
                  </div>
                ))}
              </div>
            )}
          </div>
        </div>
      </div>

      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))', gap: 18, alignItems: 'start' }}>
            {grupos.map(g => (
              <div key={g.id} className="card" style={{ padding: 16 }}>
                <div style={ctLbl}>{g.titulo}</div>
                <div style={{ fontSize: 15, fontWeight: 700, fontFamily: 'var(--font-display)', margin: '6px 0 4px' }}>{g.sub}</div>
                {g.campos.map(([label, valor]) => {
                  const texto = label === 'Stage' ? ctMostrar(valor, true) : ctMostrar(valor, false);
                  const vazio = !texto;
                  const novo = novos.includes(label);
                  return (
                    <div key={label} style={{ display: 'grid', gridTemplateColumns: '148px 1fr', gap: 8, padding: '8px 6px', borderTop: '1px solid var(--border)', background: novo ? 'color-mix(in oklch, var(--ai-500) 16%, transparent)' : 'transparent' }}>
                      <div style={{ fontSize: 12, color: 'var(--text-dim)' }}>{label}</div>
                      <div style={{ fontSize: 13, color: vazio ? 'var(--text-dim)' : 'var(--text)' }}>{vazio ? '—' : texto}</div>
                    </div>
                  );
                })}
              </div>
            ))}
      </div>

      {fase === 'fecho' && (
        <div>
          {guardado
            ? <div style={{ fontSize: 13, color: 'var(--success)' }}>Registo de teste {guardado} gravado neste módulo.</div>
            : <button className="btn btn-primary" onClick={guardar}>Guardar registo de teste</button>}
        </div>
      )}
      </div>
    </div>
  );
};

window.MktChamadaTesteScreen = MktChamadaTesteScreen;
