// js/components/pages/Vendas.jsx
// Gestão de vendas (histórico + edição + cancelamento)
// Extraído de index.html em Fase 6 (2026-04-29): L2654-L3681
// Deps runtime: fmt, fmtDate, today, saleFinalTotal, itemNet, nid, genIdUUID, toast, showConfirm, Modal, SmartSelect, StatusBadge, Icon, ConfirmDeleteModal, OrcPipeline, createSaleAtomic, znxGuard, znxCounter, NovaVendaPage
(function() {
  'use strict';
// [L3 v218.13 ONDA-L Cowork-audit] regra_loading_state_obrigatorio — module-level lets REMOVIDOS.
// Substituídos por useRef DENTRO do componente (inflightUpdateStatusRef + inflightDeleteSaleRef).
// useState isUpdatingStatus + isDeletingSale já existem (K3.7) pra disabled visual.
// Anterior: let _inFlightUpdateStatus=false; let _inFlightDeleteSale=false; (module-level, sem re-render).
  const {useState, useMemo, useRef} = React;

  // [v224.73 + v224.55] vars+check MOVED to component body (preventivo · regra_validacao_helpers_runtime)

function Vendas({user,sales,setSales,products,setProducts,clients,setReceivables,receivables,setPayables,quotes,setQuotes,allUsers,discountRequests,setDiscountRequests,cancelRequests,setCancelRequests}){
  // [v224.73 FIX 2026-05-30] vars+check em render time
  const Tabs = window.ZNX?.widgets?.Tabs;
  const vendasCalcs = window.ZNX?.vendas?.calcs;
  const SalesCardsBar = window.ZNX?.widgets?.vendas?.SalesCardsBar;
  const SalesListPanel = window.ZNX?.widgets?.vendas?.SalesListPanel;
  const SaleViewModal = window.ZNX?.widgets?.vendas?.SaleViewModal;
  const SaleEditModal = window.ZNX?.widgets?.vendas?.SaleEditModal;
  if (!Tabs || !vendasCalcs || !SalesCardsBar || !SalesListPanel || !SaleViewModal || !SaleEditModal) {
    const _msg = `[Vendas v224.73] widgets/bundles faltando: Tabs=${!!Tabs}, calcs=${!!vendasCalcs}, SalesCardsBar=${!!SalesCardsBar}, SalesListPanel=${!!SalesListPanel}, SaleViewModal=${!!SaleViewModal}, SaleEditModal=${!!SaleEditModal}`;
    console.error(_msg);
    window.Sentry?.captureMessage?.(_msg, 'error');
  }
  const isTerminalSaleStatus = (vendasCalcs && vendasCalcs.isTerminalSaleStatus) || function(s){return ['Cancelada','Cancelado','Devolvida (total)'].includes(s);};
  const vendedores=useMemo(()=>(allUsers||[]).filter(u=>u.role==='vendedor'),[allUsers]);
  // [v224.96 N+1 backlog] clientsById O(1) pra CSV export · evita 1.2M ops em 1159 sales × 1072 clients
  const clientsById=useMemo(()=>Object.fromEntries((clients||[]).map(c=>[c.id,c])),[clients]);
  const isReadOnly=user.role==='financeiro'||user.role==='vendedor';
  // [L3 v218.13 ONDA-L Cowork-audit] regra_loading_state_obrigatorio + regra_state_id_vs_objeto:
  // useRef substitui lets module-level (sem re-render bug). useState controla disabled visual.
  const inflightUpdateStatusRef=useRef(false);
  const inflightDeleteSaleRef=useRef(false);
  const[isUpdatingStatus,setIsUpdatingStatus]=useState(false);
  const[isDeletingSale,setIsDeletingSale]=useState(false);
  const[modal,setModal]=useState(null);
  // [v128 STATE-ID 2026-05-11] regra_state_id_vs_objeto — guardar ID e fazer lookup fresco a cada render
  // Antes: setViewSale(s) guardava objeto inteiro → modal podia mostrar dados stale após edit/cancel
  const[viewSaleId,setViewSaleId]=useState(null);
  const viewSale=useMemo(
    ()=>viewSaleId?sales.find(s=>s.id===viewSaleId):null,
    [sales,viewSaleId]
  );
  const setViewSale=(s)=>setViewSaleId(s&&typeof s==='object'?s.id:s);
  // [H1a v205 20260512] Persistência filtro vendas (status+search+dateFrom+dateTo) em localStorage
  const[filter,setFilter]=useState(()=>{let s={};try{s=JSON.parse(localStorage.getItem('znx_filter_vendas')||'{}');}catch(_){}return{status:s.status||'',search:s.search||'',dateFrom:s.dateFrom||'',dateTo:s.dateTo||''};});
  React.useEffect(()=>{try{localStorage.setItem('znx_filter_vendas',JSON.stringify(filter));}catch(_){}},[filter]);
  const[sort,setSort]=useState({col:'date',dir:'desc'});
  function toggleSort(col){setSort(s=>s.col===col?{col,dir:s.dir==='asc'?'desc':'asc'}:{col,dir:'desc'});}
  const[form,setForm]=useState({clientId:'',items:[],date:today(),status:'Aberto',paymentStatus:'Pendente',sellerName:user.name});
  const[itemForm,setItemForm]=useState({productId:'',qty:1,price:0,discountPct:0,discountVal:0});
  const[prodSearch,setProdSearch]=useState('');
  const[showProdDrop,setShowProdDrop]=useState(false);
  const[clientSearch,setClientSearch]=useState('');
  const[showClientDrop,setShowClientDrop]=useState(false);
  const[confirmDeleteSale,setConfirmDeleteSale]=useState(null); // { id, number, clientName } | null
  const[editSaleId,setEditSaleId]=useState(null); // id da venda em edição (admin only)
  // [ONDA1-A 2026-05-11] regra_loading_state_obrigatorio — saveEdit precisa state pra
  // botão ficar disabled. Module-level let _inFlightSaveEditSale não trigga re-render.
  const saveEditInflightRef=useRef(false);
  const[isSavingEdit,setIsSavingEdit]=useState(false);
  // [ONDA-A #9 2026-05-11] regra_idem_estavel — UUIDs persistem entre retries.
  // ANTES: cada saveEdit/cancel/del gerava UUID novo → 2 cliques rápidos = 2 RPCs com keys distintas.
  // AGORA: ref lazy-init; reset SÓ após sucesso confirmado. Falha intermediária mantém key.
  const saveEditIdemRef=useRef(null);
  const cancelIdemMapRef=useRef(new Map()); // saleId → idem key
  const delIdemMapRef=useRef(new Map());    // saleId → idem key
  const[salesFilterTab,setSalesFilterTab]=React.useState('ativas'); // 'ativas' | 'canceladas'
  // [Onda V1 20260506 sistema 4] sistema de abas Lista / Insights / Funil / 360 / Detalhado
  const[mainTab,setMainTab]=useState('lista');
  const[selected360,setSelected360]=useState(null);
  const isAdmin=user.role==='admin';

  // Vendedor só vê as próprias vendas; admin/financeiro veem todas
  const scopedSales=user.role==='vendedor'?sales.filter(s=>s.sellerName===user.name):sales;
  const todayStr=today();

  // [Wave 26 v224.7 NUCLEAR] 7 cards reconciliation extraídos pra calcs.computeSalesCards
  // Inclui: activeSales, todaySalesV, monthSalesV, periodLabel/subLabel/isToday,
  //   dailyRevenue/Pix/Cash/CreditApplied/NFTax + monthSubtotalBruto/Pix/Cash/Credit/NF
  //   + dailyDiscount/monthDiscount honest (pct vs val) + dailyVendasTotalBruto/monthVendasTotalBruto
  const cardsData = useMemo(()=>vendasCalcs.computeSalesCards(scopedSales, filter, todayStr), [scopedSales, filter, todayStr]);
  // [Wave 10 MINI CLEANUP 2026-05-17] filter + sort extraídos pra lib/vendas/calcs.js (factory puras)
  const filtered=useMemo(()=>vendasCalcs.computeFiltered(scopedSales,filter,clients,salesFilterTab),[scopedSales,filter,clients,salesFilterTab]);
  const sorted=useMemo(()=>vendasCalcs.computeSorted(filtered,sort,clients,products),[filtered,sort,clients,products]);

  const firstInStockProduct=products.find(p=>p.stock>0);

  function openNew(){
    setForm({clientId:'',items:[],date:today(),status:'Aberto',paymentStatus:'Pendente',sellerName:user.name,canal:'',obs:''});
    setItemForm({productId:'',qty:1,price:0,discountPct:0,discountVal:0});
    setProdSearch('');setShowProdDrop(false);
    setClientSearch('');setShowClientDrop(false);
    setModal('new');
  }

  function openEdit(s){
    // [FEAT 20260504] Financeiro também edita venda (pra ajustar split Pix/Dinheiro)
    if(user.role!=='admin'&&user.role!=='financeiro'){
      toast('Apenas administradores e financeiro podem editar vendas.');
      return;
    }
    setForm({...s,clientId:String(s.clientId),obs:s.obs||'',canal:s.canal||'',items:[...(s.items||[])],globalDiscountType:s.globalDiscountType||'',globalDiscountValue:s.globalDiscountValue||0,paymentMethod:s.paymentMethod||'',paymentPixValue:Number(s.paymentPixValue||s.payment_pix_value||0),paymentCashValue:Number(s.paymentCashValue||s.payment_cash_value||0)});
    setProdSearch('');setShowProdDrop(false);
    setEditSaleId(s.id);
    setModal('edit');
  }

  async function saveEdit(){
    if(saveEditInflightRef.current){toast('⏳ Salvando...');return;}
    if(!await znxGuard(['admin','financeiro']))return;
    saveEditInflightRef.current=true;
    setIsSavingEdit(true);
    const oldSale=sales.find(s=>s.id===editSaleId);
    const oldItems=oldSale?.items||[];
    const newItems=form.items||[];
    try{
      // 1. UPDATE relacional sales
      // [FEAT 20260504] Validação Misto — soma deve bater com total da venda
      if(form.paymentMethod==='Misto'){
        const tot=Number(saleFinalTotal(form)||0);
        const pix=Number(form.paymentPixValue||0);
        const cash=Number(form.paymentCashValue||0);
        if(pix<=0||cash<=0){saveEditInflightRef.current=false;setIsSavingEdit(false);toast('Pagamento Misto exige Pix > 0 E Dinheiro > 0.');return;}
        const sum=Number((pix+cash).toFixed(2));
        if(Math.abs(sum-Number(tot.toFixed(2)))>=0.01){saveEditInflightRef.current=false;setIsSavingEdit(false);toast(`Soma Pix(${fmt(pix)}) + Dinheiro(${fmt(cash)}) = ${fmt(sum)} não bate com total ${fmt(tot)}.`);return;}
      }
      // [FEAT 20260504] Normaliza splits Pix/Dinheiro/Misto pro banco
      const tot2=Number(saleFinalTotal(form)||0);
      let pixV=Number(form.paymentPixValue||0), cashV=Number(form.paymentCashValue||0);
      if(form.paymentMethod==='Pix'){pixV=tot2; cashV=0;}
      else if(form.paymentMethod==='Dinheiro Vivo'){cashV=tot2; pixV=0;}
      else if(form.paymentMethod!=='Misto'){pixV=0; cashV=0;}
      // [BUG-FIX 20260504] saveEdit via RPC update_sale_v2 atomica (BEGIN/COMMIT no servidor).
      // Antes: UPDATE + DELETE + INSERT em 3 calls. Falha intermediaria deixava sale com items=[].
      // Mesma classe de bug do ORC-0059 (Thaisy). Agora: 1 RPC, 1 transacao.
      const itemsRpc=newItems.map(i=>({
        product_id:i.productId,
        qty:Number(i.qty)||0,
        price:Number(i.price)||0,
        discount_pct:Number(i.discountPct)||0,
        product_name:i.name||i.productName||null,
      }));
      // [ONDA-A #9] idem key estável — lazy init, mantém durante retry, reset pós-sucesso.
      if(!saveEditIdemRef.current){
        saveEditIdemRef.current=(window.crypto?.randomUUID?.()||(Date.now()+'-'+Math.random()));
      }
      const _idemUpd=saveEditIdemRef.current;
      const{error:rpcErr}=await sb.rpc('update_sale_v2',{payload:{
        idem_key:_idemUpd,
        sale_id:editSaleId,
        sale:{
          status:form.status||'Aberto',
          payment_status:form.paymentStatus||'Pendente',
          payment_method:form.paymentMethod||null,
          payment_pix_value:pixV,
          payment_cash_value:cashV,
          global_discount:Number(form.globalDiscountValue||0),
          obs:form.obs||null,
          seller_name:form.sellerName||null,
          channel:form.canal||null,
          date:form.date||oldSale?.date||null,
        },
        items:itemsRpc
      }});
      if(rpcErr){
        // [v223.37 BUG-MONA-UX] tenta parsear pra toast amigável c/ nome do produto (invalid_item/insufficient_stock)
        const friendly=window.ZNX?.lib?.parseQuoteError?.(rpcErr.message,products);
        toast(friendly||('❌ '+(rpcErr.message||'Erro ao salvar venda.')),friendly?'warning':'error');
        if(typeof Sentry!=='undefined')Sentry.captureException(new Error(rpcErr.message),{extra:{context:'saveEdit_vendas_rpc',saleId:editSaleId,friendly_match:!!friendly}});
        return;
      }
      // 3. Reconcile stock (preservado)
      setProducts(prev=>prev.map(p=>{
        const oi=oldItems.find(i=>nid(i.productId,p.id));
        const ni=newItems.find(i=>nid(i.productId,p.id));
        const delta=(oi?.qty||0)-(ni?.qty||0);
        return delta!==0?{...p,stock:Math.max(0,p.stock+delta)}:p;
      }));
      // 4. Update JSONB state (preservado)
      setSales(prev=>prev.map(s=>s.id===editSaleId?{
        ...s,
        items:newItems,
        globalDiscountType:form.globalDiscountType||'',
        globalDiscountValue:form.globalDiscountValue||0,
        status:form.status,
        paymentStatus:form.paymentStatus,
        paymentMethod:form.paymentMethod||'',
        paymentPixValue:pixV,
        paymentCashValue:cashV,
        obs:form.obs||'',
        sellerName:form.sellerName||s.sellerName,
        canal:form.canal||'',
        date:form.date||s.date,
        updatedAt:new Date().toISOString(),
        updatedBy:user.name,
      }:s));
      // 5. AUD-003: atualizar receivable (preservado)
      const updatedSale=sales.find(s=>s.id===editSaleId);
      if(updatedSale){
        const newTotal=saleFinalTotal({...updatedSale,items:newItems,globalDiscountType:form.globalDiscountType||'',globalDiscountValue:form.globalDiscountValue||0});
        setReceivables(prev=>prev.map(r=>r.saleId===editSaleId?{...r,value:newTotal}:r));
      }
      setEditSaleId(null);
      setModal(null);
      // [ONDA-A #9] sucesso confirmado → reset idem key pra próxima edição
      saveEditIdemRef.current=null;
      toast('✅ Venda atualizada!');
    }catch(e){
      console.error('[ZNX] saveEdit error:',e);
      // [v223.37 BUG-MONA-UX] tenta parsear pra toast amigável c/ nome do produto
      const friendly=window.ZNX?.lib?.parseQuoteError?.(e,products);
      if(friendly){toast(friendly,'warning');}else{toast('❌ Erro inesperado ao salvar. Recarregue a página.');}
      if(typeof Sentry!=='undefined')Sentry.captureException(e,{extra:{context:'saveEdit_vendas',saleId:editSaleId,friendly_match:!!friendly}});
    }finally{
      saveEditInflightRef.current=false;
      setIsSavingEdit(false);
    }
  }

  function addItem(){
    if(!itemForm.productId)return;
    const p=products.find(x=>nid(x.id,itemForm.productId));
    if(!p)return;
    // [BUG-FIX 20260504-decantes] is_decant boolean do banco como fonte canônica.
    // 36/153 decantes (24%) NÃO têm "decant" no nome → função legacy os bloqueava.
    const isDecantProd=p.is_decant===true||p.category==='Decants'||p.categoria==='Decants'||['5ML','2ML','3ML','10ML'].includes((p.volume||'').toUpperCase())||(p.name||'').toLowerCase().includes('decant');
    // [STOCK-RESERVE 20260422] Disponível por role — outras vendedoras descontam, própria NÃO
    const availInfo=getAvailableStockForSeller(products,quotes,p.id,null,getUserContext(user));
    if(availInfo.available<=0&&!isDecantProd){toast(`⚠ "${p.name}" está com estoque zerado e não pode ser vendido.`);return;}
    let qty=Number(itemForm.qty);
    if(qty<=0)return;
    if(!isDecantProd&&qty>availInfo.available){
      const detail=availInfo.reservations.map(r=>`  • ${r.qty} un. — ${r.quoteNumber} (${r.sellerName})`).join('\n');
      const msgReserva=availInfo.isNonSeller
        ?`Disponível: ${availInfo.available} un (estoque físico)`
        :`Disponível: ${availInfo.available} un (${availInfo.physicalStock} em estoque − ${availInfo.reservedByOthers} reservados por outras vendedoras)${detail?'\n'+detail:''}`;
      toast(`⚠️ Estoque insuficiente para ${p.name}\n\n${msgReserva}\n\nMáximo que você pode adicionar: ${availInfo.available}`);
      if(availInfo.available<=0)return;
      qty=availInfo.available;
    }
    // [UX 20260505 sistema 4] Consolidação de items duplicados.
    // Se produto já existe, soma qty na linha existente em vez de criar duplicata
    // (que bateria no UNIQUE sale_items_unique_per_sale).
    setForm(f=>{
      const existingIdx=f.items.findIndex(it=>it.productId===p.id);
      if(existingIdx>=0){
        const newQty=Number(f.items[existingIdx].qty||0)+qty;
        toast(`✅ ${p.name}: +${qty} un. (total agora ${newQty})`);
        return{...f,items:f.items.map((x,idx)=>idx===existingIdx?{...x,qty:newQty}:x)};
      }
      return{...f,items:[...f.items,{productId:p.id,qty,price:Number(itemForm.price),discountPct:Number(itemForm.discountPct)||0,name:p.name,brand:p.brand,volume:p.volume}]};
    });
    setItemForm({productId:'',qty:1,price:0,discountPct:0,discountVal:0});
    setProdSearch('');
  }

  async function removeItem(i){
    // [F5-04 audit Kimi v223.29] Consistente com saveEdit L129 — somente admin/financeiro pode editar venda existente
    if(!await znxGuard(['admin','financeiro']))return;
    setForm(f=>({...f,items:f.items.filter((_,idx)=>idx!==i)}));
  }

  async function updateStatus(id,newStatus){
    if(inflightUpdateStatusRef.current){toast('⏳ Processando...');return;}
    // [SEC-001] Server-side role check — cancel/change sale status (admin only)
    if(newStatus==='Cancelada'){if(!await znxGuard(['admin']))return;}
    const sale=sales.find(s=>s.id===id);
    if(!sale)return;
    if(sale.status==='Cancelada'&&newStatus==='Cancelada')return;
    // Confirm BEFORE acquiring lock (UX: user can back out)
    if(newStatus==='Cancelada'&&sale.status!=='Cancelada'&&!sale.status?.includes('Devolvida')){
      if(!await showConfirm({title:'Cancelar Venda',message:'Cancelar esta venda? Os produtos voltarão ao estoque automaticamente.',confirmText:'Cancelar Venda',confirmColor:'#DC2626'}))return;
    }
    inflightUpdateStatusRef.current=true;setIsUpdatingStatus(true);  // [L3 v218.13] useRef + disabled visual
    try{
      // [BUG-FIX 20260504] Cancelar via RPC atomica (sale + quote relacionado em 1 transacao).
      // Demais status (não-Cancelada): UPDATE direto.
      let updErr;
      if(newStatus==='Cancelada'&&sale.status!=='Cancelada'&&!sale.status?.includes('Devolvida')){
        // [ONDA-A #9 2026-05-11] idem key estável por saleId. Retry mantém key, reset pós-sucesso.
        if(!cancelIdemMapRef.current.has(id)){
          cancelIdemMapRef.current.set(id,(window.crypto?.randomUUID?.()||(Date.now()+'-'+Math.random())));
        }
        const _idemCancel=cancelIdemMapRef.current.get(id);
        const{error}=await sb.rpc('cancel_sale_with_quote_v2',{p_sale_id:id,p_idem_key:_idemCancel});
        updErr=error;
      }else{
        const{error}=await sb.from('sales').update({status:newStatus}).eq('id',id);
        updErr=error;
      }
      if(updErr){
        toast('❌ '+(updErr.message||'Erro ao atualizar status.'));
        if(typeof Sentry!=='undefined')Sentry.captureException(new Error(updErr.message),{extra:{context:'updateStatus_vendas',saleId:id,newStatus}});
        return;
      }
      // Side effects no JSONB local após RPC
      if(newStatus==='Cancelada'&&sale.status!=='Cancelada'&&!sale.status?.includes('Devolvida')){
        // Sync JSONB quotes state
        if(typeof setQuotes==='function'){
          setQuotes(prev=>prev.map(q=>q.saleId===id||nid(q.saleId,id)?{...q,status:'Cancelado',updatedAt:new Date().toISOString()}:q));
        }
        // Restaura estoque de cada item
        setProducts(prev=>prev.map(p=>{
          const it=sale.items.find(i=>nid(i.productId,p.id));
          return it?{...p,stock:p.stock+Number(it.qty)}:p;
        }));
        // Cancela contas a receber vinculadas
        setReceivables(prev=>prev.map(r=>r.saleId===id?{...r,status:'Cancelado'}:r));
        // [ONDA-A #9] sucesso confirmado → reset idem key cancel pra esta sale
        cancelIdemMapRef.current.delete(id);
      }
      setSales(prev=>prev.map(s=>s.id===id?{...s,status:newStatus}:s));
    }catch(e){
      console.error('[ZNX] updateStatus error:',e);
      if(typeof Sentry!=='undefined')Sentry.captureException(e,{extra:{context:'updateStatus_vendas',saleId:id,newStatus}});
      toast('❌ Erro inesperado ao atualizar status.');
    }finally{
      inflightUpdateStatusRef.current=false;setIsUpdatingStatus(false);  // [L3 v218.13] useRef + libera disabled
    }
  }

  // [ONDA-PDF-N3 v206 20260512] gerarFaturaPDF agora delega pra pdfShared.gerarDocumentoPDF
  // O código v97-v138 (690 linhas) foi extraído pra js/lib/pdfShared.js. Esta função vira
  // wrapper fino que adapta opts.kind='FATURA'. Orçamento usa mesma engine via kind='ORÇAMENTO'.
  // Regras: regra_componente_vs_inline_duplicata + regra_falha_silenciosa_proibida.
  async function gerarFaturaPDF(sale, clients){
    if(typeof window.pdfShared?.gerarDocumentoPDF !== 'function'){
      toast('❌ pdfShared não carregado. Recarregue a página (Ctrl+Shift+R).','error');
      if(typeof Sentry!=='undefined') try{Sentry.captureMessage('[ZNX v206] pdfShared não disponível em gerarFaturaPDF',{level:'error'});}catch(_){}
      return;
    }
    // [BUG-FRETE v212 20260512] Refetch FRESH do banco antes de gerar PDF — mata cache stale.
    // VND-0480 Sedex aparecia como Retirada no PDF do estoque pois localStorage tinha versão pré-conversão.
    // regra_fresh_select_antes_update: SEMPRE banco autoritativo em momento crítico.
    let freshSale = sale;
    try{
      if(sale?.id && typeof sb!=='undefined'){
        const{data,error}=await sb.from('sales').select('*').eq('id',sale.id).maybeSingle();
        if(!error && data) freshSale = {...sale, ...data, items: (sale.items && sale.items.length ? sale.items : (data.items||[]))};
      }
    }catch(e){
      if(typeof Sentry!=='undefined') try{Sentry.captureMessage('[ZNX v212] refetch sale falhou (usando cache)',{level:'warning',extra:{message:e?.message,saleId:sale?.id}});}catch(_){}
    }
    return window.pdfShared.gerarDocumentoPDF({kind:'FATURA', data:freshSale, clients:clients});
  }


  return(
    <div>
      {/* [Onda V1] TabBar principal — Lista/Insights/Funil/360/Detalhado */}
      <Tabs
        tabs={[
          {id:'lista',label:'📋 Lista',count:scopedSales.length},
          {id:'insights',label:'📊 Insights',count:0,disabled:true,tip:'Aguarda Onda V2/V3'},
          {id:'funil',label:'🔄 Funil',count:0,disabled:true,tip:'Aguarda Onda V5'},
          {id:'360',label:'🎯 Venda 360º',count:selected360?1:0,disabled:!selected360,tip:'Aguarda Onda V3'},
          {id:'detalhado',label:'📋 Detalhado',count:0,disabled:true,tip:'Aguarda Onda V3 (do Relatório)'}
        ]}
        active={mainTab}
        onChange={setMainTab}
      />

      {/* ═══════════════════════ ABA LISTA ═══════════════════════ */}
      {mainTab==='lista'&&<>
      <div className="page-header">
        {/* [L2-Vendas 2026-05-09] Title com contador adaptativo + indica filtros ativos */}
        <div className="page-title">
          Vendas
          {(() => {
            const hasFilters=!!(filter.search||filter.dateFrom||filter.dateTo);
            return hasFilters
              ? <span style={{fontSize:13,color:'#B89840',fontWeight:600,marginLeft:8}}>({sorted.length} de {scopedSales.length})</span>
              : <span style={{fontSize:13,color:'#9CA3AF',fontWeight:400,marginLeft:8}}>({scopedSales.length})</span>;
          })()}
        </div>
        <div style={{display:'flex',gap:10}}>
          <div style={{display:'flex',alignItems:'center',gap:6,background:'#F8FAFC',border:'1px solid #E4E7EC',borderRadius:8,padding:'4px 10px'}}>
            <span style={{fontSize:12,color:'#6B7280',fontWeight:500}}>De</span>
            <input type="date" value={filter.dateFrom} onChange={e=>setFilter(f=>({...f,dateFrom:e.target.value}))} style={{width:130,fontSize:12,padding:'4px 6px',border:'none',background:'transparent',outline:'none'}}/>
            <span style={{fontSize:12,color:'#6B7280',fontWeight:500}}>Até</span>
            <input type="date" value={filter.dateTo} onChange={e=>setFilter(f=>({...f,dateTo:e.target.value}))} style={{width:130,fontSize:12,padding:'4px 6px',border:'none',background:'transparent',outline:'none'}}/>
            <button onClick={()=>setFilter(f=>({...f,dateFrom:today(),dateTo:today()}))} style={{background:(filter.dateFrom===today()&&filter.dateTo===today())?'#2563EB':'#EFF6FF',color:(filter.dateFrom===today()&&filter.dateTo===today())?'#FFFFFF':'#2563EB',border:'none',borderRadius:5,padding:'3px 10px',fontSize:12,fontWeight:600,cursor:'pointer'}}>Hoje</button>
            {(filter.dateFrom||filter.dateTo)&&<button onClick={()=>setFilter(f=>({...f,dateFrom:'',dateTo:''}))} style={{background:'transparent',border:'none',color:'#9CA3AF',fontSize:14,cursor:'pointer',padding:'0 2px',lineHeight:1}}>×</button>}
          </div>
          <input style={{width:220}} placeholder="🔍 Buscar nº, cliente, vendedor..." value={filter.search} onChange={e=>setFilter(f=>({...f,search:e.target.value}))}/>
          {user.role!=='vendedor'&&<button className="btn-outline" onClick={()=>exportCSV(filtered.map(s=>({Numero:s.number,Data:s.date,Cliente:clientsById[s.clientId]?.name||'',Vendedor:s.sellerName||'',Canal:s.canal||'',Total:saleFinalTotal(s).toFixed(2),Desconto:saleDiscount(s.items).toFixed(2),Status:s.status,Pagamento:s.paymentStatus,Observacoes:s.obs||''})),'vendas_zaynex')} style={{display:'flex',alignItems:'center',gap:5,fontSize:12}}>⬇ CSV</button>}
          {!isReadOnly&&<button className="btn-gold" onClick={openNew} style={{display:'flex',alignItems:'center',gap:6}}><Icon n="plus" size={14}/>Nova Venda</button>}
        </div>
      </div>

      {/* [Wave 26 v224.7 NUCLEAR] 7 cards + faixa fórmula extraídos pra SalesCardsBar */}
      {SalesCardsBar && (
        <SalesCardsBar
          cardsData={cardsData}
          userCanSeeCards={user.role==='admin'||user.role==='financeiro'}
        />
      )}

      {/* [Wave 26 v224.7 NUCLEAR] Sub-tabs + tabela principal extraídos pra SalesListPanel */}
      {SalesListPanel && (
        <SalesListPanel
          user={user} scopedSales={scopedSales} sorted={sorted}
          sort={sort} toggleSort={toggleSort}
          salesFilterTab={salesFilterTab} setSalesFilterTab={setSalesFilterTab}
          clients={clients} products={products}
          isAdmin={isAdmin} isReadOnly={isReadOnly}
          filter={filter} setFilter={setFilter}
          openView={(s)=>setViewSale(s)}
          openEdit={openEdit}
          setConfirmDeleteSale={setConfirmDeleteSale}
          setSelected360={setSelected360} setMainTab={setMainTab}
          cancelRequests={cancelRequests} setCancelRequests={setCancelRequests}
        />
      )}


      {/* ── CANCELAMENTOS SOLICITADOS PELO VENDEDOR ── */}
      {user.role==='vendedor'&&(()=>{
        const meusCancels=cancelRequests.filter(r=>r.requestedBy===user.name).sort((a,b)=>b.requestedAt>a.requestedAt?1:-1).slice(0,10);
        if(meusCancels.length===0)return null;
        return(
          <div className="card" style={{marginTop:20}}>
            <div style={{fontWeight:600,marginBottom:12,fontSize:15}}>Minhas Solicitações de Cancelamento</div>
            <table>
              <thead><tr>
                <th>Venda</th><th>Cliente</th><th>Solicitado em</th><th>Total</th><th>Status</th>
              </tr></thead>
              <tbody>
                {meusCancels.map(r=>{
                  const statusColor=r.status==='pending'?'#EA580C':r.status==='approved'?'#16A34A':'#DC2626';
                  const statusLabel=r.status==='pending'?'⏳ Aguardando':r.status==='approved'?'✓ Aprovado':'✕ Negado';
                  return(
                    <tr key={r.id}>
                      <td className="gold">{r.sale?.number||'—'}</td>
                      <td>{r.clientName}</td>
                      <td style={{fontSize:12}}>{new Date(r.requestedAt).toLocaleString('pt-BR',{day:'2-digit',month:'2-digit',hour:'2-digit',minute:'2-digit'})}</td>
                      <td>{fmt(r.saleTotal||0)}</td>
                      <td><span style={{fontSize:12,fontWeight:600,color:statusColor}}>{statusLabel}</span></td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        );
      })()}

      {/* ── SOLICITAÇÕES DO VENDEDOR ── */}
      {user.role==='vendedor'&&(()=>{
        const minhas=discountRequests.filter(r=>r.requestedBy===user.name).sort((a,b)=>b.requestedAt>a.requestedAt?1:-1).slice(0,10);
        if(minhas.length===0)return null;
        return(
          <div className="card" style={{marginTop:20}}>
            <div style={{fontWeight:600,marginBottom:12,fontSize:15}}>Minhas Solicitações de Desconto</div>
            <table>
              <thead><tr>
                <th>Data</th><th>Cliente</th><th>Desconto</th><th>Total Líquido</th><th>Status</th>
              </tr></thead>
              <tbody>
                {minhas.map(r=>{
                  const statusColor=r.status==='pending'?'#2563EB':r.status==='approved'?'#16A34A':'#DC2626';
                  const statusLabel=r.status==='pending'?'⏳ Aguardando':r.status==='approved'?('✓ Aprovado'+(r.saleNumber?' ('+r.saleNumber+')':'')):'✕ Negado'+(r.saleNumber?' — VND '+r.saleNumber+' criada no preço original':'');
                  return(
                    <tr key={r.id}>
                      <td style={{fontSize:12}}>{new Date(r.requestedAt).toLocaleString('pt-BR',{day:'2-digit',month:'2-digit',hour:'2-digit',minute:'2-digit'})}</td>
                      <td>{r.clientName}</td>
                      <td style={{color:'#EA580C'}}>−{fmt(r.totalDesconto)}</td>
                      <td className="gold">{fmt(r.totalLiquido)}</td>
                      <td><span style={{fontSize:12,fontWeight:600,color:statusColor}}>{statusLabel}</span></td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        );
      })()}
      </>}

      {/* ═══════════════════════ ABA INSIGHTS (placeholder) ═══════════════════════ */}
      {mainTab==='insights'&&(
        <div className="card" style={{padding:40,textAlign:'center',color:'#6B7280'}}>
          <div style={{fontSize:48,marginBottom:14}}>📊</div>
          <div style={{fontSize:18,fontWeight:700,color:'#1B2A4A',marginBottom:8}}>Insights de Vendas — em construção</div>
          <div style={{fontSize:13,maxWidth:520,margin:'0 auto',lineHeight:1.6}}>
            Vai consolidar: ranking vendedoras, conversão, ticket médio, análise de cancelamento (R$133k mapeados!), motivos top, performance por canal, comissão.
          </div>
          <div style={{marginTop:16,fontSize:11,color:'#9CA3AF'}}>Aguarde Onda V2 e V3.</div>
        </div>
      )}

      {/* ═══════════════════════ ABA FUNIL (placeholder) ═══════════════════════ */}
      {mainTab==='funil'&&(
        <div className="card" style={{padding:40,textAlign:'center',color:'#6B7280'}}>
          <div style={{fontSize:48,marginBottom:14}}>🔄</div>
          <div style={{fontSize:18,fontWeight:700,color:'#1B2A4A',marginBottom:8}}>Funil Pipeline — em construção</div>
          <div style={{fontSize:13,maxWidth:520,margin:'0 auto',lineHeight:1.6}}>
            Visualização Kanban de orçamentos: Rascunho → Aberto → Aprovado → Convertido / Cancelado. Drag-and-drop, reservas de estoque, dias na etapa.
          </div>
          <div style={{marginTop:16,fontSize:11,color:'#9CA3AF'}}>Aguarde Onda V5.</div>
        </div>
      )}

      {/* ═══════════════════════ ABA 360º (placeholder) ═══════════════════════ */}
      {mainTab==='360'&&selected360&&(
        <VendaTimeline
          sale={sales.find(s=>s.id===selected360)||null}
          client={clients.find(c=>c.id===(sales.find(s=>s.id===selected360)?.clientId))}
          products={products}
          receivables={receivables}
          user={user}
          allUsers={allUsers}
          onBack={()=>{setMainTab('lista');setSelected360(null);}}
          onEdit={(s)=>{setMainTab('lista');setSelected360(null);openEdit(s);}}
          onCancel={(s)=>updateStatus(s.id,'Cancelada')}
          onPrintPdf={(s)=>gerarFaturaPDF(s,clients)}
        />
      )}
      {mainTab==='360'&&!selected360&&(
        <div className="card" style={{padding:40,textAlign:'center',color:'#6B7280'}}>
          <div style={{fontSize:48,marginBottom:14}}>🎯</div>
          <div style={{fontSize:18,fontWeight:700,color:'#1B2A4A',marginBottom:8}}>Selecione uma venda</div>
          <div style={{fontSize:13,maxWidth:520,margin:'0 auto',lineHeight:1.6}}>
            Volte pra Lista e clique no 🎯 dourado de qualquer venda pra ver Venda 360º.
          </div>
        </div>
      )}

      {/* ═══════════════════════ ABA DETALHADO (placeholder) ═══════════════════════ */}
      {mainTab==='detalhado'&&(
        <div className="card" style={{padding:40,textAlign:'center',color:'#6B7280'}}>
          <div style={{fontSize:48,marginBottom:14}}>📋</div>
          <div style={{fontSize:18,fontWeight:700,color:'#1B2A4A',marginBottom:8}}>Detalhado por Nota — em construção</div>
          <div style={{fontSize:13,maxWidth:520,margin:'0 auto',lineHeight:1.6}}>
            Migração da tab "Por Nota" do Relatório com filtros globais (período/vendedor/cliente/canal).
          </div>
          <div style={{marginTop:16,fontSize:11,color:'#9CA3AF'}}>Aguarde Onda V3.</div>
        </div>
      )}

      {modal==='new'&&<NovaVendaPage user={user} products={products} clients={clients} quotes={quotes} setQuotes={setQuotes} vendedores={vendedores} sales={sales} setSales={setSales} setProducts={setProducts} setReceivables={setReceivables} discountRequests={discountRequests} setDiscountRequests={setDiscountRequests} onClose={()=>setModal(null)}/>}

      {/* [Wave 26 v224.7 NUCLEAR] Modal EDIT extraído pra SaleEditModal · saveEdit handler permanece aqui */}
      {modal==='edit' && (isAdmin||user.role==='financeiro') && SaleEditModal && (
        <SaleEditModal
          open={modal==='edit'}
          form={form} setForm={setForm}
          products={products}
          prodSearch={prodSearch} setProdSearch={setProdSearch}
          showProdDrop={showProdDrop} setShowProdDrop={setShowProdDrop}
          isSavingEdit={isSavingEdit}
          onSave={saveEdit}
          onClose={()=>{setModal(null); setEditSaleId(null);}}
        />
      )}

      {/* ── MODAL CONFIRMAR EXCLUSÃO VENDA ── */}
      <ConfirmDeleteModal
        show={!!confirmDeleteSale}
        title="Confirmar Exclusão"
        message={confirmDeleteSale?`Tem certeza que deseja excluir a venda ${confirmDeleteSale.number}? Os produtos voltarão ao estoque.`:''}
        detail={confirmDeleteSale?.clientName?`Cliente: ${confirmDeleteSale.clientName}`:''}
        onCancel={()=>setConfirmDeleteSale(null)}
        onConfirm={async()=>{
          // [SEC-001] Server-side role check — delete sale
          if(!await znxGuard(['admin'])){setConfirmDeleteSale(null);return;}
          if(inflightDeleteSaleRef.current){toast('⏳ Processando...');return;}
          const s=sales.find(x=>x.id===confirmDeleteSale.id);
          if(!s){setConfirmDeleteSale(null);return;}
          inflightDeleteSaleRef.current=true;setIsDeletingSale(true);  // [L3 v218.13] useRef + disabled
          try{
            // [BUG-FIX 20260504] DELETE cascade via RPC atomica (1 transacao):
            // restore stock + cancel quote + cancel receivables + delete payables/items/sale.
            // Antes: 6 calls separados — se metade rolasse, drift gigante (estoque fantasma,
            // receivables zumbi, sale orfa). Agora: tudo ou nada.
            // [ONDA-A #9 2026-05-11] idem key estável por saleId — retry replay-safe.
            if(!delIdemMapRef.current.has(s.id)){
              delIdemMapRef.current.set(s.id,(window.crypto?.randomUUID?.()||(Date.now()+'-'+Math.random())));
            }
            const _idemDel=delIdemMapRef.current.get(s.id);
            const{error:rpcErr}=await sb.rpc('delete_sale_cascade_v2',{p_sale_id:s.id,p_idem_key:_idemDel});
            if(rpcErr){
              toast('❌ Erro ao excluir venda: '+rpcErr.message);
              if(typeof Sentry!=='undefined')Sentry.captureException(new Error(rpcErr.message),{extra:{context:'deleteSale_rpc',saleId:s.id}});
              return;
            }
            // Sync JSONB quotes state local
            if(typeof setQuotes==='function'){
              setQuotes(prev=>prev.map(q=>q.saleId===s.id||nid(q.saleId,s.id)?{...q,status:'Cancelado',saleId:null,saleNumber:null,updatedAt:new Date().toISOString()}:q));
            }
            // State updates locais (SOMENTE após sucesso da RPC)
            // BUG-FIX-4: não retornar estoque local se já devolvida/cancelada
            setProducts(prev=>prev.map(p=>{const it=s.items.find(i=>nid(i.productId,p.id));return it&&s.status!=='Cancelada'&&!s.status?.includes('Devolvida')?{...p,stock:p.stock+Number(it.qty)}:p;}));
            setReceivables(prev=>prev.map(r=>r.saleId===s.id?{...r,status:'Cancelado'}:r));
            setPayables(prev=>prev.filter(p=>!nid(p.saleId,s.id)));
            setSales(prev=>prev.filter(x=>x.id!==s.id));
            // [ONDA-A #9] sucesso confirmado → reset idem key del pra esta sale
            delIdemMapRef.current.delete(s.id);
            toast('✅ Venda excluída com sucesso!');
          }catch(e){
            console.error('[ZNX] deleteSale error:',e);
            if(typeof Sentry!=='undefined')Sentry.captureException(e,{extra:{context:'deleteSale',saleId:s.id}});
            toast('❌ Erro inesperado ao excluir venda.');
          }finally{
            inflightDeleteSaleRef.current=false;setIsDeletingSale(false);  // [L3 v218.13] useRef + libera
            setConfirmDeleteSale(null);
          }
        }}
      />

      {/* [Wave 26 v224.7 NUCLEAR] Modal VIEW extraído pra SaleViewModal */}
      {viewSale && SaleViewModal && (
        <SaleViewModal
          viewSale={viewSale}
          clients={clients}
          products={products}
          isAdmin={isAdmin}
          onClose={()=>setViewSale(null)}
          onGenerateFaturaPDF={()=>gerarFaturaPDF(viewSale, clients)}
        />
      )}
    </div>
  );
}

  window.ZNX = window.ZNX || {};
  window.ZNX.components = window.ZNX.components || {};
  window.ZNX.components.Vendas = Vendas;
  window.Vendas = Vendas;

  window.ZNX.refactor_phase_6_loaded = window.ZNX.refactor_phase_6_loaded || {};
  window.ZNX.refactor_phase_6_loaded.Vendas = true;
  // [Wave 26 marker v224.7] confirma extract executado
  window.Vendas_v224_7_wave26 = true;

})();
