import {parseBR,productCalc,scenarios,priceScenarios} from './produto-modelo.mjs'; const $=s=>document.querySelector(s),form=$('#pc-form'),money=n=>n.toLocaleString('pt-BR',{style:'currency',currency:'BRL'}),pct=n=>n.toLocaleString('pt-BR',{maximumFractionDigits:2})+'%';let current=null; const fields=['cost','freight','pack','other','loss','price','discount','tax','commission','payment','fixedFee','units','fixed','target','nonCashFixed','desiredProfit']; const mandatory=new Set(['cost','price','units','target']); function rows(target,items){$(target).replaceChildren(...items.map(([label,value])=>{const row=document.createElement('div'),dt=document.createElement('dt'),dd=document.createElement('dd');dt.textContent=label;dd.textContent=value;row.append(dt,dd);return row}));} function hide(){current=null;$('#pc-results').hidden=true;$('#pc-error').textContent='';form.querySelectorAll('[aria-invalid]').forEach(e=>e.removeAttribute('aria-invalid'));} function run(){hide();const a={};for(const key of fields){const input=form.elements.namedItem(key);a[key]=parseBR(input.value,!mandatory.has(key));if(!Number.isFinite(a[key])||a[key]<0){input.setAttribute('aria-invalid','true');$('#pc-error').textContent='Confira o campo destacado. Use valores como 1.250,00 e percentuais como 3,5.';input.focus();return;}} try{const r=productCalc(a);current={a,r};$('#pc-results').hidden=false;$('#pc-status').textContent=r.contribution<=0?'A contribuição por venda não é positiva. Reveja preço, custos e taxas antes de ampliar o volume.':r.operating<0?'A venda contribui para os gastos fixos, mas o volume informado ainda não cobre o valor atribuído ao cenário.':'Neste cenário, a contribuição cobre os gastos fixos informados. Confira também as despesas e obrigações que ficaram fora da simulação.'; const cards=[['Preço após desconto',money(r.charged),'Por unidade vendida'],['Contribuição por unidade',money(r.contribution),pct(r.margin)+' sobre a venda'],['Resultado mensal do cenário',money(r.operating),'Após os gastos fixos informados'],['Preço-alvo após desconto',r.targetPrice===null?'Meta inviável':money(r.targetPrice),'Meta de '+pct(a.target)+' no volume informado']];$('#pc-cards').replaceChildren(...cards.map(([title,value,note])=>{const d=document.createElement('div');d.className='pc-metric';for(const [tag,text]of[['span',title],['strong',value],['small',note]]){const e=document.createElement(tag);e.textContent=text;d.append(e)}return d})); rows('#pc-breakdown',[['Preço de tabela',money(a.price)],['Desconto por unidade',money(a.price-r.charged)],['Receita após desconto',money(r.charged)],['Custos em reais por unidade, incluindo perdas',money(r.unitCost)],['Tributos informados',money(r.taxes)],['Comissão / marketplace',money(r.commission)],['Meio de pagamento',money(r.payment)],['Margem de contribuição',money(r.contribution)+' ('+pct(r.margin)+')'],['Gastos fixos atribuídos por unidade',money(r.allocated)]]); rows('#pc-pricing',[['Preço mínimo para cobrir este cenário',r.minPrice===null?'Fora do limite do modelo':money(r.minPrice)],['Preço de tabela para a meta, com desconto',r.targetListPrice===null?'Meta inviável com estas taxas':money(r.targetListPrice)],['Desconto máximo para cobrir este cenário',r.maxDiscount===null?'Não disponível':r.maxDiscount<0?'O preço de tabela já está abaixo do mínimo':pct(r.maxDiscount)],['Ponto de equilíbrio em unidades',r.breakEven===null?'Não atingível com contribuição não positiva':r.breakEven.toLocaleString('pt-BR')],['Receita no ponto de equilíbrio',r.breakEvenRevenue===null?'Não se aplica':money(r.breakEvenRevenue)],['Margem de segurança em volume',r.safety===null?'Não se aplica':pct(r.safety)],['Markup multiplicador',r.markup===null?'Base de custo zero':r.markup.toLocaleString('pt-BR',{maximumFractionDigits:3})+'×'],['Equilíbrio financeiro simplificado (unidades)',r.financialBreakEven===null?'Não atingível':r.financialBreakEven.toLocaleString('pt-BR')],['Equilíbrio com resultado desejado (unidades)',r.economicBreakEven===null?'Não atingível':r.economicBreakEven.toLocaleString('pt-BR')],['Alavancagem operacional',r.operatingLeverage===null?'Não interpretável com resultado não positivo':r.operatingLeverage.toFixed(2)+'×'],['Margem mensal do cenário',pct(r.monthlyMargin)]]); $('#pc-scenarios').replaceChildren(...scenarios(a).map(s=>{const tr=document.createElement('tr');[s.delta===0?'Volume informado':s.delta+'% no volume',s.units.toLocaleString('pt-BR'),money(s.netRevenue),money(s.operating)].forEach(t=>{const td=document.createElement('td');td.textContent=t;tr.append(td)});return tr}));$('#pc-price-scenarios').replaceChildren(...priceScenarios(a).map(s=>{const tr=document.createElement('tr');[s.delta===0?'Preço informado':s.delta+'% no preço',money(s.charged),money(s.contribution),money(s.operating)].forEach(t=>{const td=document.createElement('td');td.textContent=t;tr.append(td)});return tr}));$('#pc-results').focus({preventScroll:true});$('#pc-results').scrollIntoView({behavior:'auto',block:'start'}); }catch(e){$('#pc-error').textContent=e.message;}} form.addEventListener('submit',e=>{e.preventDefault();run()});form.addEventListener('input',hide);form.addEventListener('reset',hide); $('#pc-example').addEventListener('click',()=>{const example={cost:'60',freight:'2',pack:'2',other:'1',loss:'2',price:'120',discount:'5',tax:'6',commission:'3',payment:'2',fixedFee:'0',units:'200',fixed:'2000',target:'15',nonCashFixed:'300',desiredProfit:'1500'};for(const [k,v]of Object.entries(example))form.elements.namedItem(k).value=v;run()}); $('#pc-download').addEventListener('click',()=>{if(!current)return;const{a,r}=current;const labels={cost:'Custo unitário',freight:'Frete entrada unitário',pack:'Embalagem',other:'Outros custos variáveis',loss:'Perdas %',price:'Preço tabela',discount:'Desconto %',tax:'Tributos %',commission:'Comissão %',payment:'Pagamento %',fixedFee:'Tarifa fixa unitária',units:'Unidades mensais',fixed:'Gastos fixos atribuídos',target:'Meta margem %',nonCashFixed:'Gastos fixos sem desembolso',desiredProfit:'Resultado desejado'};const items=[['Simulação de produto — Contabilidade Carboni',''],['Campo','Valor'],...fields.map(k=>[labels[k],a[k]]),['Preço após desconto',r.charged],['Contribuição unitária',r.contribution],['Margem contribuição %',r.margin],['Resultado mensal do cenário',r.operating],['Preço-alvo',r.targetPrice??'Meta inviável'],['Equilíbrio unidades',r.breakEven??'Não atingível'],['Observação','Estimativa parcial. Não é apuração fiscal, lucro líquido ou fluxo de caixa.']];const csv='\uFEFF'+items.map(row=>row.map(v=>'"'+String(typeof v==='number'?v.toLocaleString('pt-BR',{useGrouping:false,maximumFractionDigits:4}):v).replaceAll('"','""')+'"').join(';')).join('\r\n');const url=URL.createObjectURL(new Blob([csv],{type:'text/csv;charset=utf-8'}));const link=document.createElement('a');link.href=url;link.download='carboni-simulacao-produto.csv';link.click();setTimeout(()=>URL.revokeObjectURL(url),1000);});