<?php /* views/pages/disparos_oficial.php */ ?>


<style>
  /* esconde a scrollbar só visualmente (a rolagem continua) */
  .hide-scrollbar::-webkit-scrollbar { display: none; }
  .hide-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }

  /* NAV do carrossel de pastas */
  .car-nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 20;
    width: 40px; height: 40px;
    border-radius: 9999px;
    display: grid; place-items: center;
    background: white;
    border: 1px solid rgba(0,0,0,.08);
    box-shadow: 0 2px 10px rgba(0,0,0,.10);
    cursor: pointer;
    transition: transform .15s ease, opacity .15s ease, box-shadow .15s ease;
  }
  .car-nav:hover { transform: translateY(-50%) scale(1.05); box-shadow: 0 4px 14px rgba(0,0,0,.14); }
  .car-nav:active { transform: translateY(-50%) scale(0.98); }
  .car-nav[disabled] { opacity: .35; cursor: default; transform: translateY(-50%); }

  .car-left { left: -4px; }
  .car-right { right: -4px; }

  .edge-fade {
    position: absolute; inset-y: 0; width: 48px; z-index: 15; pointer-events: none;
  }
  .edge-left  { left: 0;  background: linear-gradient(90deg, white 0%, rgba(255,255,255,0) 100%); }
  .edge-right { right: 0; background: linear-gradient(270deg, white 0%, rgba(255,255,255,0) 100%); }
</style>


<div class="p-6">
  <h1 class="text-2xl font-semibold mb-4">Disparos (API Oficial)</h1>

  <!-- Criar campanha -->
  <div class="bg-white rounded-lg p-4 shadow mb-6">
    <h2 class="text-lg font-medium mb-3">Nova campanha</h2>

    <form id="formDisparoOficial" class="grid grid-cols-1 md:grid-cols-2 gap-4">
      <div class="md:col-span-2">
        <label class="block text-sm mb-1">Nome da campanha</label>
        <input type="text" name="nome" class="w-full rounded border px-3 py-2" placeholder="Ex: Abertura Setembro" required>
      </div>

      <div>
        <label class="block text-sm mb-1">Delay mínimo (minutos)</label>
        <input type="number" name="min_delay" class="w-full rounded border px-3 py-2" value="6" min="1" required>
      </div>
      <div>
        <label class="block text-sm mb-1">Delay máximo (minutos)</label>
        <input type="number" name="max_delay" class="w-full rounded border px-3 py-2" value="15" min="1" required>
      </div>

      <!-- Janela -->
      <div class="md:col-span-2 grid grid-cols-1 md:grid-cols-3 gap-4">
        <div>
          <label class="block text-sm mb-1">Disparar entre às:</label>
          <input type="time" id="schStart" class="w-full rounded border px-3 py-2" value="09:00">
        </div>
        <div>
          <label class="block text-sm mb-1">Com término às:</label>
          <input type="time" id="schEnd" class="w-full rounded border px-3 py-2" value="18:00">
        </div>
        <div class="hidden">
          <label class="block text-sm mb-1">Timezone</label>
          <input type="text" id="schTz" class="w-full rounded border px-3 py-2" value="America/Sao_Paulo">
        </div>
      </div>

      <div class="md:col-span-2">
        <label class="block text-sm mb-1"><b class="text-red-600">Não disparar</b> nos dias da semana</label>
        <div class="flex flex-wrap gap-3 text-sm">
          <label class="inline-flex items-center gap-2"><input type="checkbox" class="schWD" value="0"> Dom</label>
          <label class="inline-flex items-center gap-2"><input type="checkbox" class="schWD" value="1"> Seg</label>
          <label class="inline-flex items-center gap-2"><input type="checkbox" class="schWD" value="2"> Ter</label>
          <label class="inline-flex items-center gap-2"><input type="checkbox" class="schWD" value="3"> Qua</label>
          <label class="inline-flex items-center gap-2"><input type="checkbox" class="schWD" value="4"> Qui</label>
          <label class="inline-flex items-center gap-2"><input type="checkbox" class="schWD" value="5"> Sex</label>
          <label class="inline-flex items-center gap-2"><input type="checkbox" class="schWD" value="6"> Sáb</label>
        </div>
        <div class="hidden mt-3">
          <label class="block text-sm mb-1">Não disparar nestas datas</label>
          <input type="text" id="schDates" class="w-full rounded border px-3 py-2" placeholder="Ex: 2025-12-25, 2026-01-01">
        </div>
      </div>

      <!-- Template -->
      <div class="md:col-span-2">
        <label class="block text-sm mb-1">Template (obrigatório na API oficial)</label>

        <div class="flex flex-wrap items-center gap-2">
          <button type="button" id="btnPickTemplate" class="rounded bg-indigo-600 hover:bg-indigo-500 text-white px-4 py-2">
            Selecionar Template…
          </button>

          <span id="templatePreview" class="text-sm text-gray-700">Nenhum template selecionado</span>
        </div>

        <input type="hidden" id="templateId" value="">
        <input type="hidden" id="templateSnapshot" value="">
        <input type="hidden" id="templateVars" value="">
      </div>

      <!-- Leads -->
      <div class="md:col-span-2">
        <button type="button" id="btnPickLeads" class="rounded bg-gray-800 hover:bg-gray-700 text-white px-4 py-2">
          Selecionar leads…
        </button>
        <span id="pickCount" class="ml-2 text-sm text-gray-600">Nenhum lead selecionado</span>
      </div>

      <div class="md:col-span-2 flex items-center gap-3">
        <button type="submit" id="btnCreateCamp" class="rounded bg-emerald-600 hover:bg-emerald-500 text-white px-4 py-2">
          Criar campanha (Oficial)
        </button>
        <span id="createMsg" class="text-sm text-gray-600"></span>
      </div>
    </form>
  </div>

  <!-- Lista -->
  <div class="bg-white rounded-lg p-4 shadow">
    <div class="flex items-center justify-between mb-3">
      <h2 class="text-lg font-medium">Minhas campanhas</h2>
      <button id="btnReload" class="rounded bg-gray-800 hover:bg-gray-700 text-white px-3 py-2 text-sm">Atualizar</button>
    </div>

    <div class="overflow-x-auto">
      <table class="min-w-full text-sm">
        <thead>
          <tr class="text-left border-b">
            <th class="py-2 pr-3">Nome</th>
            <th class="py-2 pr-3">Template</th>
            <th class="py-2 pr-3">Status</th>
            <th class="py-2 pr-3">Total</th>
            <th class="py-2 pr-3">Enviados</th>
            <th class="py-2 pr-3">Erros</th>
            <th class="py-2 pr-3">Sem WhatsApp</th>
            <th class="py-2 pr-3">Sincronizado</th>
            <th class="py-2">Ações</th>
          </tr>
        </thead>
        <tbody id="tbodyCamps"></tbody>
      </table>
    </div>
  </div>
</div>

<!-- Modal Templates -->
<div id="tplModal" class="fixed inset-0 bg-black/40 hidden z-50">
  <div class="absolute inset-0 flex items-center justify-center p-4">
    <div class="bg-white rounded-2xl shadow-xl w-full max-w-3xl p-4 border border-gray-200">
      <div class="flex items-center justify-between mb-3">
        <h3 class="text-lg font-semibold">Selecionar Template</h3>
        <button id="closeTplModal" class="text-gray-600 hover:text-black" aria-label="Fechar">&times;</button>
      </div>

      <div class="mb-3">
        <input id="tplSearch" class="w-full rounded border px-3 py-2" placeholder="Buscar template...">
      </div>

      <div class="overflow-auto max-h-[60vh] border rounded-lg">
        <table class="min-w-full text-sm">
          <thead class="text-left sticky top-0 bg-white border-b">
            <tr>
              <th class="py-2 px-3">Nome</th>
              <th class="py-2 px-3">Idioma</th>
              <th class="py-2 px-3">Categoria</th>
              <th class="py-2 px-3">Ação</th>
            </tr>
          </thead>
          <tbody id="tplRows"></tbody>
        </table>
      </div>

      <div class="mt-4 flex items-center justify-end gap-2">
        <button id="tplCancel" class="rounded border px-3 py-2 text-sm">Cancelar</button>
      </div>
    </div>
  </div>
