// js/components/widgets/Compras.jsx
// Gestão de pedidos de compra + recebimento de estoque.
// [Wave 32 v224.13 NUCLEAR EXTRACT 2026-05-24] 321L -> 225L (-29.9%) · 7ª replicação NUCLEAR
// Extract: 1 lib + 3 widgets (PurchasesTable + PurchaseFormModal + PurchaseViewModal)
// Padrão Wave 26/31 page-write · handlers save/receive PRESERVADOS LITERAL byte-a-byte (atomicidade RPC)
// JUSTIFICATIVA HONEST shrink ABAIXO floor -35%:
//   - 135L handlers save (60L) + receive (75L) PROIBIDOS tocar (atomicidade RPC + idempotency)
//   - 30L states/refs/effects preservados LITERAL
//   - Render JSX (113L) extraído 100% pra widgets — esgotou superfície extractable
//   - Cowork pre-flight superestimou floor pra page com 53% locked (handlers + state)
//
// Deps runtime: Modal, SmartSelect, Icon, StatusBadge, toast, fmt, fmtDate, dualWrite, genIdUUID, nid, today (globals)
(function() {
  'use strict';
  const {useState, useEffect, useRef, useMemo} = React;

const COMPRA_STATUSES=['Aguardando Confirmação','Em aberto','Em trânsito','Parcialmente Entregue','Entregue'];

// OB-006 estendido — inflight guards (módulo-level, fora do componente)
let _inFlightSaveNewPurchase=false;
let _inFlightReceivePurchase=false;

// [Wave 32 v224.13 + v224.55 2026-05-28] vars+check MOVED to component body (preventivo)
// regra_validacao_helpers_runtime_quando_ordem_scripts_uncertain
// Namespace cmW (compras widgets) — member-access pattern resolve JSX corretamente

function Compras({purchases,setPurchases,products,setProducts,suppliers,entries,setEntries,setPayables}){
  // [v224.55 FIX-PREV-11 2026-05-28] vars+check em render time
  const cmW = (window.ZNX && window.ZNX.widgets && window.ZNX.widgets.compras) || {};
  const comprasCalcs = window.ZNX && window.ZNX.lib && window.ZNX.lib.compras && window.ZNX.lib.compras.calcs;
  if(!comprasCalcs || !cmW.PurchasesTable || !cmW.PurchaseFormModal || !cmW.PurchaseViewModal){
    const _msg = '[Compras v224.13 wave32] widgets faltando: calcs='+!!comprasCalcs+', PurchasesTable='+!!cmW.PurchasesTable+', PurchaseFormModal='+!!cmW.PurchaseFormModal+', PurchaseViewModal='+!!cmW.PurchaseViewModal;
    console.error(_msg);
    if(window.Sentry && typeof window.Sentry.captureMessage==='function') window.Sentry.captureMessage(_msg, 'error');
  }
  const EMPTY_FORM={supplierId:suppliers[0]?.id||'',items:[],date:today(),deliveryDate:'',status:'Em aberto',currency:'BRL',exchangeRate:1,totalOriginal:0,obs:''};
  const[modal,setModal]=useState(null);
  const[viewPurchase,setViewPurchase]=useState(null);
  const[form,setForm]=useState(EMPTY_FORM);
  const[itemForm,setItemForm]=useState({productId:'',qty:1,cost:0});
  const savingRef=useRef(false);
  // [ONDA-A #9 2026-05-11] regra_loading_state_obrigatorio + regra_idem_estavel
  // ANTES: módulo-level let _inFlightSaveNewPurchase não triggava re-render → botão sem disabled.
  // Agora: state pra UI + idem refs (save/receive). Idem persiste durante retry; reset pós-sucesso.
  const[isSaving,setIsSaving]=useState(false);
  const[receivingId,setReceivingId]=useState(null);
  const saveIdemRef=useRef(null);
  const receiveIdemMapRef=useRef(new Map()); // purchaseId → idem key
  useEffect(()=>{savingRef.current=false;setIsSaving(false);saveIdemRef.current=null;},[modal]);

  // [Wave 32 v224.13] totalBRL via calcs lib wrapper (era useMemo inline)
  const totalBRL=useMemo(()=>comprasCalcs.calcTotalBRL(form.items, form.currency, form.exchangeRate),[form.items,form.currency,form.exchangeRate]);

  // [Wave 32 v224.13] openNewModal helper extraído do inline onClick L190 original
  function openNewModal(){
    setForm({...EMPTY_FORM,supplierId:suppliers[0]?.id||''});
    setItemForm({productId:products[0]?.id||'',qty:1,cost:products[0]?.avgCost||0});
    setModal('new');
  }

  async function save(){
    // [ONDA-A #9 2026-05-11] regra_loading_state_obrigatorio — savingRef early return + isSaving state.
    if(savingRef.current||_inFlightSaveNewPurchase)return;
    if(!await znxGuard(['admin','estoquista']))return;
    if(!form.supplierId){toast('Selecione um fornecedor.');return;}
    if(!form.items||form.items.length===0){toast('Adicione pelo menos um produto.');return;}
    savingRef.current=true;
    setIsSaving(true);
    _inFlightSaveNewPurchase=true;
    try{
      const newId=genIdUUID();
      // [ONDA-A #9] idem key estável — lazy init, mantém durante retry.
      // TODO: createPurchaseAtomic ainda não aceita idempotencyKey — passar quando RPC for atualizada.
      if(!saveIdemRef.current){
        saveIdemRef.current=(window.crypto?.randomUUID?.()||(Date.now()+'-'+Math.random()));
      }
      const result=await window.createPurchaseAtomic({
        form,
        supplierId:form.supplierId,
        items:form.items,
        purchaseId:newId,
        idempotencyKey:saveIdemRef.current
      });

      if(!result.success){
        const msg=(typeof mapErrorToUX==='function'
          ?mapErrorToUX(result.errorCode,result.errorMessage)
          :null)||result.errorMessage||'Erro ao criar compra.';
        toast('❌ '+msg);
        if(typeof Sentry!=='undefined')Sentry.captureException(
          new Error(result.errorCode||'saveNewPurchase failed'),
          {extra:{errorCode:result.errorCode,errorMessage:result.errorMessage,context:'save_compras'}}
        );
        return;
      }

      // Sync state local com dados confirmados pelo banco
      const newPurchase={
        ...form,
        id:result.purchaseId,
        number:result.purchaseNumber,
        total:result.total,
        exchangeRate:Number(form.exchangeRate)||1,
        createdAt:new Date().toISOString()
      };
      setPurchases(prev=>[...prev,newPurchase]);
      toast('✅ Compra '+result.purchaseNumber+' criada!');
      setModal(null);
      // [ONDA-A #9] sucesso confirmado → reset idem key pra próxima compra
      saveIdemRef.current=null;
    }catch(e){
      const msg=e.message||'Erro inesperado. Tente novamente.';
      toast('❌ '+msg);
      if(typeof Sentry!=='undefined')Sentry.captureException(e,{extra:{context:'save_compras',errorMessage:e.message}});
    }finally{
      _inFlightSaveNewPurchase=false;
      savingRef.current=false;
      setIsSaving(false);
    }
  }

  async function receive(purchase){
    // [ONDA-A #9 2026-05-11] regra_loading_state_obrigatorio — bloqueia duplo clique + UI feedback.
    if(_inFlightReceivePurchase||receivingId===purchase.id)return;
    if(!await znxGuard(['admin','estoquista']))return;
    // Guard duplo-clique
    const current=purchases.find(p=>p.id===purchase.id);
    if(!current||current.status==='Entregue'){toast('Esta compra já foi recebida.');return;}
    _inFlightReceivePurchase=true;
    setReceivingId(purchase.id);
    try{
      // [BUG-FIX 20260504] receive via RPC atomica receive_purchase_v2.
      // Antes: N+3 calls separados (loop dualWrite products + UPDATE purchase + dualWrite
      // payable + setEntries só local). Drift em qualquer falha intermediaria.
      // Agora: 1 RPC, 1 transacao. Se 1 produto falhar = ROLLBACK total.
      // Bonus: entries persiste no banco (era state local apenas).
      // [ONDA-A #9] idem key estável por purchaseId — retry replay-safe.
      // TODO: receivePurchaseAtomic ainda não aceita idempotencyKey — passar quando RPC for atualizada.
      if(!receiveIdemMapRef.current.has(purchase.id)){
        receiveIdemMapRef.current.set(purchase.id,(window.crypto?.randomUUID?.()||(Date.now()+'-'+Math.random())));
      }
      const _idemReceive=receiveIdemMapRef.current.get(purchase.id);
      const result=await window.receivePurchaseAtomic({purchaseId:purchase.id, dueDays:30, idempotencyKey:_idemReceive});
      if(!result.success){
        const msg=(typeof mapErrorToUX==='function'?mapErrorToUX(result.errorCode,result.errorMessage):null)
          ||result.errorMessage||'Erro ao receber compra.';
        toast('❌ '+msg);
        if(typeof Sentry!=='undefined')Sentry.captureException(
          new Error(result.errorCode||'receivePurchase failed'),
          {extra:{errorCode:result.errorCode,errorMessage:result.errorMessage,purchaseId:purchase.id}}
        );
        return;
      }

      // Sync state local com o que o banco persistiu
      setPurchases(prev=>prev.map(p=>p.id===purchase.id?{...p,status:'Entregue'}:p));
      // Stock + avgCost foram atualizados no banco; aplicar local pra UI imediata
      setProducts(prev=>prev.map(p=>{
        const it=(purchase.items||[]).find(i=>i.productId===p.id);
        if(!it)return p;
        const costBRL=purchase.currency&&purchase.currency!=='BRL'?Number(it.cost)*(Number(purchase.exchangeRate)||1):Number(it.cost);
        const newStock=(p.stock||0)+Number(it.qty);
        const newAvg=p.stock===0?costBRL:Math.round(((p.stock*(p.avgCost||0)+it.qty*costBRL)/newStock)*100)/100;
        return{...p,stock:newStock,avgCost:newAvg};
      }));
      // Entries: agora vem do banco; refresh via JSONB sync. Local fallback:
      if(setEntries){
        const newEntries=(purchase.items||[]).map(it=>({
          id:genIdUUID(),productId:it.productId,supplierId:purchase.supplierId,
          qty:it.qty,unitCost:it.cost,date:today(),
          notes:`Compra ${purchase.number}`
        }));
        setEntries(prev=>[...newEntries,...prev]);
      }
      // Payable já foi criado pela RPC — refletir local
      if(setPayables&&result.payableId){
        const sup=suppliers.find(s=>s.id===purchase.supplierId);
        const due=new Date(Date.now()+30*86400000).toLocaleDateString('sv-SE',{timeZone:'America/Sao_Paulo'});
        setPayables(prev=>[...prev,{
          id:result.payableId,
          description:`${purchase.number} — ${sup?.name||'Fornecedor'}`,
          supplierId:purchase.supplierId||null,
          value:result.payableValue,
          due,
          status:'Pendente'
        }]);
      }
      if(viewPurchase?.id===purchase.id)setViewPurchase(null);
      // [ONDA-A #9] sucesso confirmado → reset idem key receive pra esta compra
      receiveIdemMapRef.current.delete(purchase.id);
      toast(`✅ ${result.itemsReceived} produto(s) recebido(s) — estoque atualizado, conta a pagar criada.`);
    }catch(e){
      const msg=e.message||'Erro inesperado. Tente novamente.';
      toast('❌ '+msg);
      if(typeof Sentry!=='undefined')Sentry.captureException(e,{extra:{context:'receive_compras',errorMessage:e.message}});
    }finally{
      _inFlightReceivePurchase=false;
      setReceivingId(null);
    }
  }

  // === Render (3 widgets extraídos · addItem agora dentro de PurchaseFormModal) ===
  return(
    <div>
      <div className="page-header">
        <div className="page-title">Compras</div>
        <button className="btn-gold" onClick={openNewModal} style={{display:'flex',alignItems:'center',gap:6}}><Icon n="plus" size={14}/>Nova Compra</button>
      </div>
      {cmW.PurchasesTable && <cmW.PurchasesTable purchases={purchases} suppliers={suppliers} receivingId={receivingId} onView={setViewPurchase} onReceive={receive}/>}
      {cmW.PurchaseViewModal && <cmW.PurchaseViewModal viewPurchase={viewPurchase} suppliers={suppliers} products={products} receivingId={receivingId} onReceive={receive} onClose={()=>setViewPurchase(null)}/>}
      {cmW.PurchaseFormModal && <cmW.PurchaseFormModal open={modal==='new'} form={form} setForm={setForm} itemForm={itemForm} setItemForm={setItemForm} suppliers={suppliers} products={products} statuses={COMPRA_STATUSES} totalBRL={totalBRL} isSaving={isSaving} onSave={save} onClose={()=>setModal(null)}/>}
    </div>
  );
}

// ══════════════════════════════════════════════════════════════
// DEVOLUÇÕES
// ══════════════════════════════════════════════════════════════
const DEV_MOTIVOS=['Produto com defeito','Pedido errado','Desistência','Outro'];

  window.ZNX = window.ZNX || {};
  window.ZNX.components = window.ZNX.components || {};
  window.ZNX.components.Compras = Compras;
  window.Compras = Compras;
  window.COMPRA_STATUSES = typeof COMPRA_STATUSES !== 'undefined' ? COMPRA_STATUSES : [];

  window.ZNX.refactor_phase_5_loaded = window.ZNX.refactor_phase_5_loaded || {};
  window.ZNX.refactor_phase_5_loaded.Compras = true;
  // [Wave 32 marker v224.13] confirma extract executado
  window.Compras_v224_13_wave32 = true;

})();
