// js/components/pages/VendasDespacho.jsx
// [v224.32 FEATURE 20260526] Page dedicada estoquista vê vendas pendentes despacho
// + admin aprova/nega reimpressões. Substitui workflow whatsapp manual.
// Decisão Jamal: P1=3 dias úteis · P2=Semi-auto+backend · P3=printed_at+RPC ·
// P4=PDF simplificado · P-extra1=modal in-place · P-extra2=badge sidebar admin.
// Reuso: pdfShared.gerarDocumentoPDF (audience:'estoquista' v224.32 NEW param).
(function() {
  'use strict';
  const {useState, useEffect, useMemo, useCallback} = React;

  // Filter chips frete_type (cravado prod 90d · D1 Jamal cravou 5 chips)
  const FRETE_CHIPS = [
    {label:'Todos',          val:null,             bg:'#F3F4F6', fg:'#374151'},
    {label:'🛒 Retirada',    val:'Retirada',       bg:'#DCFCE7', fg:'#166534'},
    {label:'📦 Sedex',       val:'Sedex',          bg:'#DBEAFE', fg:'#1E40AF'},
    {label:'🚚 Transportadora', val:'Transportadora', bg:'#EDE9FE', fg:'#5B21B6'},
    {label:'📮 PAC',         val:'PAC',            bg:'#E0F2FE', fg:'#075985'},
    {label:'🛵 Motoboy',     val:'Motoboy',        bg:'#FED7AA', fg:'#9A3412'}
  ];

  // Status pill cores (printed_at + reprint_pending + reprint_approved_at + reprint_count combos)
  // [v224.32 HOTFIX SECURITY] Distingue PENDING_REQUEST (aguarda admin) vs PENDING_APPROVED (admin OK · pode imprimir)
  function getSaleStatus(sale){
    if(!sale.printed_at) return {key:'NOVA', label:'🆕 Nova', bg:'#FEF3C7', fg:'#92400E'};
    if(sale.reprint_pending && !sale.reprint_approved_at) return {key:'PENDING_REQUEST', label:'🟡 Aguardando admin', bg:'#FEF3C7', fg:'#78350F'};
    if(sale.reprint_pending && sale.reprint_approved_at) return {key:'PENDING_APPROVED', label:'🟢 Aprovada · Imprimir 2ª via', bg:'#DCFCE7', fg:'#166534'};
    if(sale.reprint_count > 0) return {key:'REPRINTED', label:'🔁 Reimpressa '+sale.reprint_count+'×', bg:'#E0E7FF', fg:'#3730A3'};
    return {key:'PRINTED', label:'✅ Impressa', bg:'#DCFCE7', fg:'#166534'};
  }

  // Helper: janela 3 dias úteis CONTANDO sábado (pula domingo) · D-3 Jamal
  function getWindowStartISO(){
    var today = new Date(); today.setHours(0,0,0,0);
    var count = (today.getDay() !== 0) ? 1 : 0; // se hoje não-domingo, conta dia 1
    var cur = new Date(today);
    while(count < 3){
      cur.setDate(cur.getDate() - 1);
      if(cur.getDay() !== 0) count++;
    }
    return cur.toISOString().slice(0,10);
  }

  function VendasDespacho({user, clients, allUsers, products}){
    const sb = window.sb;
    const isEstoquista = user?.role === 'estoquista';
    const isAdmin = user?.role === 'admin';
    // [v224.70] Flag composto: histórico + KPI tempo despacho são LEITURA (admin + estoquista)
    // Ações destrutivas (apagar/editar/reprint/bulk/filtro vendedora) MANTÊM isAdmin sozinho.
    const canViewHistory = isAdmin || isEstoquista;

    const [sales, setSales] = useState([]);
    const [loading, setLoading] = useState(true);
    const [fretFilter, setFretFilter] = useState(null);
    const [showPrinted, setShowPrinted] = useState(false);
    const [busyId, setBusyId] = useState(null);
    const [reprintModal, setReprintModal] = useState(null); // sale | null
    const [reprintReason, setReprintReason] = useState('');
    const [adminModal, setAdminModal] = useState(null); // sale | null
    const [adminNotes, setAdminNotes] = useState('');
    const [adminDenyReason, setAdminDenyReason] = useState('');
    // [v224.61 NUCLEAR] States admin god-mode
    const [detalheModal, setDetalheModal] = useState(null); // sale | null (feature D+F+G)
    const [editLogModal, setEditLogModal] = useState(null); // sale | null (feature B)
    const [editLogForm, setEditLogForm] = useState({ frete_type:'', embalagem_tipo:'', tracking_code:'', obs_transportadora:'', obs:'' });
    const [bulkMode, setBulkMode] = useState(false);
    const [bulkSelected, setBulkSelected] = useState(new Set()); // feature J
    const [bulkReason, setBulkReason] = useState('');
    const [sellerFilter, setSellerFilter] = useState(null); // feature I
    // [v224.64] Tab toggle + Histórico Impressas (admin only)
    const [viewTab, setViewTab] = useState('pending'); // 'pending' | 'printed' (admin)
    const [printedRangeStart, setPrintedRangeStart] = useState(function(){
      var d = new Date(); d.setDate(d.getDate() - 30);
      return d.toISOString().slice(0,10);
    });
    const [printedRangeEnd, setPrintedRangeEnd] = useState(function(){
      return new Date().toISOString().slice(0,10);
    });
    // [v224.97 filtro nº nota 20260601] busca livre VND-NNNN só em tab=printed (Jamal cravou 01/06 07:54 BRT)
    const [numberSearch, setNumberSearch] = useState('');

    const reload = useCallback(function(){
      setLoading(true);
      var q = sb.from('sales')
        .select('id,number,date,status,seller_id,seller_name,client_id,frete_type,frete,endereco_entrega,obs_transportadora,tracking_code,nf_enabled,printed_at,printed_by,reprint_count,reprint_pending,reprint_approved_at,admin_notes,deleted_at,deleted_by,deleted_reason,embalagem_tipo,obs,created_at,updated_at')
        .is('deleted_at', null);
      // [v224.64] 2 modos: pending (janela 3d úteis · não impressas) · printed (range admin · impressas)
      if(canViewHistory && viewTab === 'printed'){
        q = q.not('printed_at','is',null)
             .gte('printed_at', printedRangeStart + 'T00:00:00')
             .lte('printed_at', printedRangeEnd + 'T23:59:59')
             .order('printed_at', {ascending:false});
      } else {
        var winStart = getWindowStartISO();
        q = q.gte('date', winStart).order('created_at', {ascending:false});
      }

      // Admin: vê reprints pendentes (qualquer data) + janela normal
      // Estoquista: vê janela só
      q.then(function(r){
        if(r.error){
          console.error('[VendasDespacho] reload failed:', r.error.message);
          if(window.Sentry) window.Sentry.captureMessage('[VendasDespacho] reload failed: '+r.error.message,'error');
          setLoading(false);
          return;
        }
        var rows = r.data || [];
        // Fetch items separately se precisar (cards mostram nome venda + cliente)
        // Items aqui são opcionais (estoquista pode ver detalhes no PDF) · não bloqueio
        setSales(rows);
        setLoading(false);
      });
    }, [canViewHistory, viewTab, printedRangeStart, printedRangeEnd]);

    useEffect(function(){
      reload();
      // [v224.74] migração pra lib hardened (Wave 18 v223.45 pattern)
      // retry infinito + visibility resubscribe + heartbeat
      const handle = window.ZNX && window.ZNX.lib && window.ZNX.lib.realtime
        ? window.ZNX.lib.realtime.subscribe('sales', function(){ reload(); })
        : (function(){
            const ch = sb.channel('znx-vendas-despacho-page')
              .on('postgres_changes', {event:'*', schema:'public', table:'sales'}, reload)
              .subscribe();
            return { _ch: ch, _fallback: true };
          })();
      return function(){
        if(handle && handle._fallback) sb.removeChannel(handle._ch);
        else if(handle && window.ZNX?.lib?.realtime) window.ZNX.lib.realtime.unsubscribe(handle);
      };
    }, [reload]);

    // Filtered list
    const filtered = useMemo(function(){
      var list = sales;
      if(fretFilter) list = list.filter(function(s){ return s.frete_type === fretFilter; });
      // [v224.66 FIX 2026-05-29] Filter showPrinted SÓ se viewTab='pending'.
      // Em tab='printed' todos os cards são printed por definição (query SQL filtra) · NÃO re-filtrar.
      // Bug v224.64: 1067 vendas printed mas UI mostrava 0 porque !showPrinted ocultava todas.
      if(viewTab !== 'printed' && !showPrinted){
        list = list.filter(function(s){
          // mostra: nova (não impressa) OR reprint pendente
          return !s.printed_at || s.reprint_pending;
        });
      }
      return list;
    }, [sales, fretFilter, showPrinted, viewTab]);

    // [v224.40 BADGES] Contagem pending print por frete_type · decrementa quando imprime (reload)
    // PENDING_PRINT = !printed_at || reprint_pending (mesma lógica showPrinted=false)
    const pendingCountByFrete = useMemo(function(){
      var counts = { _total: 0 };
      FRETE_CHIPS.forEach(function(c){ if(c.val) counts[c.val] = 0; });
      sales.forEach(function(s){
        var isPending = !s.printed_at || s.reprint_pending;
        if(!isPending) return;
        counts._total++;
        if(counts.hasOwnProperty(s.frete_type)) counts[s.frete_type]++;
      });
      return counts;
    }, [sales]);

    function getSellerName(s){
      if(s.seller_name) return s.seller_name;
      var u = (allUsers||[]).find(function(x){ return x.id === s.seller_id; });
      return u ? (u.name || u.username || '—') : '—';
    }
    function getClientName(s){
      var c = (clients||[]).find(function(x){ return x.id === s.client_id; });
      return c ? (c.name || c.fantasyName || '—') : '—';
    }
    function getFreteMeta(fret){
      return FRETE_CHIPS.find(function(c){ return c.val === fret; }) || {bg:'#F3F4F6', fg:'#374151', label:fret||'—'};
    }

    // [v224.61] Helper · nome do estoquista que imprimiu (lookup allUsers)
    function getPrinterName(printedById){
      if(!printedById) return '—';
      const u = (allUsers || []).find(function(x){ return x.id === printedById; });
      return u?.name || u?.username || '—';
    }

    // [v224.66] Helper · formata duração entre lançamento e impressão (KPI tempo despacho)
    function formatDuration(start, end){
      if(!start || !end) return '—';
      try {
        var ms = new Date(end).getTime() - new Date(start).getTime();
        if(ms < 0) return '—';
        var totalMin = Math.floor(ms / 60000);
        if(totalMin < 60) return totalMin + 'min';
        var hours = Math.floor(totalMin / 60);
        var min = totalMin % 60;
        if(hours < 24) return hours + 'h ' + (min > 0 ? min + 'min' : '0min');
        var days = Math.floor(hours / 24);
        var remH = hours % 24;
        return days + 'd ' + (remH > 0 ? remH + 'h' : '');
      } catch(_) { return '—'; }
    }
    // [v224.66] Cor semáforo conforme tempo despacho (verde <1h · amarelo 1-4h · vermelho >4h)
    function getDurationColor(start, end){
      if(!start || !end) return { bg:'#F3F4F6', fg:'#374151', border:'#9CA3AF' };
      try {
        var hours = (new Date(end).getTime() - new Date(start).getTime()) / 3600000;
        if(hours < 1) return { bg:'#DCFCE7', fg:'#166534', border:'#16A34A' };       // verde · rápido
        if(hours < 4) return { bg:'#FEF3C7', fg:'#92400E', border:'#D97706' };       // amarelo · médio
        return { bg:'#FEE2E2', fg:'#991B1B', border:'#DC2626' };                     // vermelho · lento
      } catch(_) { return { bg:'#F3F4F6', fg:'#374151', border:'#9CA3AF' }; }
    }

    // [v224.61] Lista vendedoras únicas pra dropdown filtro (feature I)
    const sellersList = useMemo(function(){
      const set = new Set();
      sales.forEach(function(s){ if(s.seller_name) set.add(s.seller_name); });
      return Array.from(set).sort();
    }, [sales]);

    // [v224.61] Filtered com sellerFilter aplicado (feature I)
    // [v224.97] + numberSearch aplicado SÓ em viewTab='printed' (case-insensitive substring)
    const filteredWithSeller = useMemo(function(){
      var out = sellerFilter ? filtered.filter(function(s){ return s.seller_name === sellerFilter; }) : filtered;
      if(viewTab === 'printed' && numberSearch.trim()){
        var q = numberSearch.trim().toLowerCase();
        out = out.filter(function(s){ return String(s.number||'').toLowerCase().includes(q); });
      }
      return out;
    }, [filtered, sellerFilter, viewTab, numberSearch]);

    // [v224.61] Stats admin header (feature H)
    const adminStats = useMemo(function(){
      const today = new Date().toISOString().slice(0,10);
      const totalToday = sales.filter(function(s){ return s.date === today; }).length;
      const printedToday = sales.filter(function(s){ return s.date === today && s.printed_at; }).length;
      const pendingPrint = sales.filter(function(s){ return !s.printed_at || s.reprint_pending; }).length;
      const deletedToday = sales.filter(function(s){ return s.deleted_at && s.deleted_at.slice(0,10) === today; }).length;
      return { totalToday, printedToday, pendingPrint, deletedToday };
    }, [sales]);

    // [v224.61] Handler · admin direct reprint (feature C)
    async function handleAdminDirectReprint(sale){
      if(busyId || !isAdmin) return;
      if(!window.confirm('Reimprimir AGORA (admin · pula aprovação)? Audit log será criado.')) return;
      setBusyId(sale.id);
      try {
        const rpc = await sb.rpc('admin_direct_reprint_sale', { p_sale_id: sale.id });
        if(rpc.error) throw rpc.error;
        toast('🖨 Admin reprint OK · regerando PDF...', 'success');
        await handlePrint(sale);
        await reload();
      } catch(e){
        toast('❌ Erro: '+(e?.message||'unknown'), 'error');
      } finally { setBusyId(null); }
    }

    // [v224.61] Handler · soft delete admin (feature A)
    async function handleSoftDelete(sale){
      if(busyId || !isAdmin) return;
      const reason = window.prompt('Motivo da deleção (mín 3 chars):');
      if(!reason || reason.trim().length < 3){ toast('Motivo obrigatório', 'warning'); return; }
      if(!window.confirm('Apagar venda '+sale.number+'? (soft delete · admin_audit_log será gravado)')) return;
      setBusyId(sale.id);
      try {
        const rpc = await sb.rpc('soft_delete_sale_admin', { p_sale_id: sale.id, p_reason: reason });
        if(rpc.error) throw rpc.error;
        toast('🗑 '+sale.number+' apagada', 'success');
        await reload();
      } catch(e){
        toast('❌ Erro: '+(e?.message||'unknown'), 'error');
      } finally { setBusyId(null); }
    }

    // [v224.64] Handler · bulk mark sales as printed (rápido · UPDATE batch backend)
    async function handleBulkMarkPrinted(){
      if(bulkSelected.size === 0 || !isAdmin) return;
      if(bulkReason.trim().length < 3){ toast('⚠️ Escreva motivo no campo abaixo (pelo menos 3 letras)', 'warning'); return; }
      if(!window.confirm('Marcar '+bulkSelected.size+' vendas como JÁ IMPRESSAS? Estoquistas não verão mais na lista.')) return;
      const ids = Array.from(bulkSelected);
      // [v224.65 HOTFIX 2026-05-29] CHUNK_SIZE 500→10 · 12 triggers cascata em sales UPDATE
      // estourava timeout 8s. 110 vendas / 10 = 11 chunks × ~1s = ~13s total safe.
      const CHUNK_SIZE = 10;
      let totalMarked = 0, totalSkipped = 0;
      try {
        for(let i = 0; i < ids.length; i += CHUNK_SIZE){
          const chunk = ids.slice(i, i + CHUNK_SIZE);
          const chunkNum = Math.floor(i/CHUNK_SIZE) + 1;
          const totalChunks = Math.ceil(ids.length / CHUNK_SIZE);
          if(totalChunks > 1) toast('✅ Lote '+chunkNum+'/'+totalChunks+' ('+chunk.length+')...', 'info');
          const rpc = await sb.rpc('bulk_mark_sales_printed_admin', { p_sale_ids: chunk, p_reason: bulkReason });
          if(rpc.error){ toast('❌ Erro lote '+chunkNum+': '+rpc.error.message, 'error'); break; }
          totalMarked += rpc.data.marked_count || 0;
          totalSkipped += rpc.data.skipped_count || 0;
        }
        const msg = '✅ '+totalMarked+' marcadas como impressas'+(totalSkipped>0?' · '+totalSkipped+' já estavam':'');
        toast(msg, 'success');
        setBulkSelected(new Set()); setBulkMode(false); setBulkReason('');
        await reload();
      } catch(e){
        toast('❌ Erro: '+(e?.message||'unknown'), 'error');
      }
    }

    // [v224.61 + v224.63 CHUNKING 2026-05-29] Handler · bulk soft delete com chunks de 50
    // Backend RPC limit 50 por chamada (proteção payload) · frontend divide e itera transparente.
    // Toast progresso por chunk · admin vê 1/N · 2/N · 3/N acontecendo.
    async function handleBulkDelete(){
      if(bulkSelected.size === 0 || !isAdmin) return;
      if(bulkReason.trim().length < 3){ toast('⚠️ Escreva motivo no campo abaixo (pelo menos 3 letras)', 'warning'); return; }
      if(!window.confirm('Apagar '+bulkSelected.size+' vendas em lote? Audit log será gravado.')) return;
      const ids = Array.from(bulkSelected);
      const CHUNK_SIZE = 50;
      const totalChunks = Math.ceil(ids.length / CHUNK_SIZE);
      let totalSuccess = 0, totalFailed = 0, totalErrors = [];
      try {
        for(let i = 0; i < ids.length; i += CHUNK_SIZE){
          const chunk = ids.slice(i, i + CHUNK_SIZE);
          const chunkNum = Math.floor(i/CHUNK_SIZE) + 1;
          if(totalChunks > 1) toast('🗑 Lote '+chunkNum+'/'+totalChunks+' ('+chunk.length+' vendas)...', 'info');
          const rpc = await sb.rpc('bulk_soft_delete_sales_admin', { p_sale_ids: chunk, p_reason: bulkReason });
          if(rpc.error){
            toast('❌ Erro lote '+chunkNum+': '+rpc.error.message, 'error');
            totalErrors.push('lote '+chunkNum+': '+rpc.error.message);
            break;
          }
          totalSuccess += rpc.data.success_count || 0;
          totalFailed += rpc.data.failed_count || 0;
        }
        const finalMsg = '🗑 Total: '+totalSuccess+' apagadas'+(totalFailed > 0 ? ' · '+totalFailed+' falhou' : '');
        toast(finalMsg, totalFailed > 0 ? 'warning' : 'success');
        setBulkSelected(new Set()); setBulkMode(false); setBulkReason('');
        await reload();
      } catch(e){
        toast('❌ Erro bulk: '+(e?.message||'unknown'), 'error');
      }
    }

    // [v224.61] Handler · open edit logistics modal (feature B)
    function openEditLogistics(sale){
      if(!isAdmin) return;
      setEditLogForm({
        frete_type: sale.frete_type || '',
        embalagem_tipo: sale.embalagem_tipo || '',
        tracking_code: sale.tracking_code || '',
        obs_transportadora: sale.obs_transportadora || '',
        obs: sale.obs || ''
      });
      setEditLogModal(sale);
    }

    async function saveEditLogistics(){
      if(!editLogModal || !isAdmin) return;
      try {
        const rpc = await sb.rpc('admin_update_sale_logistics', {
          p_sale_id: editLogModal.id,
          p_payload: editLogForm
        });
        if(rpc.error) throw rpc.error;
        toast('✏ '+editLogModal.number+' logística atualizada', 'success');
        setEditLogModal(null);
        await reload();
      } catch(e){
        toast('❌ Erro: '+(e?.message||'unknown'), 'error');
      }
    }

    // Action: imprimir (1ª via OR 2ª via reprint approved)
    async function handlePrint(sale){
      if(busyId) return;
      setBusyId(sale.id);
      try {
        // 1. Gera PDF estoquista (audience preserva R$ off · MODO ENTREGA banner)
        if(typeof window.pdfShared?.gerarDocumentoPDF !== 'function'){
          toast('❌ pdfShared não carregado · Ctrl+Shift+R','error');
          setBusyId(null); return;
        }
        // [v224.34/35/36 HOTFIX P0 marathon 20260526] Fetch fresh sale COMPLETA com items + cliente JOIN.
        // v224.34: items eram NULL (SELECT inicial L63 sem sale_items)
        // v224.35: audience='estoquista' removed · PDF estoquista = PDF admin completo
        // v224.36: cliente "—" persiste · pdfShared L145 espera data.clientId camelCase + data.clientName
        //   · raw fetch traz só client_id snake_case · JOIN client + enrichment 7 fields
        //   · defesa em depth · INDEPENDENTE do clients prop (zero race condition)
        var fresh = await sb.from('sales')
          .select('*, items:sale_items(*), client:clients!sales_client_id_fkey(*)')
          .eq('id', sale.id).single();
        if(fresh.error){
          toast('❌ Erro buscar venda completa: '+fresh.error.message, 'error');
          setBusyId(null); return;
        }
        var c = fresh.data.client || {};
        // [v224.57 FIX 2026-05-28] enriquecimento camelCase COMPLETO pra pdfShared.js
        // VND-1398 Marcus Vinicius: estoquista despachou Sedex sem endereço/CPF/etc.
        // pdfShared L145 usa data.clientId (lookup client) · L346 data.sellerName · L348 data.paymentStatus.
        // Sem esses mappings client={} → clientRow(client.*) vazio → CPF/Tel/Endereço/Cidade/CEP somem.
        // regra_falha_silenciosa_proibida: warn defensivo se Sedex sem endereço.
        if(fresh.data.frete_type === 'Sedex' && !c.address){
          console.warn('[VendasDespacho] Sedex sem endereço cadastrado · cliente:', c.name || sale.id);
          if(window.Sentry) window.Sentry.captureMessage('[VendasDespacho] Sedex sem endereço · sale='+sale.number, 'warning');
        }
        var fullSale = Object.assign({}, fresh.data, {
          // [v224.68 FIX 20260529] createdAt + updatedAt camelCase pra pdfShared fmtH (L147).
          // Sem isso fallback cai em data.date date-only → JS UTC midnight → BRT 21:00 falso.
          createdAt: fresh.data.created_at,
          updatedAt: fresh.data.updated_at,
          clientId: fresh.data.client_id,           // L145 lookup client object via clients array
          sellerName: fresh.data.seller_name,        // L346 Vendedor
          paymentStatus: fresh.data.status,          // L348 Status (FATURA usa paymentStatus pra cor seal)
          clientName: c.name,
          clientDocument: c.document,
          clientPhone: c.phone,
          clientEmail: c.email,                      // L328 E-mail (redundante via lookup mas safe)
          clientAddress: c.address,
          clientCity: c.city,
          clientState: c.state,
          clientCep: c.cep
        });
        // [v224.101 RACE FIX 20260601] Inverter ordem: RPC PRIMEIRO + PDF DEPOIS.
        // Bug operacional 01/06: 3 vendas zumbis (VND-1567/1568/1575) em 15 criadas (20% rate).
        // Ordem antiga: PDF blocking window.print() OR jspdf throw silente -> RPC nunca chamada.
        // Ordem nova: RPC marca printed_at primeiro (FOR UPDATE atomicidade backend).
        // Refs: regra_falha_silenciosa_proibida + regra_pre_flight_pode_invalidar_solucao_proposta
        var rpc = await sb.rpc('mark_sale_printed', {p_sale_id: sale.id});
        if(rpc.error){
          toast('❌ '+rpc.error.message, 'error');
          if(window.Sentry) window.Sentry.captureMessage('[VendasDespacho] mark_sale_printed RPC failed pre-PDF', {level:'warning', extra:{sale_id: sale.id, sale_number: sale.number, error: rpc.error.message}});
          setBusyId(null); return;
        }
        var isReprint = rpc.data?.is_reprint;
        // [v224.101] PDF gerado APENAS apos RPC sucesso (printed_at cravado banco).
        try {
          await window.pdfShared.gerarDocumentoPDF({
            kind: 'FATURA',
            data: fullSale,
            clients: clients || []
          });
          toast(isReprint ? '✅ 2ª via impressa · reprint registrado' : '✅ Venda impressa · marcada no sistema', 'success');
        } catch(pdfErr) {
          // PDF falhou APOS RPC marcar printed_at · raro mas possivel (jspdf timeout)
          // Estoquista pode pedir reimpressao via workflow request_reprint existente
          console.error('[VendasDespacho] PDF gen failed post-RPC:', pdfErr);
          if(window.Sentry) window.Sentry.captureException(pdfErr, {tags:{feature:'vendas-despacho-print-post-rpc'}, extra:{sale_id: sale.id, sale_number: sale.number}});
          toast('⚠️ Marcada impressa mas PDF falhou · use "Solicitar reimpressão" se necessário', 'warning');
        }
        // Realtime propaga · reload via channel
      } catch(e){
        console.error('[VendasDespacho] handlePrint failed:', e);
        toast('❌ Erro impressão: '+(e?.message||'falha'), 'error');
        if(window.Sentry) window.Sentry.captureException(e, {tags:{feature:'vendas-despacho-print'}});
      }
      setBusyId(null);
    }

    async function handleRequestReprint(){
      if(!reprintModal) return;
      if(reprintReason.trim().length < 5){
        toast('Motivo mín 5 chars', 'error'); return;
      }
      setBusyId(reprintModal.id);
      var rpc = await sb.rpc('request_reprint', {p_sale_id: reprintModal.id, p_reason: reprintReason});
      if(rpc.error){
        toast('❌ '+rpc.error.message, 'error');
      } else {
        toast('🟡 Reimpressão solicitada · admin precisa aprovar', 'success');
        setReprintModal(null); setReprintReason('');
      }
      setBusyId(null);
    }

    async function handleAdminApprove(){
      if(!adminModal) return;
      setBusyId(adminModal.id);
      var rpc = await sb.rpc('approve_reprint', {p_sale_id: adminModal.id, p_notes: adminNotes || null});
      if(rpc.error){
        toast('❌ '+rpc.error.message, 'error');
      } else {
        toast('✅ Reimpressão aprovada · estoquista pode imprimir', 'success');
        setAdminModal(null); setAdminNotes('');
      }
      setBusyId(null);
    }

    async function handleAdminDeny(){
      if(!adminModal) return;
      if(adminDenyReason.trim().length < 5){
        toast('Motivo negação mín 5 chars', 'error'); return;
      }
      setBusyId(adminModal.id);
      var rpc = await sb.rpc('deny_reprint', {p_sale_id: adminModal.id, p_reason: adminDenyReason});
      if(rpc.error){
        toast('❌ '+rpc.error.message, 'error');
      } else {
        toast('Reimpressão negada · estoquista será notificado', 'success');
        setAdminModal(null); setAdminDenyReason('');
      }
      setBusyId(null);
    }

    function renderActionButton(sale){
      const st = getSaleStatus(sale);
      const busy = busyId === sale.id;

      // ADMIN view: se reprint pendente · botão Revisar Aprovação
      if(isAdmin && sale.reprint_pending){
        return (
          <button onClick={function(){ setAdminModal(sale); setAdminNotes(''); setAdminDenyReason(''); }} disabled={busy}
            style={{padding:'8px 14px',background:'#B89840',color:'#fff',border:'none',borderRadius:6,fontSize:12,fontWeight:700,cursor:'pointer'}}>
            🟡 Revisar Aprovação
          </button>
        );
      }
      // Nova (não impressa): botão Imprimir
      if(st.key === 'NOVA'){
        return (
          <button onClick={function(){ handlePrint(sale); }} disabled={busy}
            style={{padding:'8px 14px',background:busy?'#9CA3AF':'#15803D',color:'#fff',border:'none',borderRadius:6,fontSize:12,fontWeight:700,cursor:busy?'not-allowed':'pointer'}}>
            {busy ? '⏳ Imprimindo...' : '🖨️ Imprimir'}
          </button>
        );
      }
      // [v224.32 HOTFIX SECURITY] Distingue 2 estados pending via reprint_approved_at:
      //   PENDING_REQUEST: estoquista solicitou MAS admin ainda não aprovou · BLOQUEIA imprimir
      //   PENDING_APPROVED: admin aprovou · estoquista pode imprimir 2ª via (mark_sale_printed incrementa + limpa)
      if(st.key === 'PENDING_REQUEST'){
        if(isEstoquista){
          return (
            <div style={{display:'flex',flexDirection:'column',alignItems:'flex-end',gap:2}}>
              <span style={{fontSize:11,color:'#78350F',fontWeight:600}}>🟡 Aguardando admin</span>
              <span style={{fontSize:9,color:'#9CA3AF'}}>botão liberado pós-aprovação</span>
            </div>
          );
        }
        // Admin: já tem botão Revisar Aprovação acima (renderiza via reprint_pending check no topo)
        return <span style={{fontSize:11,color:'#78350F',fontWeight:600}}>🟡 Aguarda você</span>;
      }
      if(st.key === 'PENDING_APPROVED'){
        if(isEstoquista){
          return (
            <button onClick={function(){ handlePrint(sale); }} disabled={busy}
              style={{padding:'8px 14px',background:busy?'#9CA3AF':'#15803D',color:'#fff',border:'none',borderRadius:6,fontSize:12,fontWeight:700,cursor:busy?'not-allowed':'pointer'}}>
              {busy ? '⏳ Imprimindo...' : '🖨️ Imprimir 2ª via'}
            </button>
          );
        }
        return <span style={{fontSize:11,color:'#166534',fontWeight:600}}>🟢 Aprovada · estoquista pode imprimir</span>;
      }
      // Impressa (sem reprint pendente): link solicitar reimpressão
      return (
        <div style={{display:'flex',gap:8,alignItems:'center'}}>
          <span style={{fontSize:11,color:'#166534',fontWeight:600}}>✅ Impressa</span>
          {isEstoquista && (
            <button onClick={function(){ setReprintModal(sale); setReprintReason(''); }}
              style={{padding:'4px 10px',background:'transparent',color:'#6B7280',border:'1px solid #D1D5DB',borderRadius:4,fontSize:10,cursor:'pointer'}}>
              Solicitar reimpressão
            </button>
          )}
        </div>
      );
    }

    return (
      <div style={{padding:'4px',minHeight:'100vh'}}>
        <div style={{marginBottom:18}}>
          <h2 style={{fontSize:24,fontWeight:700,color:'#111827',margin:0}}>🛒 Vendas a Despachar</h2>
          <div style={{fontSize:13,color:'#6B7280',marginTop:4}}>
            {filtered.length} venda{filtered.length!==1?'s':''} · janela 3 dias úteis (pula domingo)
            {isAdmin && (() => {
              const rp = sales.filter(function(s){ return s.reprint_pending; }).length;
              return rp > 0 ? <span style={{marginLeft:10,color:'#B45309',fontWeight:600}}> · 🟡 {rp} reprint{rp!==1?'s':''} aguardando você</span> : null;
            })()}
          </div>
        </div>

        {/* [v224.64+v224.70] Tab toggle: A Despachar vs Histórico Impressas (admin + estoquista) */}
        {canViewHistory && (
          <div style={{display:'flex',gap:6,marginBottom:14,borderBottom:'1px solid #E5E7EB',paddingBottom:4}}>
            <button onClick={()=>setViewTab('pending')}
              style={{padding:'8px 16px',background:viewTab==='pending'?'#1B2A4A':'transparent',color:viewTab==='pending'?'#FFF':'#6B7280',border:'none',borderRadius:'6px 6px 0 0',fontSize:13,fontWeight:700,cursor:'pointer'}}>
              📦 A Despachar{viewTab==='pending' ? ' ('+filteredWithSeller.length+')' : ''}
            </button>
            <button onClick={()=>setViewTab('printed')}
              style={{padding:'8px 16px',background:viewTab==='printed'?'#1B2A4A':'transparent',color:viewTab==='printed'?'#FFF':'#6B7280',border:'none',borderRadius:'6px 6px 0 0',fontSize:13,fontWeight:700,cursor:'pointer'}}>
              📜 Histórico Impressas{viewTab==='printed' ? ' ('+filteredWithSeller.length+')' : ''}
            </button>
          </div>
        )}
        {/* [v224.64+v224.70] Date range filter (só visível em tab printed) */}
        {canViewHistory && viewTab === 'printed' && (
          <div style={{display:'flex',gap:10,marginBottom:14,alignItems:'center',flexWrap:'wrap'}}>
            <span style={{fontSize:12,color:'#6B7280',fontWeight:600}}>📅 Período:</span>
            <input type="date" value={printedRangeStart} onChange={e=>setPrintedRangeStart(e.target.value)}
              style={{padding:'6px 10px',border:'1px solid #D1D5DB',borderRadius:6,fontSize:12}}/>
            <span style={{color:'#9CA3AF'}}>→</span>
            <input type="date" value={printedRangeEnd} onChange={e=>setPrintedRangeEnd(e.target.value)}
              style={{padding:'6px 10px',border:'1px solid #D1D5DB',borderRadius:6,fontSize:12}}/>
            <button onClick={()=>{
              var d = new Date(); d.setDate(d.getDate() - 7);
              setPrintedRangeStart(d.toISOString().slice(0,10));
              setPrintedRangeEnd(new Date().toISOString().slice(0,10));
            }}
              style={{padding:'4px 10px',background:'#EFF6FF',color:'#1E40AF',border:'1px solid #93C5FD',borderRadius:6,fontSize:11,cursor:'pointer'}}>
              7d
            </button>
            <button onClick={()=>{
              var d = new Date(); d.setDate(d.getDate() - 30);
              setPrintedRangeStart(d.toISOString().slice(0,10));
              setPrintedRangeEnd(new Date().toISOString().slice(0,10));
            }}
              style={{padding:'4px 10px',background:'#EFF6FF',color:'#1E40AF',border:'1px solid #93C5FD',borderRadius:6,fontSize:11,cursor:'pointer'}}>
              30d
            </button>
            <button onClick={()=>{
              var d = new Date(); d.setDate(d.getDate() - 90);
              setPrintedRangeStart(d.toISOString().slice(0,10));
              setPrintedRangeEnd(new Date().toISOString().slice(0,10));
            }}
              style={{padding:'4px 10px',background:'#EFF6FF',color:'#1E40AF',border:'1px solid #93C5FD',borderRadius:6,fontSize:11,cursor:'pointer'}}>
              90d
            </button>
            {/* [v224.97] busca livre nº nota · só visível em tab printed · Jamal cravou 01/06 */}
            <input type="text" value={numberSearch} onChange={e=>setNumberSearch(e.target.value)}
              placeholder="🔍 Buscar nº nota (ex: 1537)"
              style={{padding:'6px 10px',border:'1px solid #D1D5DB',borderRadius:6,fontSize:12,width:180,marginLeft:8}}/>
            {numberSearch&&<button onClick={()=>setNumberSearch('')}
              style={{padding:'4px 10px',background:'transparent',color:'#9CA3AF',border:'1px solid #E5E7EB',borderRadius:6,fontSize:11,cursor:'pointer'}}>
              × limpar
            </button>}
          </div>
        )}

        {/* [v224.61+v224.70] FEATURE H · Stats cards (admin + estoquista) */}
        {canViewHistory && (
          <div style={{display:'grid',gridTemplateColumns:'repeat(4,1fr)',gap:10,marginBottom:14}}>
            <div style={{padding:'10px 14px',background:'#EFF6FF',borderLeft:'3px solid #2563EB',borderRadius:8}}>
              <div style={{fontSize:10,color:'#1E40AF',fontWeight:600,textTransform:'uppercase'}}>Total hoje</div>
              <div style={{fontSize:20,fontWeight:800,color:'#1E40AF'}}>{adminStats.totalToday}</div>
            </div>
            <div style={{padding:'10px 14px',background:'#ECFDF5',borderLeft:'3px solid #059669',borderRadius:8}}>
              <div style={{fontSize:10,color:'#065F46',fontWeight:600,textTransform:'uppercase'}}>Impressas hoje</div>
              <div style={{fontSize:20,fontWeight:800,color:'#065F46'}}>{adminStats.printedToday}</div>
            </div>
            <div style={{padding:'10px 14px',background:'#FEF3C7',borderLeft:'3px solid #D97706',borderRadius:8}}>
              <div style={{fontSize:10,color:'#92400E',fontWeight:600,textTransform:'uppercase'}}>Pendentes</div>
              <div style={{fontSize:20,fontWeight:800,color:'#92400E'}}>{adminStats.pendingPrint}</div>
            </div>
            <div style={{padding:'10px 14px',background:'#FEE2E2',borderLeft:'3px solid #DC2626',borderRadius:8}}>
              <div style={{fontSize:10,color:'#991B1B',fontWeight:600,textTransform:'uppercase'}}>Apagadas hoje</div>
              <div style={{fontSize:20,fontWeight:800,color:'#991B1B'}}>{adminStats.deletedToday}</div>
            </div>
          </div>
        )}

        {/* [v224.61] FEATURE I · Filtro vendedora · FEATURE J · Bulk mode toggle */}
        {isAdmin && (
          <div style={{display:'flex',gap:10,marginBottom:10,flexWrap:'wrap',alignItems:'center'}}>
            <select value={sellerFilter||''} onChange={e=>setSellerFilter(e.target.value||null)}
              style={{padding:'6px 10px',border:'1px solid #D1D5DB',borderRadius:6,fontSize:12,background:'#FFF'}}>
              <option value="">🔎 Todas vendedoras</option>
              {sellersList.map(name => <option key={name} value={name}>{name}</option>)}
            </select>
            <button onClick={()=>{ setBulkMode(!bulkMode); if(bulkMode){ setBulkSelected(new Set()); setBulkReason(''); }}}
              style={{padding:'6px 12px',background:bulkMode?'#DC2626':'#374151',color:'#FFF',border:'none',borderRadius:6,fontSize:12,fontWeight:600,cursor:'pointer'}}>
              {bulkMode ? '✖ Sair Bulk ('+bulkSelected.size+' sel.)' : '☑ Bulk Apagar'}
            </button>
            {/* [v224.62] Toggle "Selecionar todos visíveis" / "Desmarcar todos" */}
            {bulkMode && filteredWithSeller.length > 0 && (
              <button onClick={()=>{
                if(bulkSelected.size === filteredWithSeller.length){
                  setBulkSelected(new Set());
                } else {
                  setBulkSelected(new Set(filteredWithSeller.map(function(s){ return s.id; })));
                }
              }}
                style={{padding:'6px 12px',background:'#1E40AF',color:'#FFF',border:'none',borderRadius:6,fontSize:12,fontWeight:600,cursor:'pointer'}}>
                {bulkSelected.size === filteredWithSeller.length
                  ? '☐ Desmarcar todos'
                  : '☑ Selecionar todos visíveis ('+filteredWithSeller.length+')'}
              </button>
            )}
            {bulkMode && bulkSelected.size > 0 && (
              <>
                <input type="text" value={bulkReason} onChange={e=>setBulkReason(e.target.value)}
                  placeholder="Motivo bulk (mín 3 chars)"
                  style={{padding:'6px 10px',border:'1px solid #FCA5A5',borderRadius:6,fontSize:12,minWidth:240}}/>
                <button onClick={handleBulkMarkPrinted}
                  style={{padding:'6px 12px',background:'#059669',color:'#FFF',border:'none',borderRadius:6,fontSize:12,fontWeight:700,cursor:'pointer'}}>
                  ✅ Marcar {bulkSelected.size} como já impressas
                </button>
                <button onClick={handleBulkDelete}
                  style={{padding:'6px 12px',background:'#DC2626',color:'#FFF',border:'none',borderRadius:6,fontSize:12,fontWeight:700,cursor:'pointer'}}>
                  🗑 Apagar {bulkSelected.size}
                </button>
              </>
            )}
          </div>
        )}

        {/* [v224.40] Filter chips frete_type com badges contagem pending · decrementa quando imprime */}
        <div style={{display:'flex',gap:8,marginBottom:12,flexWrap:'wrap'}}>
          {FRETE_CHIPS.map(function(c){
            var active = fretFilter === c.val;
            var count = c.val === null ? pendingCountByFrete._total : (pendingCountByFrete[c.val] || 0);
            return (
              <div key={c.label} style={{position:'relative',display:'inline-block'}}>
                <button onClick={function(){ setFretFilter(c.val); }}
                  style={{
                    padding:'6px 14px',fontSize:12,fontWeight:600,cursor:'pointer',
                    border:'1px solid '+(active?'#B89840':'#E5E7EB'),
                    background:active?c.bg:'#fff',
                    color:active?c.fg:'#6B7280',
                    borderRadius:8
                  }}>
                  {c.label}
                </button>
                {count > 0 && (
                  <span style={{
                    position:'absolute', top:-7, right:-7,
                    background:'#DC2626', color:'#fff',
                    borderRadius:999, fontSize:10, fontWeight:700,
                    minWidth:20, height:20, lineHeight:'20px',
                    textAlign:'center', padding:'0 5px',
                    border:'2px solid #fff',
                    boxShadow:'0 1px 3px rgba(0,0,0,0.15)'
                  }}>{count}</span>
                )}
              </div>
            );
          })}
        </div>

        {/* Toggle mostrar impressas */}
        <div style={{marginBottom:14,display:'flex',gap:14,alignItems:'center',fontSize:12,color:'#6B7280'}}>
          <label style={{display:'flex',alignItems:'center',gap:6,cursor:'pointer'}}>
            <input type="checkbox" checked={showPrinted} onChange={function(e){ setShowPrinted(e.target.checked); }}/>
            Mostrar já impressas
          </label>
        </div>

        {loading ? (
          <div style={{padding:30,textAlign:'center',color:'#6B7280'}}>Carregando...</div>
        ) : filtered.length === 0 ? (
          <div style={{padding:50,textAlign:'center',color:'#9CA3AF',background:'#fff',borderRadius:12,border:'1px solid #E5E7EB'}}>
            🎉 Nenhuma venda pendente despacho!
          </div>
        ) : (
          <div style={{display:'grid',gridTemplateColumns:'repeat(auto-fill,minmax(380px,1fr))',gap:12}}>
            {filteredWithSeller.map(function(s){
              const fretMeta = getFreteMeta(s.frete_type);
              const st = getSaleStatus(s);
              const ee = s.endereco_entrega || {};
              const enderecoLinha = ee.cidade ? (ee.cidade + (ee.estado ? '/'+ee.estado : '')) : (s.frete_type === 'Retirada' ? '— retirada na loja —' : '—');
              return (
                <div key={s.id} style={{background:'#fff',border:'1px solid #E5E7EB',borderRadius:10,padding:14,boxShadow:'0 1px 3px rgba(0,0,0,.04)'}}>
                  {/* Top: badges */}
                  <div style={{display:'flex',justifyContent:'space-between',alignItems:'flex-start',marginBottom:10,gap:8}}>
                    <span style={{padding:'4px 12px',borderRadius:6,fontSize:12,fontWeight:700,background:fretMeta.bg,color:fretMeta.fg}}>
                      {fretMeta.label}
                    </span>
                    <span style={{padding:'3px 9px',borderRadius:999,fontSize:10,fontWeight:600,background:st.bg,color:st.fg,whiteSpace:'nowrap'}}>
                      {st.label}
                    </span>
                  </div>
                  {/* Number + cliente + vendedora */}
                  <div style={{fontSize:14,fontWeight:700,color:'#1B2A4A',marginBottom:2}}>{s.number}</div>
                  <div style={{fontSize:12,color:'#374151',marginBottom:2}}>👤 {getClientName(s)}</div>
                  <div style={{fontSize:11,color:'#6B7280',marginBottom:6}}>
                    Vendedora: {getSellerName(s)} · {new Date(s.created_at).toLocaleDateString('pt-BR')} {new Date(s.created_at).toLocaleTimeString('pt-BR',{hour:'2-digit',minute:'2-digit'})}
                  </div>
                  {/* Endereço resumo */}
                  <div style={{fontSize:11,color:'#6B7280',padding:'6px 8px',background:'#F9FAFB',borderRadius:4,marginBottom:10}}>
                    📍 {enderecoLinha}
                    {s.tracking_code && <div style={{color:'#B89840',marginTop:2}}>📦 {s.tracking_code}</div>}
                  </div>
                  {/* [v224.42 ENTREGA] Badge laranja info data+hora se Transportadora · pro estoquista ver direto no card */}
                  {s.frete_type === 'Transportadora' && (ee.delivery_date || ee.delivery_time) && (
                    <div style={{marginBottom:10,padding:'5px 9px',background:'#FFF7ED',borderLeft:'3px solid #EA580C',borderRadius:4,fontSize:11,color:'#9A3412',fontWeight:600}}>
                      🚚 Entrega: {ee.delivery_date ? new Date(ee.delivery_date+'T00:00:00').toLocaleDateString('pt-BR') : '—'}
                      {ee.delivery_time && ' às ' + ee.delivery_time}
                    </div>
                  )}
                  {/* [v224.61] FEATURE E · Horário impressão visível (compacto · tab A Despachar) */}
                  {viewTab !== 'printed' && s.printed_at && (
                    <div style={{fontSize:10,color:'#166534',marginTop:2,marginBottom:6}}>
                      🖨 Impressa {new Date(s.printed_at).toLocaleString('pt-BR', { hour:'2-digit', minute:'2-digit', day:'2-digit', month:'2-digit' })}
                      {' · por '+getPrinterName(s.printed_by)}
                      {s.reprint_count > 0 && <span style={{color:'#B45309'}}> · {s.reprint_count}× reimpressões</span>}
                    </div>
                  )}
                  {/* [v224.66] Bloco KPI tempo despacho · só aparece em tab Histórico (admin only via tab gate) */}
                  {viewTab === 'printed' && s.printed_at && (() => {
                    const col = getDurationColor(s.created_at, s.printed_at);
                    return (
                      <div style={{marginTop:6,padding:'8px 10px',background:'#F9FAFB',borderRadius:6,fontSize:11,borderLeft:'3px solid '+col.border}}>
                        <div style={{color:'#6B7280'}}>🛒 Lançada: {new Date(s.created_at).toLocaleString('pt-BR', {day:'2-digit',month:'2-digit',hour:'2-digit',minute:'2-digit'})}</div>
                        <div style={{color:'#374151',marginTop:2}}>🖨 Impressa por: <strong>{getPrinterName(s.printed_by)}</strong></div>
                        <div style={{color:'#6B7280',marginTop:2}}>🕐 às {new Date(s.printed_at).toLocaleString('pt-BR', {day:'2-digit',month:'2-digit',hour:'2-digit',minute:'2-digit'})}</div>
                        <div style={{marginTop:4,display:'inline-block',padding:'3px 8px',borderRadius:4,background:col.bg,color:col.fg,fontWeight:700,fontSize:11}}>
                          ⏱ Tempo de despacho: {formatDuration(s.created_at, s.printed_at)}
                        </div>
                      </div>
                    );
                  })()}
                  {/* Action button + admin actions */}
                  <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',gap:8,flexWrap:'wrap'}}>
                    {/* [v224.61] FEATURE J · Checkbox bulk mode */}
                    {isAdmin && bulkMode && (
                      <input type="checkbox" checked={bulkSelected.has(s.id)}
                        onChange={e=>{
                          const nset = new Set(bulkSelected);
                          if(e.target.checked) nset.add(s.id); else nset.delete(s.id);
                          setBulkSelected(nset);
                        }}
                        style={{transform:'scale(1.4)',marginRight:8,cursor:'pointer'}}
                      />
                    )}
                    <div style={{flex:1,display:'flex',justifyContent:'flex-end'}}>
                      {renderActionButton(s)}
                    </div>
                  </div>
                  {/* [v224.61] FEATURES A+B+C+D · 4 botões admin god-mode (gated) */}
                  {isAdmin && (
                    <div style={{display:'flex',gap:6,marginTop:8,flexWrap:'wrap'}}>
                      <button onClick={()=>setDetalheModal(s)} title="Ver detalhes + histórico + notas"
                        style={{padding:'4px 10px',background:'#EFF6FF',color:'#1E40AF',border:'1px solid #93C5FD',borderRadius:6,fontSize:11,fontWeight:600,cursor:'pointer'}}>
                        👁 Detalhes
                      </button>
                      <button onClick={()=>openEditLogistics(s)} title="Editar frete/embalagem/tracking"
                        style={{padding:'4px 10px',background:'#FEF3C7',color:'#92400E',border:'1px solid #FCD34D',borderRadius:6,fontSize:11,fontWeight:600,cursor:'pointer'}}>
                        ✏ Editar
                      </button>
                      {s.printed_at && (
                        <button onClick={()=>handleAdminDirectReprint(s)} disabled={busyId===s.id} title="Reimprimir SEM aprovação (admin)"
                          style={{padding:'4px 10px',background:'#ECFDF5',color:'#065F46',border:'1px solid #6EE7B7',borderRadius:6,fontSize:11,fontWeight:600,cursor:'pointer'}}>
                          🖨 Reimprimir agora
                        </button>
                      )}
                      <button onClick={()=>handleSoftDelete(s)} disabled={busyId===s.id} title="Apagar venda (soft + audit log)"
                        style={{padding:'4px 10px',background:'#FEE2E2',color:'#991B1B',border:'1px solid #FCA5A5',borderRadius:6,fontSize:11,fontWeight:600,cursor:'pointer'}}>
                        🗑 Apagar
                      </button>
                    </div>
                  )}
                </div>
              );
            })}
          </div>
        )}

        {/* Modal: estoquista solicita reimpressão */}
        {reprintModal && (
          <div onClick={function(){ setReprintModal(null); setReprintReason(''); }}
            style={{position:'fixed',inset:0,background:'rgba(0,0,0,.5)',display:'flex',alignItems:'center',justifyContent:'center',zIndex:9999}}>
            <div onClick={function(e){ e.stopPropagation(); }}
              style={{background:'#fff',padding:24,borderRadius:12,minWidth:380,maxWidth:480,boxShadow:'0 10px 40px rgba(0,0,0,.2)'}}>
              <h3 style={{margin:'0 0 14px',fontSize:16,fontWeight:700,color:'#1B2A4A'}}>Solicitar Reimpressão</h3>
              <div style={{fontSize:13,color:'#6B7280',marginBottom:14}}>
                Venda <strong>{reprintModal.number}</strong> · {getClientName(reprintModal)}
              </div>
              <label style={{fontSize:11,color:'#6B7280',fontWeight:600,display:'block',marginBottom:4}}>MOTIVO (mín 5 chars · admin vai ver)</label>
              <textarea value={reprintReason} onChange={function(e){ setReprintReason(e.target.value); }}
                placeholder="Ex: PDF rasgou · Cliente pediu cópia extra · Erro no endereço · etc"
                style={{width:'100%',padding:'8px 10px',border:'1px solid #D1D5DB',borderRadius:5,fontSize:13,minHeight:80,resize:'vertical',marginBottom:14}}/>
              <div style={{display:'flex',gap:8,justifyContent:'flex-end'}}>
                <button onClick={function(){ setReprintModal(null); setReprintReason(''); }}
                  style={{padding:'8px 16px',border:'1px solid #D1D5DB',background:'#fff',borderRadius:5,cursor:'pointer',fontSize:13}}>Cancelar</button>
                <button onClick={handleRequestReprint} disabled={busyId === reprintModal.id || reprintReason.trim().length < 5}
                  style={{padding:'8px 18px',background:(busyId === reprintModal.id || reprintReason.trim().length < 5)?'#9CA3AF':'#B89840',color:'#fff',border:'none',borderRadius:5,cursor:'pointer',fontSize:13,fontWeight:700}}>
                  {busyId === reprintModal.id ? '⏳ Solicitando...' : 'Solicitar'}
                </button>
              </div>
            </div>
          </div>
        )}

        {/* Modal: admin aprovar/negar reimpressão (P-extra1=A in-place) */}
        {adminModal && (
          <div onClick={function(){ setAdminModal(null); setAdminNotes(''); setAdminDenyReason(''); }}
            style={{position:'fixed',inset:0,background:'rgba(0,0,0,.5)',display:'flex',alignItems:'center',justifyContent:'center',zIndex:9999}}>
            <div onClick={function(e){ e.stopPropagation(); }}
              style={{background:'#fff',padding:24,borderRadius:12,minWidth:420,maxWidth:520,boxShadow:'0 10px 40px rgba(0,0,0,.2)'}}>
              <h3 style={{margin:'0 0 14px',fontSize:16,fontWeight:700,color:'#1B2A4A'}}>Revisar Reimpressão</h3>
              <div style={{fontSize:13,color:'#374151',marginBottom:6}}>
                Venda <strong>{adminModal.number}</strong> · {getClientName(adminModal)}
              </div>
              <div style={{fontSize:11,color:'#6B7280',marginBottom:14}}>
                Vendedora: {getSellerName(adminModal)} · 1ª impressão: {adminModal.printed_at ? new Date(adminModal.printed_at).toLocaleString('pt-BR') : '—'}
                {adminModal.reprint_count > 0 && <span> · Já reimpressa {adminModal.reprint_count}× antes</span>}
                {adminModal.reprint_approved_at && <span style={{color:'#15803D',fontWeight:600}}> · 🟢 Já aprovada às {new Date(adminModal.reprint_approved_at).toLocaleString('pt-BR')}</span>}
              </div>
              <div style={{padding:'10px 14px',background:'#FEF3C7',borderRadius:6,fontSize:12,color:'#78350F',marginBottom:14}}>
                ⚠️ Estoquista solicitou reimpressão. Audit log já registra o motivo da request via RPC.
              </div>

              <label style={{fontSize:11,color:'#15803D',fontWeight:600,display:'block',marginBottom:4}}>NOTAS APROVAÇÃO (opcional)</label>
              <input type="text" value={adminNotes} onChange={function(e){ setAdminNotes(e.target.value); }}
                placeholder="Ex: OK · cliente legítimo"
                style={{width:'100%',padding:'7px 10px',border:'1px solid #D1D5DB',borderRadius:5,fontSize:13,marginBottom:14}}/>

              <label style={{fontSize:11,color:'#991B1B',fontWeight:600,display:'block',marginBottom:4}}>MOTIVO NEGAÇÃO (obrigatório se negar · mín 5)</label>
              <input type="text" value={adminDenyReason} onChange={function(e){ setAdminDenyReason(e.target.value); }}
                placeholder="Ex: Já reimpressa muitas vezes · Suspeita de fraude"
                style={{width:'100%',padding:'7px 10px',border:'1px solid #D1D5DB',borderRadius:5,fontSize:13,marginBottom:14}}/>

              <div style={{display:'flex',gap:8,justifyContent:'flex-end'}}>
                <button onClick={function(){ setAdminModal(null); setAdminNotes(''); setAdminDenyReason(''); }}
                  style={{padding:'8px 16px',border:'1px solid #D1D5DB',background:'#fff',borderRadius:5,cursor:'pointer',fontSize:13}}>Fechar</button>
                <button onClick={handleAdminDeny} disabled={busyId === adminModal.id || adminDenyReason.trim().length < 5}
                  style={{padding:'8px 16px',background:(busyId === adminModal.id || adminDenyReason.trim().length < 5)?'#9CA3AF':'#991B1B',color:'#fff',border:'none',borderRadius:5,cursor:'pointer',fontSize:13,fontWeight:600}}>
                  ✕ Negar
                </button>
                <button onClick={handleAdminApprove} disabled={busyId === adminModal.id}
                  style={{padding:'8px 18px',background:busyId === adminModal.id?'#9CA3AF':'#15803D',color:'#fff',border:'none',borderRadius:5,cursor:'pointer',fontSize:13,fontWeight:700}}>
                  ✓ Aprovar
                </button>
              </div>
            </div>
          </div>
        )}

        {/* [v224.61] FEATURE D · Modal detalhes (carrega via window registry) */}
        {detalheModal && window.AdminVendaDetalheModal && (
          <window.AdminVendaDetalheModal sale={detalheModal} isOpen={true}
            onClose={()=>setDetalheModal(null)}
            onAfterSave={()=>reload()}
          />
        )}

        {/* [v224.61] FEATURE B · Modal editar logística */}
        {editLogModal && (
          <div onClick={e=>{if(e.target===e.currentTarget) setEditLogModal(null);}}
            style={{position:'fixed',inset:0,background:'rgba(0,0,0,0.6)',zIndex:100001,display:'flex',alignItems:'center',justifyContent:'center',padding:16}}>
            <div style={{background:'#FFF',borderRadius:12,maxWidth:500,width:'100%',padding:20}}>
              <div style={{fontSize:16,fontWeight:700,marginBottom:14,color:'#111827'}}>✏ Editar logística · {editLogModal.number}</div>
              <div style={{display:'flex',flexDirection:'column',gap:10}}>
                <label style={{fontSize:12}}>Frete tipo
                  <select value={editLogForm.frete_type} onChange={e=>setEditLogForm({...editLogForm,frete_type:e.target.value})}
                    style={{width:'100%',padding:'6px 10px',marginTop:4,border:'1px solid #D1D5DB',borderRadius:6,fontSize:12}}>
                    <option value="">—</option>
                    <option>Retirada</option><option>Sedex</option><option>Transportadora</option><option>PAC</option><option>Motoboy</option>
                  </select>
                </label>
                <label style={{fontSize:12}}>Embalagem
                  <input value={editLogForm.embalagem_tipo} onChange={e=>setEditLogForm({...editLogForm,embalagem_tipo:e.target.value})}
                    style={{width:'100%',padding:'6px 10px',marginTop:4,border:'1px solid #D1D5DB',borderRadius:6,fontSize:12}}/>
                </label>
                <label style={{fontSize:12}}>Tracking code
                  <input value={editLogForm.tracking_code} onChange={e=>setEditLogForm({...editLogForm,tracking_code:e.target.value})}
                    style={{width:'100%',padding:'6px 10px',marginTop:4,border:'1px solid #D1D5DB',borderRadius:6,fontSize:12}}/>
                </label>
                <label style={{fontSize:12}}>Obs transportadora
                  <input value={editLogForm.obs_transportadora} onChange={e=>setEditLogForm({...editLogForm,obs_transportadora:e.target.value})}
                    style={{width:'100%',padding:'6px 10px',marginTop:4,border:'1px solid #D1D5DB',borderRadius:6,fontSize:12}}/>
                </label>
                <label style={{fontSize:12}}>Obs geral
                  <textarea value={editLogForm.obs} onChange={e=>setEditLogForm({...editLogForm,obs:e.target.value})}
                    style={{width:'100%',padding:'6px 10px',marginTop:4,border:'1px solid #D1D5DB',borderRadius:6,fontSize:12,minHeight:50,fontFamily:'inherit'}}/>
                </label>
              </div>
              <div style={{display:'flex',justifyContent:'flex-end',gap:8,marginTop:16}}>
                <button onClick={()=>setEditLogModal(null)}
                  style={{padding:'8px 14px',background:'transparent',border:'1px solid #D1D5DB',color:'#374151',borderRadius:6,fontSize:12,cursor:'pointer'}}>Cancelar</button>
                <button onClick={saveEditLogistics}
                  style={{padding:'8px 14px',background:'#B89840',color:'#FFF',border:'none',borderRadius:6,fontSize:12,fontWeight:700,cursor:'pointer'}}>💾 Salvar</button>
              </div>
            </div>
          </div>
        )}
      </div>
    );
  }

  window.ZNX = window.ZNX || {};
  window.ZNX.components = window.ZNX.components || {};
  window.ZNX.components.VendasDespacho = VendasDespacho;
  window.VendasDespacho = VendasDespacho;
})();