</div>

<!-- Modal Variáveis do Template -->
<div id="varsModal" class="fixed inset-0 bg-black/40 hidden z-50">
  <div class="absolute inset-0 flex items-center justify-center p-4">
    <div class="bg-white rounded-2xl shadow-xl w-full max-w-2xl p-4 border border-gray-200">
      <div class="flex items-center justify-between mb-3">
        <div>
          <h3 class="text-lg font-semibold">Configurar variáveis</h3>
          <p class="text-xs text-gray-500" id="varsModalSubtitle">—</p>
        </div>
        <button id="closeVarsModal" class="text-gray-600 hover:text-black" aria-label="Fechar">&times;</button>
      </div>

      <div id="varsBody" class="space-y-3"></div>

      <div class="mt-4 flex items-center justify-between gap-2">
        <span class="text-xs text-gray-500">Dica: você pode usar valores fixos ou placeholders que o n8n vai trocar depois.</span>
        <div class="flex gap-2">
          <button id="varsCancel" class="rounded border px-3 py-2 text-sm">Cancelar</button>
          <button id="varsSave" class="rounded bg-indigo-600 hover:bg-indigo-500 text-white px-4 py-2 text-sm">Salvar variáveis</button>
        </div>
      </div>
    </div>
  </div>
</div>

<!-- Modal seleção de leads (com pastas) -->
<div id="leadModal" class="fixed inset-0 bg-black/40 hidden z-50">
  <div class="absolute inset-0 flex items-center justify-center p-4">
    <div class="bg-white rounded-2xl shadow-xl w-full max-w-5xl p-4 border border-gray-200">
      <div class="flex items-center justify-between mb-3">
        <h3 class="text-lg font-semibold">Selecionar leads</h3>
        <button id="closeLeadModal" class="text-gray-600 hover:text-black" aria-label="Fechar">&times;</button>
      </div>

      <!-- Pastas (carrossel horizontal) -->
      <div class="mb-3">
        <div class="mb-2 text-xs text-gray-500">Pastas</div>
        <div class="relative">
          <div class="edge-fade edge-left"></div>
          <div class="edge-fade edge-right"></div>

          <button id="mFprev" class="car-nav car-left" title="Anterior" aria-label="Anterior">
            <svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2">
              <path d="M15 18l-6-6 6-6"/>
            </svg>
          </button>
          <button id="mFnext" class="car-nav car-right" title="Próxima" aria-label="Próxima">
            <svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2">
              <path d="M9 6l6 6-6 6"/>
            </svg>
          </button>

          <div id="mFoldersViewport" class="overflow-x-auto hide-scrollbar px-6">
            <div id="mFoldersBar" class="flex gap-3 pr-6 snap-x snap-mandatory"></div>
          </div>
        </div>
      </div>

      <div class="flex items-center gap-2 mb-3">
        <label class="text-sm">Itens por página</label>
        <select id="mPageSize" class="rounded border px-2 py-1 text-sm">
          <option>10</option>
          <option selected>20</option>
          <option>50</option>
        </select>
        <div class="ml-auto flex gap-2">
          <button id="mPrev" class="rounded border px-3 py-1.5 text-sm">Anterior</button>
          <span id="mPageInfo" class="text-sm text-gray-600 self-center">1</span>
          <button id="mNext" class="rounded border px-3 py-1.5 text-sm">Próxima</button>
        </div>
      </div>

      <div class="overflow-x-auto max-h-[60vh]">
        <table class="min-w-full text-sm">
          <thead class="text-left">
            <tr>
              <th class="py-2 pr-3 w-10"><input id="mCheckAll" class="size-4" type="checkbox"></th>
              <th class="py-2 pr-3">Empresa</th>
              <th class="py-2 pr-3">Telefone</th>
              <th class="py-2 pr-3">Criado</th>
            </tr>
          </thead>
          <tbody id="mLeadRows"></tbody>
        </table>
      </div>

      <div class="mt-4 flex items-center justify-between">
        <span id="mSelCount" class="text-sm text-gray-700">0 selecionados</span>
        <div class="flex gap-2">
          <button id="mClear" class="rounded border px-3 py-2 text-sm">Limpar</button>
          <button id="mConfirm" class="rounded bg-indigo-600 hover:bg-indigo-500 text-white px-4 py-2 text-sm">Usar selecionados</button>
        </div>
      </div>
    </div>
  </div>
</div>
<!-- Modal Detalhes da campanha -->
<div id="detModal" class="fixed inset-0 z-50 hidden">
  <div class="absolute inset-0 bg-black/50 backdrop-blur-sm"></div>
  <div class="absolute inset-0 flex items-center justify-center p-4">
    <div class="w-full max-w-5xl rounded-2xl bg-white shadow-xl ring-1 ring-black/5">
      <div class="px-5 py-4 border-b flex items-center justify-between">
        <h3 class="text-lg font-semibold">Detalhes da campanha</h3>
        <button id="detClose" class="text-gray-600 hover:text-black text-2xl leading-none" aria-label="Fechar">&times;</button>
      </div>

      <div class="p-5">
        <!-- Header/resumo -->
        <div id="detHeader" class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4"></div>

        <!-- Badges -->
        <div id="detBadges" class="flex flex-wrap gap-2 mb-4"></div>

        <!-- Tabela de leads (paginada) -->
        <div class="border rounded-lg">
          <div class="px-3 py-2 border-b flex items-center justify-between">
            <div class="text-sm font-medium">Leads da campanha</div>
            <div class="flex items-center gap-2">
              <label class="text-xs">Itens por página</label>
              <select id="detPageSize" class="rounded border px-2 py-1 text-xs">
                <option>10</option>
                <option selected>20</option>
                <option>50</option>
                <option>100</option>
              </select>
              <button id="detPrev" class="rounded border px-2 py-1 text-xs">Anterior</button>
              <span id="detPageInfo" class="text-xs text-gray-600">1</span>
              <button id="detNext" class="rounded border px-2 py-1 text-xs">Próxima</button>
            </div>
          </div>
          <div class="overflow-x-auto max-h-[60vh]">
            <table class="min-w-full text-sm">
              <thead class="text-left sticky top-0 bg-white">
                <tr>
                  <th class="py-2 px-3">ID</th>
                  <th class="py-2 px-3">Empresa</th>
                  <th class="py-2 px-3">Telefone</th>
                  <th class="py-2 px-3">Status</th>
                  <th class="py-2 px-3">Criado</th>
                  <th class="py-2 px-3">Pasta</th>
                </tr>
              </thead>
              <tbody id="detRows"></tbody>
            </table>
          </div>
        </div>
      </div>

      <div class="px-5 py-4 border-t flex items-center justify-end">
        <button id="detClose2" class="px-3 py-2 rounded border">Fechar</button>
      </div>
    </div>
  </div>
</div>



