// js/lib/produtos/calcs.js
// [Wave 7 KIMI 2026-05-17] Extraído de Produtos.jsx L537-552 (isLossSelling + stockBadge).
// [Wave 25-alt v224.6 NUCLEAR 2026-05-23] EXPANSION +5 factories:
//   filterProductsWithLoss, computeCustoPreview, makeSortComparator, filterByStatus, extractBrand
//
// Deps runtime globals (window): isLowStock (declarado index.html L717).
// Validação RUNTIME (padrão Wave 6 v223.2).
(function() {
  'use strict';

  // regra_validacao_helpers_runtime_quando_ordem_scripts_uncertain — RUNTIME 1x
  let _validated = false;
  function _validateHelpers() {
    if (_validated) return;
    _validated = true;
    const _missing = [];
    if (typeof isLowStock === 'undefined') _missing.push('isLowStock');
    if (_missing.length > 0) {
      const _msg = `[lib/produtos/calcs] runtime helpers faltando: ${_missing.join(', ')}`;
      console.error(_msg);
      window.Sentry?.captureMessage?.(_msg, 'error');
    }
  }

  // [Onda L Fase 1 — 20260507] Detecta produto vendendo com prejuízo (sale_price <= avg_cost)
  function isLossSelling(p){
    _validateHelpers();
    const cost = Number(p.avgCost||p.avg_cost||0);
    const price = Number(p.salePrice||p.sale_price||0);
    return cost > 0 && price > 0 && price <= cost;
  }

  // Badge JSX — retorna <span> baseado em stock + isLowStock helper
  function stockBadge(p, sales){
    _validateHelpers();
    if(p.stock<=0)return<span className="badge badge-red">Zerado</span>;
    if(p.stock<=(p.estoqueMin||20))return<span className="badge badge-red" style={{background:'#3a1800',color:'#EA580C'}}>Acabando</span>;
    if(isLowStock(p,sales))return<span className="badge badge-yellow">Baixo</span>;
    return<span className="badge badge-green">OK</span>;
  }

  // ═══ FACTORIES NOVAS Wave 25-alt v224.6 ═══

  // extractBrand helper (fallback se já não existe global)
  function extractBrand(productName){
    if(!productName) return '';
    const first = String(productName).trim().split(/\s+/)[0];
    return first || '';
  }

  // Filter products with loss (cost >= salePrice · active != false · !deletedAt)
  function filterProductsWithLoss(products){
    return (products||[]).filter(function(p){
      if(p.active === false || p.deletedAt) return false;
      return isLossSelling(p);
    });
  }

  // Custo médio preview (entrada novo lote)
  function computeCustoPreview(form){
    const newQty = Number(form.newQty||0);
    const newCost = Number(form.newCost||0);
    const oldStock = Number(form.stock||0);
    const oldCost = Number(form.avgCost||0);
    if(newQty <= 0 || newCost <= 0) return null;
    const newStock = oldStock + newQty;
    const newAvg = oldStock === 0 ? newCost : Math.round(((oldStock * oldCost + newQty * newCost) / newStock) * 100) / 100;
    return {
      old: oldCost,
      newAvg: newAvg,
      diferenca: newAvg - oldCost,
      newStock: newStock
    };
  }

  // Sort comparator factory (column + direction · matches inline L216-226 LITERAL)
  function makeSortComparator(col, dir, suppliers){
    return function(a, b){
      function statusRank(p){
        const min = p.estoqueMin||20;
        if(p.stock<=0) return 0;
        if(p.stock<=min) return 1;
        return 2;
      }
      let va, vb;
      if(col === 'brand'){ va = (a.brand||''); vb = (b.brand||''); }
      else if(col === 'name'){ va = (a.name||''); vb = (b.name||''); }
      else if(col === 'stock'){ va = a.stock||0; vb = b.stock||0; }
      else if(col === 'salePrice'){ va = a.salePrice||0; vb = b.salePrice||0; }
      else if(col === 'avgCost'){ va = a.avgCost||0; vb = b.avgCost||0; }
      else if(col === 'status'){ va = statusRank(a); vb = statusRank(b); }
      else { va = a[col]||0; vb = b[col]||0; }
      const sign = dir === 'asc' ? 1 : -1;
      if(typeof va === 'string') return va.localeCompare(vb) * sign;
      return (va - vb) * sign;
    };
  }

  // Filter by status (factory pra futuro · main file mantém inline por enquanto)
  function filterByStatus(products, filterStatus){
    if(!filterStatus) return products;
    return (products||[]).filter(function(p){
      const stock = Number(p.stock||0);
      const minStock = Number(p.estoqueMin||20);
      if(filterStatus === 'ok') return stock > minStock;
      if(filterStatus === 'acabando') return stock > 0 && stock <= minStock;
      if(filterStatus === 'zerado') return stock <= 0;
      if(filterStatus === 'prejuizo') return isLossSelling(p);
      if(filterStatus === 'sem_fornecedor') return !p.supplierId;
      if(filterStatus === 'sem_categoria') return !p.categoria;
      if(filterStatus === 'sem_volume') return !p.volume;
      if(filterStatus === 'sem_genero') return !p.genero;
      if(filterStatus === 'sem_ncm') return !p.ncm;
      if(filterStatus === 'sem_ean') return !p.ean;
      if(filterStatus === 'custo_zero') return Number(p.avgCost||0) <= 0;
      return true;
    });
  }

  // [F4b 20260804] VIGÊNCIA da promoção — espelho JS de public.znx_promocao_vigente().
  //
  // is_promotion  = MARCADO  (o operador respondeu "promoção" quando o preço caiu)
  // promocaoVigente() = VIGENTE (marcado E ainda dentro do prazo)
  //
  // NÃO existe job que desliga promoção vencida, e isso é de propósito: `products` já tem
  // 9 triggers (duas já morderam a casa) e uma linha marcada não precisa ser apagada quando
  // vence — precisa ser lida direito. is_promotion=true numa promoção vencida é o registro
  // histórico da decisão do operador, e é o que dá sentido ao original_price.
  // Se você veio aqui pra "consertar" ligando um cron: não é bug, é o desenho.
  function promocaoVigente(p){
    if(!p) return false;
    const marcado = (p.is_promotion===true) || (p.isPromotion===true);
    if(!marcado) return false;
    const ends = p.promotion_ends_at || p.promotionEndsAt || null;
    if(!ends) return true;                    // sem prazo = vigente (espelha p_ends_at IS NULL)
    const t = Date.parse(ends);
    if(Number.isNaN(t)) return true;          // data ilegível: não esconde o que está marcado
    return t > Date.now();
  }

  window.ZNX = window.ZNX || {};
  window.ZNX.produtos = window.ZNX.produtos || {};
  window.ZNX.produtos.calcs = {
    isLossSelling: isLossSelling,
    promocaoVigente: promocaoVigente,
    stockBadge: stockBadge,
    extractBrand: extractBrand,
    filterProductsWithLoss: filterProductsWithLoss,
    computeCustoPreview: computeCustoPreview,
    makeSortComparator: makeSortComparator,
    filterByStatus: filterByStatus
  };
  window.ZNX_produtos_calcs_v224_6_wave25alt = true;
})();
