<?php
require_once __DIR__ . "/_bootstrap.php";
?>
<!doctype html>
<html lang="pt-br">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
<title>Cartão • Administração</title>

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">

<style>
  :root{
    --bg:#1e1e1e;
    --card:#252526;
    --card2:#2d2d30;
    --text:#f3f3f3;
    --muted:#bdbdbd;
    --line: rgba(255,255,255,.10);
    --accent:#0078d4;
    --accent2:#2b88d8;
    --ok:#22c55e;
    --warn:#f59e0b;
    --danger:#ff5a5f;
    --btn:#2d2d30;
    --btnHover:#3a3a3d;
    --input:#1f1f1f;
  }
  body{background:var(--bg);color:var(--text);font-family:"Segoe UI",system-ui; }
  .cardX{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:14px;margin-bottom:12px;box-shadow:0 10px 24px rgba(0,0,0,.35)}
  .field{background:var(--input);border:1px solid rgba(255,255,255,.18);color:#fff;padding:10px 12px;border-radius:10px;width:100%;outline:none;font-weight:700}
  .field:focus{box-shadow:0 0 0 3px rgba(0,120,212,.35)}
  .btnAccent{background:linear-gradient(135deg,var(--accent),var(--accent2));border:0;color:#fff;padding:10px 14px;border-radius:10px;font-weight:900}
  .btnGhost{background:var(--btn);border:1px solid rgba(255,255,255,.18);color:#fff;padding:10px 14px;border-radius:10px;font-weight:800}
  .btnGhost:hover{background:var(--btnHover)}
  .btnDanger{background:linear-gradient(135deg,var(--danger),#b91c1c);border:0;color:#fff;padding:10px 14px;border-radius:10px;font-weight:900}
  .muted{color:var(--muted);font-size:12px}
  .pill{display:inline-flex;gap:8px;align-items:center;padding:7px 10px;border-radius:999px;border:1px solid var(--line);font-weight:900;font-size:12px}
  .pill.ok{border-color:rgba(34,197,94,.55);color:#bff7d2}
  .pill.warn{border-color:rgba(245,158,11,.55);color:#ffe3b0}
  .pill.danger{border-color:rgba(255,90,95,.55);color:#ffd1d3}
  .sysmsg{display:none;padding:10px 12px;border-radius:12px;border:1px solid var(--line);background:var(--card2);margin-bottom:12px;font-weight:900}
  .sysmsg.ok{border-color:rgba(34,197,94,.55)}
  .sysmsg.err{border-color:rgba(255,90,95,.55)}
  .tableX{width:100%;border-collapse:collapse;border:1px solid var(--line);border-radius:12px;overflow:hidden}
  .tableX th,.tableX td{padding:10px;border-bottom:1px solid var(--line);vertical-align:middle}
  .tableX th{color:var(--muted);font-size:11px;text-transform:uppercase;letter-spacing:.6px;background:var(--card2)}
  .mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace}
  .miniBtn{padding:6px 10px;border-radius:10px;font-weight:900}
</style>
</head>
<body>

<div class="container-fluid p-3" style="max-width:1200px;">
  <div id="sysmsg" class="sysmsg"></div>

  <!-- HEADER -->
  <div class="cardX">
    <div class="d-flex align-items-start justify-content-between flex-wrap gap-2">
      <div>
        <div style="font-weight:950;font-size:16px;">💳 Administração do Cartão</div>
        <div class="muted">Recarga • Estorno (sessão atual) • Fechar • Transferência em caso de perda</div>
      </div>
      <div class="d-flex gap-2 flex-wrap">
        <a class="btnGhost text-decoration-none" href="../balcao/index.php">⬅ Voltar ao Balcão</a>
      </div>
    </div>
  </div>

  <div class="row g-3">
    <!-- COL ESQUERDA -->
    <div class="col-lg-4">

      <!-- CONSULTA ORIGEM -->
      <div class="cardX">
        <div style="font-weight:950;">Consultar cartão (origem)</div>
        <div class="muted">Pode colar o conteúdo do QR; o sistema extrai o código.</div>

        <div class="row g-2 mt-2">
          <div class="col-12">
            <input id="codigo" class="field mono" placeholder="Código do cartão / QR">
          </div>
          <div class="col-6 d-grid">
            <button id="btnBuscar" class="btnAccent">Buscar</button>
          </div>
          <div class="col-6 d-grid">
            <button id="btnAbrirCam" class="btnGhost">📷 Ler QR</button>
          </div>
        </div>

        <div id="camBox" style="display:none; margin-top:12px;">
          <video id="video" playsinline style="width:100%; max-height:260px; border-radius:12px; border:1px solid var(--line);"></video>
          <div class="muted mt-2">Aponte para o QR do cartão.</div>
          <div class="row g-2 mt-2">
            <div class="col-6 d-grid"><button id="btnTrocarCam" class="btnGhost">🔄 Trocar câmera</button></div>
            <div class="col-6 d-grid"><button id="btnFecharCam" class="btnDanger">✖ Fechar</button></div>
          </div>
        </div>
      </div>

      <!-- RESUMO -->
      <div class="cardX">
        <div class="muted">Cartão carregado</div>
        <div style="font-weight:950;font-size:18px;"><span id="cCodigo" class="mono">—</span></div>
        <div class="muted" id="cCliente">Cliente: —</div>
        <div class="muted" id="cSessao">Sessão: —</div>

        <div class="d-flex gap-2 flex-wrap align-items-center mt-2">
          <span id="pillStatus" class="pill">Status: —</span>
          <span id="pillSaldo" class="pill ok">Saldo: R$ 0,00</span>
        </div>

        <div class="row g-2 mt-2">
          <div class="col-6 d-grid"><button id="btnAtualizar" class="btnGhost">↻ Atualizar</button></div>
          <div class="col-6 d-grid"><button id="btnFecharCartao" class="btnDanger">✅ Fechar</button></div>
        </div>

        <div class="muted mt-2">
          Observação: saldo é <b>somente da sessão atual</b>. Ao fechar, o saldo “zera”.
        </div>
      </div>

      <!-- RECARGA -->
      <div class="cardX">
        <div style="font-weight:950;">➕ Recarga (admin)</div>
        <div class="muted">Após a recarga inicial com cartão aberto, nome/celular não podem mudar.</div>

        <div class="mt-2">
          <label class="muted mb-1">Valor (R$)</label>
          <input id="valorRecarga" class="field mono" placeholder="0,00" inputmode="decimal">
        </div>

        <div class="row g-2 mt-2">
          <div class="col-12">
            <label class="muted mb-1">Nome do cliente (opcional)</label>
            <input id="nome" class="field" placeholder="Nome">
          </div>
          <div class="col-12">
            <label class="muted mb-1">Celular (opcional)</label>
            <input id="celular" class="field" placeholder="Celular" inputmode="tel">
          </div>
          <div class="col-12">
            <label class="muted mb-1">Observação (opcional)</label>
            <input id="obs" class="field" placeholder="Obs">
          </div>
        </div>

        <div class="d-grid mt-2">
          <button id="btnRecarregar" class="btnAccent">Registrar recarga</button>
        </div>
      </div>

    </div>

    <!-- COL DIREITA -->
    <div class="col-lg-8">

      <!-- MOVIMENTOS -->
      <div class="cardX">
        <div class="d-flex justify-content-between align-items-end flex-wrap gap-2">
          <div>
            <div style="font-weight:950;">Movimentos da sessão atual</div>
            <div class="muted">Estorno apenas em <b>Débito (D)</b> e com cartão em uso (aberto).</div>
          </div>
          <button id="btnCarregarMovs" class="btnGhost miniBtn">↻ Recarregar lista</button>
        </div>

        <div class="mt-3" style="overflow:auto;">
          <table class="tableX">
            <thead>
              <tr>
                <th style="width:70px;">Op</th>
                <th>Motivo / Obs</th>
                <th style="width:140px;">Valor</th>
                <th style="width:120px;">Origem</th>
                <th style="width:180px;">Quando</th>
                <th style="width:130px;">Ação</th>
              </tr>
            </thead>
            <tbody id="tblMov">
              <tr><td colspan="6" class="muted">Carregue um cartão para ver os movimentos.</td></tr>
            </tbody>
          </table>
        </div>
      </div>

      <!-- BUSCA CLIENTE / TRANSFERÊNCIA -->
      <div class="cardX">
        <div style="font-weight:950;">🔎 Buscar cliente (perda do cartão) • Transferir saldo</div>
        <div class="muted">Transferência sempre a partir do cartão carregado (origem). Escolha um cartão <b>LIVRE</b> como destino.</div>

        <div class="row g-2 mt-2">
          <div class="col-md-8">
            <input id="buscaCliente" class="field" placeholder="Nome ou celular (mín. 3 caracteres)">
          </div>
          <div class="col-md-4 d-grid">
            <button id="btnBuscarCliente" class="btnGhost">Buscar</button>
          </div>
        </div>

        <div class="mt-3" style="overflow:auto;">
          <table class="tableX">
            <thead>
              <tr>
                <th style="width:140px;">Código</th>
                <th>Cliente</th>
                <th style="width:140px;">Status</th>
                <th style="width:160px;">Ação</th>
              </tr>
            </thead>
            <tbody id="listaClientes">
              <tr><td colspan="4" class="muted">Digite acima para buscar…</td></tr>
            </tbody>
          </table>
        </div>
      </div>

    </div>
  </div>
</div>

<!-- MODAL (avisos/erros/confirmacao) -->
<div class="modal fade" id="warnModal" tabindex="-1" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered">
    <div class="modal-content" style="background:var(--card); color:var(--text); border:1px solid var(--line); border-radius:16px;">
      <div class="modal-header" style="border-color:var(--line);">
        <h5 class="modal-title" id="warnTitle">Aviso</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Fechar" style="filter:invert(1)"></button>
      </div>
      <div class="modal-body" id="warnBody"></div>
      <div class="modal-footer" style="border-color:var(--line);">
        <button type="button" class="btn btn-outline-light" data-bs-dismiss="modal" id="warnCancel">OK</button>
        <button type="button" class="btn btn-warning d-none" id="warnConfirm">Confirmar</button>
      </div>
    </div>
  </div>
</div>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script>
(() => {
  const apiBase = 'api.php';

  // ===== UI helpers =====
  const sys = document.getElementById('sysmsg');
  let sysTimer = null;
  function showMsg(text, type='ok'){
    clearTimeout(sysTimer);
    sys.className = 'sysmsg ' + (type === 'err' ? 'err' : 'ok');
    sys.textContent = text;
    sys.style.display = 'block';
    sysTimer = setTimeout(()=> sys.style.display='none', 3500);
  }

  const warnModal = new bootstrap.Modal(document.getElementById('warnModal'));
  const warnTitle = document.getElementById('warnTitle');
  const warnBody  = document.getElementById('warnBody');
  const warnCancel= document.getElementById('warnCancel');
  const warnConfirm = document.getElementById('warnConfirm');
  let onConfirm = null;

  function esc(s){
    s = String(s ?? '');
    return s.replaceAll('&','&amp;').replaceAll('<','&lt;').replaceAll('>','&gt;').replaceAll('"','&quot;').replaceAll("'","&#39;");
  }

  function showWarn(title, body, opts){
    warnTitle.textContent = title || 'Aviso';
    warnBody.innerHTML = body || '';
    onConfirm = null;

    if (opts && typeof opts.confirm === 'function'){
      warnConfirm.classList.remove('d-none');
      warnConfirm.textContent = opts.confirmText || 'Confirmar';
      warnCancel.textContent = opts.cancelText || 'Cancelar';
      onConfirm = opts.confirm;
    } else {
      warnConfirm.classList.add('d-none');
      warnCancel.textContent = 'OK';
    }
    warnModal.show();
  }

  warnConfirm.addEventListener('click', async () => {
    if (!onConfirm) return;
    warnConfirm.disabled = true;
    try { await onConfirm(); warnModal.hide(); }
    finally { warnConfirm.disabled = false; }
  });

  async function apiGet(params){
    const url = apiBase + '?' + new URLSearchParams(params).toString();
    const r = await fetch(url, {credentials:'same-origin'});
    const text = await r.text();
    try { return JSON.parse(text); }
    catch {
      showWarn('Resposta inválida do servidor', `<pre style="white-space:pre-wrap;margin:0;">${esc(text.slice(0,800))}</pre>`);
      return {ok:false, error:'Resposta inválida do servidor.'};
    }
  }

  async function apiPost(action, body){
    const url = apiBase + '?action=' + encodeURIComponent(action);
    const r = await fetch(url, {
      method:'POST',
      credentials:'same-origin',
      headers:{'Content-Type':'application/json'},
      body: JSON.stringify(body || {})
    });
    const text = await r.text();
    try { return JSON.parse(text); }
    catch {
      showWarn('Resposta inválida do servidor', `<pre style="white-space:pre-wrap;margin:0;">${esc(text.slice(0,800))}</pre>`);
      return {ok:false, error:'Resposta inválida do servidor.'};
    }
  }

  function fmtBRL(n){
    n = Number(n || 0);
    return n.toLocaleString('pt-BR', {minimumFractionDigits:2, maximumFractionDigits:2});
  }

  function normalizeCodigo(raw){
    raw = (raw || '').trim();
    if (!raw) return '';
    const m = raw.match(/(?:cartao|carta[oó]|codigo)\s*[:=]\s*([A-Za-z0-9_-]{4,80})/i);
    if (m && m[1]) return m[1].trim();
    const tokens = raw.split(/[^A-Za-z0-9_-]+/g).filter(Boolean);
    if (tokens.length) return tokens[tokens.length - 1];
    return raw.replace(/[^A-Za-z0-9_-]/g,'') || raw;
  }

  // ===== state =====
  let cartaoAtual = null;

  function setResumo(cartao, saldo){
    cartaoAtual = cartao || null;

    document.getElementById('cCodigo').textContent = cartao?.codigo || '—';
    const nome = (cartao?.nome || '').trim();
    const cel  = (cartao?.celular || '').trim();
    document.getElementById('cCliente').textContent = 'Cliente: ' + (nome ? nome : '—') + (cel ? (' • ' + cel) : '');

    const status = String(cartao?.status || '').toUpperCase();
    const pillStatus = document.getElementById('pillStatus');
    pillStatus.className = 'pill ' + (status === 'EM_USO' ? 'ok' : (status === 'BLOQUEADO' ? 'danger' : 'warn'));
    pillStatus.textContent = 'Status: ' + (status || '—');

    document.getElementById('pillSaldo').textContent = 'Saldo: R$ ' + fmtBRL(saldo || 0);

    const aberto = cartao?.aberto_em || null;
    const fechado = cartao?.fechado_em || null;
    if (aberto && !fechado) document.getElementById('cSessao').textContent = 'Sessão: ABERTA desde ' + aberto;
    else if (aberto && fechado) document.getElementById('cSessao').textContent = 'Sessão: FECHADA em ' + fechado;
    else document.getElementById('cSessao').textContent = 'Sessão: —';
  }

  // ===== movimentos =====
  const tblMov = document.getElementById('tblMov');

    function renderMovs(movs){
      tblMov.innerHTML = '';
      if (!Array.isArray(movs) || movs.length === 0){
        tblMov.innerHTML = `<tr><td colspan="6" class="muted">Nenhum movimento nesta sessão.</td></tr>`;
        return;
      }

      // ✅ movimentos que já foram estornados: existe um registro com ref_mov_id = idOriginal
      const estornados = new Set();
      for (const m of movs) {
        const ref = Number(m.ref_mov_id || 0);
        if (ref > 0) estornados.add(ref);
      }
      window.__estornados = estornados;

      for (const m of movs){
        const op = String(m.operacao||'').toUpperCase();
        const motivoUp = String(m.motivo||'').toUpperCase();

        // ✅ considera estorno por operação E OU motivo ESTORNO (compatível com dados antigos)
        const isEstorno = (op === 'E') || (motivoUp === 'ESTORNO');

        // ✅ badge corrigido (C / D / EST)
        let badge = `<span class="pill danger">D</span>`;
        if (op === 'C' && !isEstorno) badge = `<span class="pill ok">C</span>`;
        if (isEstorno) badge = `<span class="pill warn">EST</span>`;

        const motivo = esc(m.motivo||'') + (m.observacao ? `<div class="muted mt-1">${esc(m.observacao)}</div>` : '');
        const valor = 'R$ ' + fmtBRL(m.valor);
        const origem = esc(String(m.origem||'').toUpperCase());
        const quando = esc(m.criado_em || '');

        const isDebitoReal = (op === 'D') && !isEstorno; // ✅ estorno não pode ser tratado como débito
        const jaEstornado = estornados.has(Number(m.id));

        // ✅ botão só para D real, e ainda não estornado
        const canEstornar = isDebitoReal && !jaEstornado;

        const tr = document.createElement('tr');
        tr.innerHTML = `
          <td>${badge}</td>
          <td>${motivo}</td>
          <td class="mono">${valor}</td>
          <td>${origem}</td>
          <td class="mono">${quando}</td>
          <td>
            ${
              canEstornar
                ? `<button class="btn btn-sm btn-outline-warning miniBtn" data-estornar="${m.id}">Estornar</button>`
                : (jaEstornado
                    ? `<button type="button"
                              class="btn btn-sm btn-outline-success miniBtn"
                              data-ja-estornado="1">
                          ✅ Estornado
                        </button>`
                    : `<span class="muted">—</span>`
                  )
            }
          </td>
        `;
        tblMov.appendChild(tr);
      }

      tblMov.querySelectorAll('[data-estornar]').forEach(btn=>{
        btn.addEventListener('click', ()=>{
          const movId = parseInt(btn.getAttribute('data-estornar'), 10);

          // ✅ blindagem: se já estornado, nem deixa tentar
          if (window.__estornados && window.__estornados.has(movId)) {
            showWarn('Atenção', 'Este lançamento já foi estornado. O estorno só pode ser feito 1 vez.');
            return;
          }

          if (!cartaoAtual?.codigo){
            showWarn('Atenção', 'Carregue um cartão antes de estornar.');
            return;
          }

          showWarn(
            'Confirmar estorno',
            'Deseja estornar este lançamento? (Somente Débito (D), cartão em uso/aberto e sessão atual.)',
            {
              confirmText:'Confirmar',
              cancelText:'Cancelar',
              confirm: async ()=>{
                const j = await apiPost('cartao_estornar', {mov_id: movId});
                if (!j.ok){
                  showWarn('Não foi possível estornar', esc(j.error||'Erro.'));
                  return;
                }

                if (window.__estornados) window.__estornados.add(movId);

                showMsg('Estorno realizado com sucesso.');
                await atualizarTudo();

                tblMov.querySelectorAll('[data-ja-estornado]').forEach(btn => {
                  btn.addEventListener('click', () => {
                    showWarn('Atenção','Este lançamento já foi estornado. O estorno só pode ser feito 1 vez.');
                  });
                });
              }
            }
          );
        });
      });
    }



    async function carregarMovs(){
        if (!cartaoAtual?.codigo){
          tblMov.innerHTML = `<tr><td colspan="6" class="muted">Carregue um cartão para ver os movimentos.</td></tr>`;
          return;
        }

        // ❌ cartão fechado → não mostra movimentações
        if (String(cartaoAtual.status || '').toUpperCase() !== 'EM_USO') {
          tblMov.innerHTML = `<tr><td colspan="6" class="muted">Cartão fechado. Nenhuma movimentação disponível.</td></tr>`;
          return;
        }

        const j = await apiGet({action:'cartao_movimentos', codigo: cartaoAtual.codigo});
        if (!j.ok){
          showWarn('Falha ao carregar movimentos', esc(j.error||'Erro.'));
          return;
        }

        setResumo(j.cartao, j.saldo);
        renderMovs(j.movimentos || []);
    }


  async function atualizarTudo(){
    if (!cartaoAtual?.codigo) return;
    const j = await apiGet({action:'cartao_buscar', codigo: cartaoAtual.codigo});
    if (j.ok) setResumo(j.cartao, j.saldo);
    await carregarMovs();
  }

  // ===== consultar cartão =====
  async function buscarCartao(){
    const raw = document.getElementById('codigo').value;
    const codigo = normalizeCodigo(raw);
    if (!codigo){
      showWarn('Atenção', 'Informe o código do cartão.');
      return;
    }
    const j = await apiGet({action:'cartao_buscar', codigo});
    if (!j.ok){
      showWarn('Não foi possível consultar', esc(j.error||'Erro.'));
      return;
    }
    setResumo(j.cartao, j.saldo);
    await carregarMovs();
    showMsg('Cartão carregado.');
  }

  document.getElementById('btnBuscar').addEventListener('click', buscarCartao);
  document.getElementById('codigo').addEventListener('keydown', (e)=>{ if (e.key === 'Enter') buscarCartao(); });
  document.getElementById('btnAtualizar').addEventListener('click', atualizarTudo);
  document.getElementById('btnCarregarMovs').addEventListener('click', carregarMovs);

  // ===== recarga =====
  document.getElementById('valorRecarga').addEventListener('input', ()=>{
    let v = document.getElementById('valorRecarga').value;
    v = v.replace(/[^0-9,]/g,'');
    const parts = v.split(',');
    if (parts.length > 2) v = parts[0] + ',' + parts.slice(1).join('');
    document.getElementById('valorRecarga').value = v;
  });

  async function recarregar(){
    if (!cartaoAtual?.codigo){
      showWarn('Atenção', 'Carregue um cartão antes de recarregar.');
      return;
    }

    const vr = (document.getElementById('valorRecarga').value || '').replace(/\./g,'').replace(',', '.');
    const valor = parseFloat(vr);
    if (!(valor > 0)){
      showWarn('Atenção', 'Informe um valor válido para recarga.');
      return;
    }

    const body = {
      codigo: cartaoAtual.codigo,
      valor,
      nome: (document.getElementById('nome').value || '').trim() || null,
      celular: (document.getElementById('celular').value || '').trim() || null,
      observacao: (document.getElementById('obs').value || '').trim() || null
    };

    const j = await apiPost('cartao_recarregar', body);
    if (!j.ok){
      showWarn('Não foi possível recarregar', esc(j.error||'Erro.'));
      return;
    }

    setResumo(j.cartao, j.saldo);
    await carregarMovs();

    showWarn(
      'Recarga concluída',
      `Recarga registrada com sucesso.<br>
      <div class="mt-2"><b>Cartão:</b> <span class="mono">${esc(j.cartao?.codigo || '')}</span></div>
      <div class="mt-1"><b>Saldo da sessão:</b> R$ ${fmtBRL(j.saldo || 0)}</div>`
    );

    document.getElementById('valorRecarga').value = '';
  }

  document.getElementById('btnRecarregar').addEventListener('click', recarregar);

  // ===== fechar cartão =====
  async function fecharCartao(){
    if (!cartaoAtual?.codigo){
      showWarn('Atenção', 'Carregue um cartão antes de fechar.');
      return;
    }

    showWarn(
      'Fechar cartão',
      'Confirmar fechamento do cartão? Após fechar, o saldo zera e não será permitido estorno.',
      {
        confirmText:'Confirmar',
        cancelText:'Cancelar',
        confirm: async ()=>{
          const j = await apiPost('cartao_fechar', {codigo: cartaoAtual.codigo});
          if (!j.ok){
            showWarn('Não foi possível fechar', esc(j.error||'Erro.'));
            return;
          }
          setResumo(j.cartao, j.saldo);
          await carregarMovs();
          showMsg('Cartão fechado com sucesso.');
        }
      }
    );
  }
  document.getElementById('btnFecharCartao').addEventListener('click', fecharCartao);

  // ===== busca cliente + transferência =====
  const listaClientes = document.getElementById('listaClientes');
  let debounce = null;

  function renderResultados(list){
    listaClientes.innerHTML = '';
    if (!Array.isArray(list) || list.length === 0){
      listaClientes.innerHTML = `<tr><td colspan="4" class="muted">Nenhum cartão encontrado.</td></tr>`;
      return;
    }

    const stAtual = String(cartaoAtual?.status || '').toUpperCase();
    const atualEhLivre = (stAtual === 'LIVRE');
    const atualEhEmUso = (stAtual === 'EM_USO');

    for (const c of list){
      const status = String(c.status||'').toUpperCase();
      const isLivre = (status === 'LIVRE');
      const isEmUso  = (status === 'EM_USO');

      const tr = document.createElement('tr');
      tr.innerHTML = `
        <td class="mono">${esc(c.codigo || '')}</td>
        <td>${esc((c.nome||'—'))} <span class="muted">${c.celular ? ' • ' + esc(c.celular) : ''}</span></td>
        <td>${isLivre ? '<span class="pill ok">LIVRE</span>' : (status === 'EM_USO' ? '<span class="pill warn">EM_USO</span>' : '<span class="pill danger">'+esc(status)+'</span>')}</td>
        <td>
          ${
            // MODO 1 (antigo): cartão atual = origem (EM_USO) -> lista mostra destinos LIVRE
            (atualEhEmUso && isLivre)
              ? `<button class="btn btn-sm btn-warning miniBtn" data-dest="${esc(c.codigo || '')}">Transferir</button>`
              // MODO 2 (perda): cartão atual = destino (LIVRE) -> lista mostra origens EM_USO (perdido)
              : (atualEhLivre && isEmUso)
                ? `<button class="btn btn-sm btn-primary miniBtn" data-orig="${esc(c.codigo || '')}">Receber</button>`
                : '<span class="muted">—</span>'
          }
        </td>
      `;
      listaClientes.appendChild(tr);
    }

    listaClientes.querySelectorAll('[data-dest]').forEach(btn=>{
      btn.addEventListener('click', ()=>{
        const dest = btn.getAttribute('data-dest');

        if (!cartaoAtual?.codigo){
          showWarn('Atenção', 'Carregue o cartão de origem antes de transferir.');
          return;
        }

        showWarn(
          'Confirmar transferência',
          `Transferir o saldo do cartão <b class="mono">${esc(cartaoAtual.codigo)}</b> para o cartão <b class="mono">${esc(dest)}</b>?`,
          {
            confirmText:'Confirmar',
            cancelText:'Cancelar',
            confirm: async ()=>{
              const j = await apiPost('cartao_transferir', {
                orig_codigo: cartaoAtual.codigo,
                novo_codigo: dest
              });
              if (!j.ok){
                showWarn('Não foi possível transferir', esc(j.error || 'Erro.'));
                return;
              }

              // ✅ Após transferir, carregar o cartão DESTINO automaticamente
              setResumo(j.destino, j.saldo_destino || 0);
              await carregarMovs();
              showMsg('Transferência realizada e cartão destino carregado.');

              showWarn(
                'Transferência concluída',
                `Saldo transferido com sucesso.<br>
                 <div class="mt-2"><b>Destino:</b> <span class="mono">${esc(j.destino?.codigo || '')}</span></div>
                 <div class="mt-1"><b>Saldo da sessão (destino):</b> R$ ${fmtBRL(j.saldo_destino || 0)}</div>`
              );
            }
          }
        );
      });
    });

    // Transferência por perda (mantém movimentações)
    listaClientes.querySelectorAll('[data-orig]').forEach(btn=>{
      btn.addEventListener('click', ()=>{
        const orig = btn.getAttribute('data-orig');

        if (!cartaoAtual?.codigo){
          showWarn('Atenção', 'Carregue o cartão LIVRE (destino) antes de receber a transferência.');
          return;
        }

        const st = String(cartaoAtual?.status || '').toUpperCase();
        if (st !== 'LIVRE'){
          showWarn('Atenção', 'Para transferência por perda, o cartão atual deve estar LIVRE (destino).');
          return;
        }

        showWarn(
          'Confirmar transferência por perda',
          `Transferir <b>saldo e histórico</b> do cartão <b class="mono">${esc(orig)}</b> para o cartão livre <b class="mono">${esc(cartaoAtual.codigo)}</b>?<br>
           <div class="mt-2 muted">O cartão perdido será marcado como <b>BLOQUEADO</b> após a transferência.</div>`,
          {
            confirmText:'Confirmar',
            cancelText:'Cancelar',
            confirm: async ()=>{
              const j = await apiPost('cartao_transferir_perda', {
                orig_codigo: orig,
                dest_codigo: cartaoAtual.codigo
              });
              if (!j.ok){
                showWarn('Não foi possível transferir', esc(j.error || 'Erro.'));
                return;
              }

              // Após transferir, carregar o DESTINO (cartão atual)
              setResumo(j.destino, j.saldo_destino || 0);
              await carregarMovs();

              showWarn(
                'Transferência concluída',
                `Histórico e saldo transferidos com sucesso.<br>
                 <div class="mt-2"><b>Destino:</b> <span class="mono">${esc(j.destino?.codigo || '')}</span></div>
                 <div class="mt-1"><b>Saldo da sessão (destino):</b> R$ ${fmtBRL(j.saldo_destino || 0)}</div>
                 <div class="mt-1 muted">Origem agora está: <b>${esc(String(j.origem?.status || '').toUpperCase())}</b></div>`
              );
            }
          }
        );
      });
    });
  }

  async function buscarCliente(){
    const term = (document.getElementById('buscaCliente').value || '').trim();
    if (term.length < 3){
      listaClientes.innerHTML = `<tr><td colspan="4" class="muted">Digite pelo menos 3 caracteres.</td></tr>`;
      return;
    }
    const j = await apiGet({action:'cartao_buscar_cliente', term});
    if (!j.ok){
      showWarn('Falha na busca', esc(j.error||'Erro.'));
      return;
    }
    renderResultados(j.resultados || []);
  }

  document.getElementById('btnBuscarCliente').addEventListener('click', buscarCliente);
  document.getElementById('buscaCliente').addEventListener('input', ()=>{
    clearTimeout(debounce);
    debounce = setTimeout(buscarCliente, 450);
  });

  // ===== câmera QR =====
  let stream = null;
  let usingBack = true;
  let detector = null;
  let scanTimer = null;

  async function stopCam(){
    clearInterval(scanTimer);
    scanTimer = null;
    if (stream){ stream.getTracks().forEach(t=>t.stop()); stream = null; }
    document.getElementById('camBox').style.display='none';
  }

  async function startCam(){
    if (!navigator.mediaDevices?.getUserMedia){
      showWarn('Câmera indisponível', 'Este navegador não suporta acesso à câmera.');
      return;
    }
    if (!('BarcodeDetector' in window)){
      showWarn('Leitor indisponível', 'Este navegador não suporta leitura nativa de QR. Cole ou digite o código.');
      return;
    }
    detector = new BarcodeDetector({formats:['qr_code']});

    try{
      stream = await navigator.mediaDevices.getUserMedia({
        video: { facingMode: usingBack ? 'environment' : 'user' },
        audio: false
      });
      const v = document.getElementById('video');
      v.srcObject = stream;
      await v.play();
      document.getElementById('camBox').style.display='block';

      scanTimer = setInterval(async ()=>{
        try{
          const codes = await detector.detect(v);
          if (codes && codes.length){
            const raw = codes[0].rawValue || '';
            const code = normalizeCodigo(raw);
            if (code){
              document.getElementById('codigo').value = code;
              await stopCam();
              await buscarCartao();
            }
          }
        }catch(_e){}
      }, 250);
    }catch(_e){
      showWarn('Não foi possível abrir a câmera', 'Verifique a permissão de câmera do navegador.');
    }
  }

  document.getElementById('btnAbrirCam').addEventListener('click', startCam);
  document.getElementById('btnFecharCam').addEventListener('click', stopCam);
  document.getElementById('btnTrocarCam').addEventListener('click', async ()=>{
    usingBack = !usingBack;
    await stopCam();
    await startCam();
  });

})();
</script>

</body>
</html>