<!-- Modal Confirmação (Ações) -->
<div id="confirmModal" class="fixed inset-0 hidden z-50">
  <div class="absolute inset-0 bg-black/50 backdrop-blur-sm"></div>

  <div class="absolute inset-0 flex items-center justify-center p-4">
    <div class="w-full max-w-md rounded-2xl bg-white shadow-xl ring-1 ring-black/5">
      <div class="px-5 py-4 border-b flex items-start gap-3">
        <div id="confirmIcon" class="mt-0.5 size-10 rounded-full bg-gray-100 flex items-center justify-center text-gray-700">
          <!-- ícone -->
          <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
            <path d="M12 9v4"></path>
            <path d="M12 17h.01"></path>
            <path d="M10.3 3.6h3.4a2 2 0 0 1 1.7 1l7.1 12.3a2 2 0 0 1-1.7 3H3.2a2 2 0 0 1-1.7-3L8.6 4.6a2 2 0 0 1 1.7-1z"></path>
          </svg>
        </div>

        <div class="flex-1">
          <div id="confirmTitle" class="text-base font-semibold text-gray-900">Confirmar ação</div>
          <div id="confirmDesc" class="mt-1 text-sm text-gray-600">Tem certeza?</div>
        </div>

        <button id="confirmClose" class="text-gray-500 hover:text-black text-2xl leading-none" aria-label="Fechar">&times;</button>
      </div>

      <div class="px-5 py-4">
        <div id="confirmHint" class="text-xs text-gray-500">
          Você pode cancelar caso tenha clicado sem querer.
        </div>
      </div>

      <div class="px-5 py-4 border-t flex items-center justify-end gap-2">
        <button id="confirmCancel" class="rounded-lg border px-3 py-2 text-sm hover:bg-gray-50">
          Cancelar
        </button>
        <button id="confirmOk" class="rounded-lg bg-gray-900 text-white px-4 py-2 text-sm hover:bg-black">
          Confirmar
        </button>
      </div>
    </div>
  </div>
</div>

<!-- Modal Loading (executando ação) -->
<div id="actionModal" class="fixed inset-0 hidden z-50">
  <div class="absolute inset-0 bg-black/50"></div>
  <div class="absolute inset-0 flex items-center justify-center p-4">
    <div class="w-full max-w-md rounded-2xl bg-white shadow-xl border border-gray-200 p-5">
      <div class="flex items-center gap-3">
        <div class="size-10 rounded-full bg-indigo-50 flex items-center justify-center text-indigo-700">
          <svg class="animate-spin" width="22" height="22" viewBox="0 0 24 24" fill="none">
            <circle cx="12" cy="12" r="9" stroke="currentColor" stroke-width="3" opacity="0.2"></circle>
            <path d="M21 12a9 9 0 0 0-9-9" stroke="currentColor" stroke-width="3" stroke-linecap="round"></path>
          </svg>
        </div>
        <div>
          <div id="actionTitle" class="text-base font-semibold text-gray-900">Aguarde…</div>
          <div id="actionDesc" class="text-sm text-gray-600">Processando sua solicitação</div>
        </div>
      </div>

      <div id="actionMsg" class="mt-4 text-sm text-gray-700"></div>

      <div class="mt-4 flex items-center justify-end">
        <button id="actionClose" class="hidden rounded-lg border px-3 py-2 text-sm hover:bg-gray-50">
          Fechar
        </button>
      </div>
    </div>
  </div>
</div>

















<script>
/* =========================================================
 * Utils
 * ======================================================= */
function b64EncodeUnicode(str) { return btoa(unescape(encodeURIComponent(str))); }
function b64DecodeUnicode(str) { return decodeURIComponent(escape(atob(str))); }
function escHtml(s){
  return String(s||'')
    .replaceAll('&','&amp;')
    .replaceAll('<','&lt;')
    .replaceAll('>','&gt;')
    .replaceAll('"','&quot;')
    .replaceAll("'","&#039;");
}

/* ===== Helpers para badges ===== */
function badge(label, tone='gray'){
  const colors = {
    gray:   'bg-gray-100 text-gray-800',
    blue:   'bg-blue-100 text-blue-800',
    green:  'bg-emerald-100 text-emerald-800',
    red:    'bg-rose-100 text-rose-800',
    amber:  'bg-amber-100 text-amber-800',
    indigo: 'bg-indigo-100 text-indigo-800',
    violet: 'bg-violet-100 text-violet-800'
  };
  return `<span class="inline-flex items-center gap-1 px-2 py-1 rounded text-xs ${colors[tone]||colors.gray}">${label}</span>`;
}

function fmtTimeRange(sch){
  if (!sch) return '—';
  const start = sch.start || '—';
  const end   = sch.end   || '—';
  return `${start} – ${end}`;
}

function fmtWeekdays(blocked){
  const map = ['Dom','Seg','Ter','Qua','Qui','Sex','Sáb'];
  const arr = (blocked||[]).map(n => map[Number(n)]).filter(Boolean);
  return arr.length ? arr.join(', ') : 'Nenhum';
}

/* ===== Template Vars helpers ===== */
function extractTemplateVars(template){
  const components = Array.isArray(template?.components) ? template.components : [];
  const texts = components
    .filter(c => c && (c.type === 'BODY' || c.type === 'HEADER' || c.type === 'FOOTER'))
    .map(c => String(c.text || ''));

  const all = texts.join('\n');

  const pos = [...all.matchAll(/\{\{\s*(\d+)\s*\}\}/g)].map(m => m[1]);
  const posUnique = [...new Set(pos)].sort((a,b)=>Number(a)-Number(b)).map(n => `{{${n}}}`);

  const named = [...all.matchAll(/\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}/g)].map(m => m[1]);
  const namedUnique = [...new Set(named)].sort().map(k => `{{${k}}}`);

  const fmt = String(template?.parameter_format || '').toUpperCase();
  if (fmt === 'NAMED') return { format:'NAMED', vars: namedUnique };
  if (fmt === 'POSITIONAL') return { format:'POSITIONAL', vars: posUnique };

  if (posUnique.length) return { format:'POSITIONAL', vars: posUnique };
  if (namedUnique.length) return { format:'NAMED', vars: namedUnique };

  return { format:'NONE', vars: [] };
}

(function(){
  const API = 'api/disparos_oficial.php';
  const API_LEADS = 'api/leads.php';

  // ====== modal sync leads ======
  const syncModal = document.getElementById('syncModal');
  const syncModalMsg = document.getElementById('syncModalMsg');

  function openSyncModal(text){
    syncModalMsg.textContent = text || '';
    syncModalMsg.className = 'text-sm text-gray-700';
    syncModal.classList.remove('hidden');
  }
  function closeSyncModal(){
    syncModal.classList.add('hidden');
    syncModalMsg.textContent = '';
  }
  async function syncLeads(campId){
    openSyncModal('Aguarde, sincronizando leads com Evolua Chat…');

    const body = new URLSearchParams({ id:String(campId) });

    const r = await fetch(`${API}?action=sync_leads`, {
      method:'POST',
      headers:{ 'Content-Type':'application/x-www-form-urlencoded' },
      body,
      credentials:'same-origin'
    });

    const t = await r.text();
    let j = null;
    try { j = JSON.parse(t); } catch {}

    if(!r.ok || !j || !j.ok){
      const err = j?.error || t || 'Falha ao sincronizar';
      syncModalMsg.className = 'text-sm text-rose-700';
      syncModalMsg.textContent = err;
      setTimeout(closeSyncModal, 2000);
      throw new Error(err);
    }

    syncModalMsg.className = 'text-sm text-emerald-700';
    syncModalMsg.textContent = '✓ Sincronizado com sucesso!';
    setTimeout(closeSyncModal, 800);
    return j;
  }

  // ====== seleção leads ======
  const sel = new Set();
  const pickCount = document.getElementById('pickCount');
  const mSelCount = document.getElementById('mSelCount');

  function updateSelCount(){
    mSelCount.textContent = `${sel.size} selecionados`;
    pickCount.textContent = sel.size ? `${sel.size} lead(s) selecionado(s)` : 'Nenhum lead selecionado';
  }

  async function fetchJSON(url, opts={}){
    const r = await fetch(url, { credentials:'same-origin', cache:'no-store', ...opts });
    const t = await r.text();
    let j; try{ j = JSON.parse(t); }catch(e){}
    if(!r.ok || !j) return { ok:false, error: j?.error || t || 'Falha' };
    return j;
  }

// ====== modal leads (com pastas) ======
  const leadModal = document.getElementById('leadModal');
  const btnPickLeads = document.getElementById('btnPickLeads');
  const closeLeadModal = document.getElementById('closeLeadModal');
  const mRows = document.getElementById('mLeadRows');
  const mCheckAll = document.getElementById('mCheckAll');
  const mPrev = document.getElementById('mPrev');
  const mNext = document.getElementById('mNext');
  const mPageInfo = document.getElementById('mPageInfo');
  const mPageSize = document.getElementById('mPageSize');
  const mClear = document.getElementById('mClear');
  const mConfirm = document.getElementById('mConfirm');

  const mFoldersViewport = document.getElementById('mFoldersViewport');
  const mFprev = document.getElementById('mFprev');
  const mFnext = document.getElementById('mFnext');
  const mFoldersBar = document.getElementById('mFoldersBar');

  let mPage=1, mTotal=0, mSize=parseInt(mPageSize.value,10);
  let mCurFolder = '';       // '', 'none' ou id (string)
  let mFoldersCache = [];    // [{id,name,count}]
  let mNoneCount = 0;

  const nextFrame = () => new Promise(res =>
    requestAnimationFrame(() => requestAnimationFrame(res))
  );

  function mUpdateFolderArrows(){
    if (!mFoldersViewport) return;
    const atStart = mFoldersViewport.scrollLeft <= 2;
    const atEnd   = (mFoldersViewport.scrollLeft + mFoldersViewport.clientWidth) >= (mFoldersViewport.scrollWidth - 2);
    if (mFprev) mFprev.disabled = atStart;
    if (mFnext) mFnext.disabled = atEnd;
  }

  function mScrollFolders(dir){
    if (!mFoldersViewport) return;
    const vw = mFoldersViewport.getBoundingClientRect().width || mFoldersViewport.clientWidth || 0;
    const step = Math.max(240, Math.round(vw * 0.9));
    mFoldersViewport.scrollBy({ left: dir * step, behavior: 'smooth' });
    setTimeout(mUpdateFolderArrows, 160);
  }

  mFprev?.addEventListener('click', () => mScrollFolders(-1));
  mFnext?.addEventListener('click', () => mScrollFolders(+1));
  mFoldersViewport?.addEventListener('scroll', mUpdateFolderArrows);
  mFoldersViewport?.addEventListener('wheel', (e) => {
    if (Math.abs(e.deltaY) > Math.abs(e.deltaX)) {
      e.preventDefault();
      mFoldersViewport.scrollLeft += e.deltaY;
      mUpdateFolderArrows();
    }
  }, { passive: false });
  window.addEventListener('resize', mUpdateFolderArrows);

  async function mLoadFolders(){
    const d = await fetchJSON(`${API_LEADS}?action=folders.list`);
    if (!d.ok) return;
    const counts = d.counts || {};
    mFoldersCache = (d.folders || []).map(f => ({ id:String(f.id), name:f.name, count: counts[String(f.id)] || 0 }));
    mNoneCount = d.none || 0;
    mRenderFoldersBar();
    await nextFrame();
    mUpdateFolderArrows();
  }

  function mFolderIcon(){
    return `
      <svg viewBox="0 0 24 24" fill="none" class="w-full h-full">
        <path d="M3.5 6.75A1.75 1.75 0 0 1 5.25 5h4.19c.46 0 .9.19 1.21.52l1.1 1.13c.31.33.75.52 1.21.52h4.79A1.75 1.75 0 0 1 19.5 9v7.25A2.75 2.75 0 0 1 16.75 19h-9.5A2.75 2.75 0 0 1 4.5 16.25v-8.5Z" stroke="currentColor" class="text-indigo-500" stroke-width="1.5"/>
        <path d="M4.5 9.75h15" stroke="currentColor" stroke-width="1.5" class="text-indigo-400"/>
      </svg>
    `;
  }

  function mRenderFoldersBar(){
    mFoldersBar.innerHTML = '';
    const tile = (label, count, value) => {
      const active = (mCurFolder === value) || (value==='' && mCurFolder==='');
      return `
        <button data-value="${value}"
          class="shrink-0 snap-start group flex flex-col justify-between items-start p-3 rounded-2xl border
                 ${active ? 'border-indigo-500 bg-indigo-50' : 'border-gray-200 hover:border-indigo-400'}
                 w-44 h-28 transition">
          <span class="w-12 h-10 text-indigo-500">${mFolderIcon()}</span>
          <div>
            <div class="text-sm font-medium leading-tight">${escHtml(label)}</div>
            <div class="text-xs text-gray-500">${count} lead(s)</div>
          </div>
        </button>
      `;
    };
    const total = mFoldersCache.reduce((s,f)=>s+f.count, 0) + mNoneCount;
    mFoldersBar.insertAdjacentHTML('beforeend', tile('Todas', total, ''));
    mFoldersBar.insertAdjacentHTML('beforeend', tile('Sem pasta', mNoneCount, 'none'));
    mFoldersCache.forEach(f => mFoldersBar.insertAdjacentHTML('beforeend', tile(f.name, f.count, f.id)));

    mFoldersBar.querySelectorAll('button').forEach(btn => {
      btn.addEventListener('click', async e => {
        mCurFolder = e.currentTarget.getAttribute('data-value') || '';
        mRenderFoldersBar();
        await mLoadPage(1);
        mUpdateFolderArrows();
      });
    });
  }

  async function mLoadPage(page=1){
    mPage = page;
    mSize = parseInt(mPageSize.value,10);

    const qs = new URLSearchParams({ action:'list', page:String(mPage), limit:String(mSize) });
    if (mCurFolder !== '') qs.set('folder_id', mCurFolder);

    const d = await fetchJSON(`${API_LEADS}?${qs.toString()}`);
    if(!d.ok){
      mRows.innerHTML = `<tr><td class="py-3" colspan="4">Erro: ${escHtml(d.error)}</td></tr>`;
      return;
    }

    mTotal = d.total || 0;
    const last = Math.max(1, Math.ceil(mTotal / mSize));
    mPageInfo.textContent = `${d.page} / ${last}`;
    mPrev.disabled = (d.page <= 1);
    mNext.disabled = (d.page >= last);

    mRows.innerHTML = (d.data||[]).map(it=>{
      const checked = sel.has(it.id) ? 'checked' : '';
      return `
        <tr class="border-t">
          <td class="py-2 pr-3"><input type="checkbox" data-id="${it.id}" class="mRow size-4" ${checked}></td>
          <td class="py-2 pr-3">${escHtml(it.company || '—')}</td>
          <td class="py-2 pr-3">${escHtml(it.phone || '—')}</td>
          <td class="py-2 pr-3">${it.created_at ? new Date(it.created_at).toLocaleString() : '—'}</td>
        </tr>
      `;
    }).join('') || `<tr><td class="py-3" colspan="4">Nenhum lead</td></tr>`;

    mRows.querySelectorAll('.mRow').forEach(cb=>{
      cb.addEventListener('change', e=>{
        const id = parseInt(e.target.getAttribute('data-id'),10);
        if(e.target.checked) sel.add(id); else sel.delete(id);
        updateSelCount();
        mUpdateMaster();
      });
    });

    mUpdateMaster();
  }

  function mUpdateMaster(){
    const boxes = mRows.querySelectorAll('.mRow');
    const total = boxes.length;
    const checked = [...boxes].filter(b=>b.checked).length;
    mCheckAll.checked = (total>0 && checked===total);
    mCheckAll.indeterminate = (checked>0 && checked<total);
  }

  btnPickLeads.addEventListener('click', async ()=>{
    leadModal.classList.remove('hidden');
    await nextFrame();
    await mLoadFolders();
    await mLoadPage(1);
    if (mFoldersViewport) {
      mFoldersViewport.scrollLeft += 1;
      mFoldersViewport.scrollLeft -= 1;
    }
    mUpdateFolderArrows();
    updateSelCount();
  });
  closeLeadModal.addEventListener('click', ()=> leadModal.classList.add('hidden'));
  mConfirm.addEventListener('click', ()=> { leadModal.classList.add('hidden'); updateSelCount(); });
  mPrev.addEventListener('click', ()=> mLoadPage(mPage-1));
  mNext.addEventListener('click', ()=> mLoadPage(mPage+1));
  mPageSize.addEventListener('change', ()=> mLoadPage(1));
  mClear.addEventListener('click', ()=> { sel.clear(); updateSelCount(); mLoadPage(mPage); });

  mCheckAll.addEventListener('change', ()=>{
    const boxes = mRows.querySelectorAll('.mRow');
    boxes.forEach(cb=>{
      const id = parseInt(cb.getAttribute('data-id'),10);
      cb.checked = mCheckAll.checked;
      if(mCheckAll.checked) sel.add(id); else sel.delete(id);
    });
    updateSelCount();
    mUpdateMaster();
  });
  // ====== modal templates ======
  const tplModal = document.getElementById('tplModal');
  const btnPickTemplate = document.getElementById('btnPickTemplate');
  const closeTplModal = document.getElementById('closeTplModal');
  const tplCancel = document.getElementById('tplCancel');
  const tplRows = document.getElementById('tplRows');
  const tplSearch = document.getElementById('tplSearch');

  const templateIdEl = document.getElementById('templateId');
  const templateSnapshotEl = document.getElementById('templateSnapshot');
  const templatePreview = document.getElementById('templatePreview');
  const templateVarsEl = document.getElementById('templateVars');

  const varsModal = document.getElementById('varsModal');
  const varsBody = document.getElementById('varsBody');
  const varsSave = document.getElementById('varsSave');
  const varsCancel = document.getElementById('varsCancel');
  const closeVarsModal = document.getElementById('closeVarsModal');
  const varsModalSubtitle = document.getElementById('varsModalSubtitle');

  let __pendingTemplate = null;
  let tplCache = [];

  function openVarsModal(template){
    const info = extractTemplateVars(template);
    varsBody.innerHTML = '';
    varsModalSubtitle.textContent = `${template?.name || 'Template'} • ${template?.language || '-'} • ${info.format}`;

    if (!info.vars.length){
      templateVarsEl.value = '';
      return;
    }

    info.vars.forEach(v => {
      const id = `var_${v.replaceAll('{','').replaceAll('}','').replaceAll(':','_')}`;
      varsBody.insertAdjacentHTML('beforeend', `
        <div class="rounded-lg border p-3">
          <div class="text-sm font-medium">${escHtml(v)}</div>
          <input
            data-var="${escHtml(v)}"
            id="${escHtml(id)}"
            class="mt-2 w-full rounded border px-3 py-2 text-sm"
            placeholder="Ex: Andrew / 123 / {{lead.nome}} / etc"
          />
          <div class="text-xs text-gray-500 mt-1">Valor que será enviado junto no webhook.</div>
        </div>
      `);
    });

    let prev = null;
    try { prev = templateVarsEl.value ? JSON.parse(templateVarsEl.value) : null; } catch {}
    if (prev && prev.values){
      varsBody.querySelectorAll('input[data-var]').forEach(inp=>{
        const k = inp.getAttribute('data-var');
        if (k in prev.values) inp.value = String(prev.values[k] ?? '');
      });
    }

    varsModal.classList.remove('hidden');
  }

  function closeVars(){ varsModal.classList.add('hidden'); }
  closeVarsModal.addEventListener('click', closeVars);
  varsCancel.addEventListener('click', closeVars);
  varsModal.addEventListener('click', (e)=>{ if(e.target === varsModal) closeVars(); });

  function renderTpl(list){
    const q = (tplSearch.value||'').trim().toLowerCase();
    const filtered = !q ? list : list.filter(t => {
      const name = String(t.name||'').toLowerCase();
      const lang = String(t.language||'').toLowerCase();
      const cat  = String(t.category||'').toLowerCase();
      return name.includes(q) || lang.includes(q) || cat.includes(q);
    });

    tplRows.innerHTML = filtered.map(t=>{
      const snap = t.snapshot || null;
      const snapStr = snap ? JSON.stringify(snap) : '';
      const snapB64 = snapStr ? b64EncodeUnicode(snapStr) : '';
      const preview = t.body_preview ? `<div class="text-xs text-gray-500 mt-1">${escHtml(t.body_preview)}</div>` : '';
      const badge = (txt) => `<span class="inline-flex items-center rounded-full border px-2 py-0.5 text-xs text-gray-700">${escHtml(txt)}</span>`;

      return `
        <tr class="border-t align-top">
          <td class="py-2 px-3">
            <div class="font-medium">${escHtml(t.name || '—')}</div>
            ${preview}
          </td>
          <td class="py-2 px-3">
            ${badge(t.language || '—')}
          </td>
          <td class="py-2 px-3">
            <div class="flex flex-wrap gap-1">
              ${badge(t.category || '—')}
              ${t.sub_category ? badge(t.sub_category) : ''}
            </div>
          </td>
          <td class="py-2 px-3">
            <div class="flex items-center gap-2">
              ${badge(t.status || '—')}
              <button
                class="pickTpl px-3 py-1 rounded bg-indigo-600 hover:bg-indigo-500 text-white"
                data-id="${escHtml(t.id)}"
                data-snapb64="${snapB64}"
                data-name="${escHtml(t.name || '')}"
                data-lang="${escHtml(t.language || '')}"
              >Selecionar</button>
            </div>
          </td>
        </tr>
      `;
    }).join('') || `<tr><td class="py-3 px-3" colspan="4">Nenhum template encontrado</td></tr>`;

    tplRows.querySelectorAll('.pickTpl').forEach(btn=>{
      btn.addEventListener('click', (e)=>{
        const id = e.currentTarget.getAttribute('data-id') || '';
        const name = e.currentTarget.getAttribute('data-name') || '';
        const lang = e.currentTarget.getAttribute('data-lang') || '';
        const snapB64 = e.currentTarget.getAttribute('data-snapb64') || '';

        let snap = null;
        if (snapB64) {
          try { snap = JSON.parse(b64DecodeUnicode(snapB64)); } catch {}
        }

        templateIdEl.value = id;
        templateSnapshotEl.value = snap ? JSON.stringify(snap) : '';
        templatePreview.textContent = name ? `${name} (${lang || '-'})` : 'Nenhum template selecionado';

        templateVarsEl.value = '';
        tplModal.classList.add('hidden');

        if (snap){
          const info = extractTemplateVars(snap);
          if (info.vars.length){
            __pendingTemplate = snap;
            openVarsModal(snap);
          }
        }
      });
    });
  }

  varsSave.addEventListener('click', ()=>{
    if (!__pendingTemplate){
      closeVars();
      return;
    }

    const info = extractTemplateVars(__pendingTemplate);
    const values = {};

    varsBody.querySelectorAll('input[data-var]').forEach(inp=>{
      const k = inp.getAttribute('data-var');
      values[k] = (inp.value || '').trim();
    });

    const missing = info.vars.filter(v => !values[v]);
    if (missing.length){
      alert(`Preencha as variáveis: ${missing.join(', ')}`);
      return;
    }

    templateVarsEl.value = JSON.stringify({ format: info.format, values });
    closeVars();
  });

  btnPickTemplate.addEventListener('click', async ()=>{
    tplModal.classList.remove('hidden');
    tplRows.innerHTML = `<tr><td class="py-3 px-3" colspan="4">Carregando templates...</td></tr>`;

    const d = await fetchJSON(`${API}?action=templates.list&_=${Date.now()}`);
    if(!d.ok){
      tplRows.innerHTML = `<tr><td class="py-3 px-3" colspan="4">Erro: ${escHtml(d.error || 'Falha')}</td></tr>`;
      tplCache = [];
      return;
    }

    tplCache = Array.isArray(d.data) ? d.data : [];
    renderTpl(tplCache);
  });

  closeTplModal.addEventListener('click', ()=> tplModal.classList.add('hidden'));
  tplCancel.addEventListener('click', ()=> tplModal.classList.add('hidden'));
  tplModal.addEventListener('click', (e)=>{ if(e.target === tplModal) tplModal.classList.add('hidden'); });
  tplSearch.addEventListener('input', ()=> renderTpl(tplCache));

  // ====== modal detalhes ======
  const detModal = document.getElementById('detModal');
  const detClose = document.getElementById('detClose');
  const detClose2 = document.getElementById('detClose2');
  const detHeader = document.getElementById('detHeader');
  const detBadges = document.getElementById('detBadges');
  const detRows = document.getElementById('detRows');
  const detPageSize = document.getElementById('detPageSize');
  const detPrev = document.getElementById('detPrev');
  const detNext = document.getElementById('detNext');
  const detPageInfo = document.getElementById('detPageInfo');

  let detState = { id: null, page: 1, limit: 20, total: 0 };

  function openDetModal(){ detModal.classList.remove('hidden'); }
  function closeDetModal(){ detModal.classList.add('hidden'); detState = { id:null, page:1, limit:20, total:0 }; }

  detClose?.addEventListener('click', closeDetModal);
  detClose2?.addEventListener('click', closeDetModal);
  detModal?.addEventListener('click', e => { 
    if (e.target === detModal || e.target.classList.contains('backdrop-blur-sm')) closeDetModal(); 
  });
  document.addEventListener('keydown', e => { 
    if (!detModal.classList.contains('hidden') && e.key==='Escape') closeDetModal(); 
  });

  async function loadDetailsPage(id, page, limit){
    const qs = new URLSearchParams({ action:'details', id:String(id), page:String(page), limit:String(limit) });
    const d = await fetchJSON(`${API}?${qs.toString()}`);
    
    if (!d.ok) { 
      detHeader.innerHTML = `<div class="text-sm text-rose-700">${escHtml(d.error||'Falha ao carregar')}</div>`; 
      return; 
    }

    const c   = d.campaign || {};
    const sch = c.schedule || null;
    const snap = c.template_snapshot || null;
    const vars = c.template_vars || null;
    const st  = d.stats || {};
    const L   = d.leads || { page:1, limit:limit, total:0, data:[] };

    // Header resumido
    detHeader.innerHTML = `
      <div class="bg-gray-50 rounded-lg p-3">
        <div class="text-xs text-gray-500">Nome</div>
        <div class="text-sm font-medium">${escHtml(c.nome||'—')}</div>
        <div class="mt-2 grid grid-cols-2 gap-2 text-xs text-gray-600">
          <div><span class="text-gray-500">ID:</span> ${c.id||'—'}</div>
          <div><span class="text-gray-500">Criado:</span> ${c.created_at ? new Date(c.created_at).toLocaleString() : '—'}</div>
        </div>
      </div>
      <div class="bg-gray-50 rounded-lg p-3">
        <div class="text-xs text-gray-500">Template</div>
        <div class="text-sm font-medium">${snap?.name ? escHtml(snap.name) : 'Template Oficial'}</div>
        ${snap?.language ? `<div class="text-xs text-gray-600 mt-1">Idioma: ${escHtml(snap.language)}</div>` : ''}
      </div>
    `;

    // Badges (status + janela + bloqueios + vars)
    const badges = [];
    badges.push(badge(`status: ${c.status||'—'}`, c.status==='running'?'green':(c.status==='paused'?'amber':'gray')));
    badges.push(badge(`janela: ${fmtTimeRange(sch)}`, 'indigo'));
    if (sch?.blocked_weekdays?.length) badges.push(badge(`não disparar: ${fmtWeekdays(sch.blocked_weekdays)}`, 'violet'));
    if (sch?.blocked_dates?.length) badges.push(badge(`datas vetadas: ${sch.blocked_dates.join(', ')}`, 'violet'));
    if (vars && Object.keys(vars.values || {}).length) badges.push(badge(`variáveis: ${Object.keys(vars.values).length}`, 'blue'));
    
    // contadores
    badges.push(badge(`Total itens: ${st.total||0}`, 'gray'));
    badges.push(badge(`Pendentes: ${st.pending||0}`, 'gray'));
    badges.push(badge(`Enviados: ${st.sent||0}`, 'green'));
    if ((st.error||0) > 0) badges.push(badge(`Erros: ${st.error}`, 'red'));
    if ((st.sem_whatsapp||0) > 0) badges.push(badge(`Sem WhatsApp: ${st.sem_whatsapp}`, 'amber'));
    
    detBadges.innerHTML = badges.join(' ');

    // Leads table
    detRows.innerHTML = (L.data||[]).map(r => `
      <tr class="border-t">
        <td class="py-2 px-3">${r.id}</td>
        <td class="py-2 px-3">${escHtml(r.company_name||'—')}</td>
        <td class="py-2 px-3">${escHtml(r.telefone||'—')}</td>
        <td class="py-2 px-3">${escHtml(r.status||'—')}</td>
        <td class="py-2 px-3">${r.lead_created_at ? new Date(r.lead_created_at).toLocaleString() : '—'}</td>
        <td class="py-2 px-3">${escHtml(r.folder_name || (r.folder_id ?? '—'))}</td>
      </tr>
    `).join('') || `<tr><td class="py-3 px-3" colspan="6">Nenhum lead</td></tr>`;

    // paginação
    detState.id = c.id;
    detState.page = L.page;
    detState.limit = L.limit;
    detState.total = L.total;

    const last = Math.max(1, Math.ceil((L.total||0) / L.limit));
    detPageInfo.textContent = `${L.page} / ${last}`;
    detPrev.disabled = (L.page <= 1);
    detNext.disabled = (L.page >= last);
  }

  detPrev?.addEventListener('click', () => loadDetailsPage(detState.id, detState.page-1, detState.limit));
  detNext?.addEventListener('click', () => loadDetailsPage(detState.id, detState.page+1, detState.limit));
  detPageSize?.addEventListener('change', () => {
    const lim = parseInt(detPageSize.value, 10) || 20;
    loadDetailsPage(detState.id, 1, lim);
  });

  // ====== listagem campanhas ======
  async function loadCamps(){
    const tb = document.getElementById('tbodyCamps');
    tb.innerHTML = '<tr><td class="py-3" colspan="9">Carregando...</td></tr>';

    const d = await fetchJSON(`${API}?action=list&_=${Date.now()}`);
    if(!d.ok){
      tb.innerHTML = `<tr><td class="py-3" colspan="9">${escHtml(d.error)}</td></tr>`;
      return;
    }

    tb.innerHTML = (d.data||[]).map(c=>{
      const sent = Number(c.enviados||0);
      const err  = Number(c.erros||0);
      const sem  = Number(c.sem_whatsapp||0);
      const total= Number(c.total||0);
      const done = total>0 && (sent+err+sem) >= total;
      const synced = c.synced_evolua_chat;

      const syncBadge = synced
        ? '<span class="inline-flex items-center rounded-full bg-emerald-50 px-2 py-0.5 text-xs text-emerald-700 border border-emerald-200">✓ Sincronizado</span>'
        : '<span class="inline-flex items-center rounded-full bg-amber-50 px-2 py-0.5 text-xs text-amber-700 border border-amber-200">Pendente</span>';

      let actions = '';
      
      if (!synced) {
        actions = `<button data-act="sync_leads" data-id="${c.id}" class="px-2 py-1 rounded bg-indigo-600 text-white mr-1">Sincronizar</button>`;
      } else if (done) {
        actions = `<button data-act="delete" data-id="${c.id}" class="px-2 py-1 rounded bg-rose-600 text-white">Excluir</button>`;
      } else {
        actions = `
          ${c.status==='draft' ? `<button data-act="start" data-id="${c.id}" class="px-2 py-1 rounded bg-green-600 text-white mr-1">Iniciar</button>` : ''}
          ${c.status==='running'? `<button data-act="pause" data-id="${c.id}" class="px-2 py-1 rounded bg-yellow-600 text-white mr-1">Pausar</button>` : ''}
          ${c.status==='paused' ? `<button data-act="resume" data-id="${c.id}" class="px-2 py-1 rounded bg-blue-600 text-white mr-1">Retomar</button>` : ''}
          <button data-act="delete" data-id="${c.id}" class="px-2 py-1 rounded bg-rose-600 text-white">Excluir</button>
        `;
      }

      return `
        <tr class="border-b hover:bg-gray-50 cursor-pointer" data-camp-id="${c.id}">
          <td class="py-2 pr-3">${escHtml(c.nome)}</td>
          <td class="py-2 pr-3">${escHtml(c.template_name || '—')}</td>
          <td class="py-2 pr-3">${escHtml(done ? 'finished' : c.status)}</td>
          <td class="py-2 pr-3">${Number(c.total||0)}</td>
          <td class="py-2 pr-3">${Number(c.enviados||0)}</td>
          <td class="py-2 pr-3">${Number(c.erros||0)}</td>
          <td class="py-2 pr-3">${Number(sem||0)}</td>
          <td class="py-2 pr-3">${syncBadge}</td>
<td class="py-2">${actions}</td>
        </tr>
      `;
    }).join('') || '<tr><td class="py-3" colspan="9">Nenhuma campanha</td></tr>';

    // Adiciona evento de clique nas linhas para abrir detalhes
    tb.querySelectorAll('tr[data-camp-id]').forEach(row => {
      row.addEventListener('click', async (e) => {
        // Ignora se clicou em botão
        if (e.target.closest('button')) return;
        
        const campId = parseInt(row.getAttribute('data-camp-id'), 10);
        if (campId) {
          openDetModal();
          const lim = parseInt(detPageSize?.value || '20', 10) || 20;
          await loadDetailsPage(campId, 1, lim);
        }
      });
    });
  }

// ====== Modal helpers (confirm + loading) ======
const confirmModal = document.getElementById('confirmModal');
const confirmTitle = document.getElementById('confirmTitle');
const confirmDesc  = document.getElementById('confirmDesc');
const confirmHint  = document.getElementById('confirmHint');
const confirmOk    = document.getElementById('confirmOk');
const confirmCancel= document.getElementById('confirmCancel');
const confirmClose = document.getElementById('confirmClose');
const confirmIcon  = document.getElementById('confirmIcon');

const actionModal  = document.getElementById('actionModal');
const actionTitle  = document.getElementById('actionTitle');
const actionDesc   = document.getElementById('actionDesc');
const actionMsg    = document.getElementById('actionMsg');
const actionClose  = document.getElementById('actionClose');

function openConfirm({ title, desc, hint, tone='default', okText='Confirmar' }){
  confirmTitle.textContent = title || 'Confirmar ação';
  confirmDesc.textContent  = desc || 'Tem certeza?';
  confirmHint.textContent  = hint || 'Você pode cancelar caso tenha clicado sem querer.';
  confirmOk.textContent    = okText || 'Confirmar';

  const tones = {
    default: { box:'bg-gray-100 text-gray-700', btn:'bg-gray-900 hover:bg-black' },
    danger:  { box:'bg-rose-50 text-rose-700', btn:'bg-rose-600 hover:bg-rose-700' },
    info:    { box:'bg-indigo-50 text-indigo-700', btn:'bg-indigo-600 hover:bg-indigo-700' },
    warn:    { box:'bg-amber-50 text-amber-800', btn:'bg-amber-500 hover:bg-amber-600' },
  };
  const t = tones[tone] || tones.default;

  confirmIcon.className = `mt-0.5 size-10 rounded-full flex items-center justify-center ${t.box}`;
  confirmOk.className   = `rounded-lg text-white px-4 py-2 text-sm ${t.btn}`;

  confirmModal.classList.remove('hidden');
}

function closeConfirm(){
  confirmModal.classList.add('hidden');
}

function openActionModal({ title, desc, msg }){
  actionTitle.textContent = title || 'Aguarde…';
  actionDesc.textContent  = desc  || 'Processando';
  actionMsg.className     = 'mt-4 text-sm text-gray-700';
  actionMsg.textContent   = msg   || '';
  actionClose.classList.add('hidden');
  actionModal.classList.remove('hidden');
}

function setActionModalDone({ msg, ok=true, autoCloseMs=null }){
  actionMsg.className = ok ? 'mt-4 text-sm text-emerald-700' : 'mt-4 text-sm text-rose-700';
  actionMsg.textContent = msg || (ok ? 'Concluído' : 'Falhou');
  actionClose.classList.remove('hidden');

  // Fecha sozinho (ex: após sucesso)
  if (typeof autoCloseMs === 'number' && autoCloseMs > 0) {
    setTimeout(() => {
      closeActionModal();
    }, autoCloseMs);
  }
}


function closeActionModal(){
  actionModal.classList.add('hidden');
  actionMsg.textContent = '';
  actionMsg.className = 'mt-4 text-sm text-gray-700';
}

confirmCancel.addEventListener('click', closeConfirm);
confirmClose.addEventListener('click', closeConfirm);
confirmModal.addEventListener('click', (e)=>{ if(e.target === confirmModal) closeConfirm(); });

actionClose.addEventListener('click', closeActionModal);
actionModal.addEventListener('click', (e)=>{ if(e.target === actionModal) closeActionModal(); });

// ====== Anti double-click ======
const inflightByCamp = new Map();   // key = `${action}:${id}` -> true
function lockKey(action, id){ return `${action}:${id}`; }

function setBtnLoading(btn, loading){
  if(!btn) return;
  if(loading){
    btn.dataset._oldText = btn.textContent;
    btn.disabled = true;
    btn.classList.add('opacity-60','cursor-not-allowed');
    btn.textContent = 'Aguarde...';
  } else {
    btn.disabled = false;
    btn.classList.remove('opacity-60','cursor-not-allowed');
    if(btn.dataset._oldText) btn.textContent = btn.dataset._oldText;
  }
}

function getConfirmConfig(action, id){
  if (action === 'start') return {
    title:'Iniciar campanha',
    desc:`Deseja iniciar a campanha #${id}?`,
    hint:'Isso irá iniciar os disparos com o template selecionado.',
    tone:'info',
    okText:'Iniciar'
  };

  if (action === 'delete') return {
    title:'Excluir campanha',
    desc:`Excluir a campanha #${id}?`,
    hint:'Essa ação não pode ser desfeita. A campanha e os itens serão removidos.',
    tone:'danger',
    okText:'Excluir'
  };

  // opcional: confirmar também pause/resume/cancel/sync
  if (action === 'pause') return { title:'Pausar campanha', desc:`Pausar a campanha #${id}?`, hint:'', tone:'warn', okText:'Pausar' };
  if (action === 'resume') return { title:'Retomar campanha', desc:`Retomar a campanha #${id}?`, hint:'', tone:'info', okText:'Retomar' };
  if (action === 'cancel') return { title:'Cancelar campanha', desc:`Cancelar a campanha #${id}?`, hint:'', tone:'warn', okText:'Cancelar' };
  if (action === 'sync_leads') return { title:'Sincronizar leads', desc:`Sincronizar leads da campanha #${id}?`, hint:'', tone:'info', okText:'Sincronizar' };

  return null;
}

function shouldConfirm(action){
  return ['start','delete','pause','resume','cancel','sync_leads'].includes(action);
}

// ====== NOVO campAction ======
async function campAction(id, action, btnEl){
  id = parseInt(id, 10);
  if(!id || !action) return;

  const key = lockKey(action, id);
  if (inflightByCamp.get(key)) return; // evita 2 cliques
  inflightByCamp.set(key, true);

  setBtnLoading(btnEl, true);

  try{
    // confirmação
    if (shouldConfirm(action)){
      const cfg = getConfirmConfig(action, id) || {};
      await new Promise((resolve, reject) => {
        openConfirm(cfg);

        const onOk = () => { cleanup(); resolve(true); };
        const onCancel = () => { cleanup(); reject(new Error('cancelled')); };

        function cleanup(){
          confirmOk.removeEventListener('click', onOk);
          confirmCancel.removeEventListener('click', onCancel);
          confirmClose.removeEventListener('click', onCancel);
          closeConfirm();
        }

        confirmOk.addEventListener('click', onOk);
        confirmCancel.addEventListener('click', onCancel);
        confirmClose.addEventListener('click', onCancel);
      });
    }

    // loading modal
    const mapTxt = {
      start:'Iniciando campanha…',
      pause:'Pausando campanha…',
      resume:'Retomando campanha…',
      cancel:'Cancelando campanha…',
      delete:'Excluindo campanha…',
      sync_leads:'Sincronizando leads…'
    };

    openActionModal({ title:'Aguarde…', desc: mapTxt[action] || 'Processando…', msg:'' });

    // executa
    if (action === 'sync_leads') {
      await syncLeads(id);
setActionModalDone({ msg:'✓ Leads sincronizados com sucesso!', ok:true, autoCloseMs: 900 });
      await loadCamps();
      return;
    }

    const body = new URLSearchParams({ id:String(id) });
    const r = await fetch(`${API}?action=${action}`, {
      method:'POST',
      headers:{ 'Content-Type':'application/x-www-form-urlencoded' },
      body,
      credentials:'same-origin'
    });

    const t = await r.text();
    let j = null;
    try { j = JSON.parse(t); } catch {}

    if(!r.ok || !j || !j.ok){
setActionModalDone({ msg: (j?.error || t || 'Falha'), ok:false, autoCloseMs: 2500 });
      return;
    }

setActionModalDone({ msg:'✓ Ação executada com sucesso!', ok:true, autoCloseMs: 900 });
    await loadCamps();
  } catch(e){
    if (String(e?.message) !== 'cancelled'){
      console.error(e);
      openActionModal({ title:'Ops…', desc:'Não foi possível concluir', msg:'' });
      setActionModalDone({ msg: e?.message || 'Falha', ok:false });
    }
  } finally {
    inflightByCamp.delete(lockKey(action, id));
    setBtnLoading(btnEl, false);
  }
}

// ====== SUBSTITUI o listener antigo do tbody por este ======
document.getElementById('tbodyCamps').addEventListener('click', async (ev)=>{
  const b = ev.target.closest('button');
  if(!b) return;

  ev.stopPropagation(); // não abre o modal de detalhes quando clicar no botão

  const id  = b.getAttribute('data-id');
  const act = b.getAttribute('data-act');
  if(id && act) await campAction(id, act, b);
});


  document.getElementById('btnReload').addEventListener('click', loadCamps);

  // ====== submit create ======
  const formEl = document.getElementById('formDisparoOficial');
  let isSubmitting = false;

  formEl.addEventListener('submit', async (ev)=>{
    ev.preventDefault();

    if (isSubmitting) return;
    isSubmitting = true;

    const msg = document.getElementById('createMsg');
    const btn = document.getElementById('btnCreateCamp');

    if(sel.size===0){ 
      alert('Selecione ao menos 1 lead.'); 
      isSubmitting = false;
      return; 
    }
    const tplId = (templateIdEl.value||'').trim();
    if(!tplId){ 
      alert('Selecione um template.'); 
      isSubmitting = false;
      return; 
    }

    const fd = new FormData(ev.currentTarget);

    const payload = {
      nome: fd.get('nome'),
      min_delay: Number(fd.get('min_delay')||1),
      max_delay: Number(fd.get('max_delay')||1),
      lead_ids: [...sel],
      template_id: tplId,
      template_snapshot: templateSnapshotEl.value ? JSON.parse(templateSnapshotEl.value) : null,
      template_vars: templateVarsEl.value ? JSON.parse(templateVarsEl.value) : null
    };

    const schStart = (document.getElementById('schStart')?.value||'').trim();
    const schEnd   = (document.getElementById('schEnd')?.value||'').trim();
    const schTz    = (document.getElementById('schTz')?.value||'America/Sao_Paulo').trim();
    const wds = [...document.querySelectorAll('.schWD:checked')].map(i=>parseInt(i.value,10));
    const datesRaw = (document.getElementById('schDates')?.value||'').trim();
    const blockedDates = datesRaw ? datesRaw.split(',').map(s=>s.trim()).filter(Boolean) : [];

    if(schStart && schEnd){
      payload.schedule = { tz: schTz || 'America/Sao_Paulo', start: schStart, end: schEnd, blocked_weekdays: wds, blocked_dates: blockedDates };
    }

    msg.textContent = 'Criando campanha...';
    btn.disabled = true;

    try {
      const r = await fetch(`${API}?action=create`, {
        method:'POST',
        headers:{ 'Content-Type':'application/json' },
        body: JSON.stringify(payload),
        credentials:'same-origin'
      });

      const j = await r.json().catch(()=>({ok:false}));

      if(j.ok){
        msg.textContent = `Campanha #${j.id} criada. Sincronizando leads...`;

        try {
          await syncLeads(j.id);
          msg.className = 'text-sm text-emerald-600';
          msg.textContent = `✓ Campanha #${j.id} criada e sincronizada!`;
          
          // LIMPAR FORMULÁRIO E SELEÇÕES
          formEl.reset();
          sel.clear();
          updateSelCount();
          templateIdEl.value = '';
          templateSnapshotEl.value = '';
          templateVarsEl.value = '';
          templatePreview.textContent = 'Nenhum template selecionado';
          
          setTimeout(() => {
            msg.textContent = '';
            msg.className = 'text-sm text-gray-600';
          }, 3000);

        } catch (e) {
          msg.className = 'text-sm text-rose-600';
          msg.textContent = `Campanha #${j.id} criada, mas falhou a sincronização.`;
        }

        await loadCamps();
      } else {
        msg.className = 'text-sm text-rose-600';
        msg.textContent = 'Erro: ' + (j.error || 'Falha');
      }
    } catch (e) {
      msg.className = 'text-sm text-rose-600';
      msg.textContent = 'Erro: ' + e.message;
    } finally {
      btn.disabled = false;
      isSubmitting = false;
    }
  });

  // boot
  updateSelCount();
  loadCamps();
})();
</script>