/**
 * A matemática do termômetro do mês: progresso do ponto de equilíbrio,
 * ritmo, previsões e a curva ABC de contribuição.
 *
 * O modelo é o consagrado do ramo (RKW): a margem de contribuição de uma
 * venda é o que sobra depois da matéria-prima e dos custos variáveis da
 * venda — a mão de obra e os fixos pelo tempo ficam DENTRO da contribuição,
 * porque são exatamente a recuperação do custo fixo que a venda faz. A soma
 * das contribuições do mês paga o custo fixo; dali em diante vira lucro.
 *
 * Antes, a mão de obra era subtraída da margem E contada na meta de fixos —
 * o equilíbrio saía superestimado. Aqui a conta fecha dos dois lados: os
 * salários e a energia estão no custo fixo mensal (categorias fixo e
 * fixo_producao), e a venda que os recupera pontua no termômetro.
 *
 * Tudo função pura: o teste ao lado cobre as pontas (primeiro dia, último
 * dia, mês sem venda, contribuição negativa, custo fixo zerado).
 */

const duasCasas = (n: number) => Math.round(n * 100) / 100;
const positivo = (v: unknown) => {
  const n = Number(v);
  return Number.isFinite(n) && n > 0 ? n : 0;
};

export type ProgressoDoMes = {
  /* Quanto do custo fixo o mês já pagou. Pode passar de 100. */
  progressoPct: number;
  /* Contribuição acumulada − custo fixo. Negativo = ainda devendo. */
  lucroAtual: number;
  bateu: boolean;
  diasRestantes: number;
  /* O proporcional dos dias corridos: onde a contribuição DEVERIA estar. */
  cmNecessariaAteHoje: number;
  emRitmo: boolean;
  /* A margem média real do mês — é ela que dita o faturamento necessário. */
  mcPctDoMes: number;
  /* Custo fixo ÷ margem do mês. Nulo sem contribuição positiva: nenhum
     volume de venda pagaria as contas. */
  faturamentoParaEquilibrio: number | null;
  metaVendaAteHoje: number | null;
  /* No ritmo dos dias corridos, onde o mês termina. */
  previsaoCm: number;
  previsaoLucro: number;
  previsaoVenda: number;
  /* A meta de VENDAS da gestão, quando definida — decisão de quem quer
     crescer, não consequência da conta. Nula: vale só o equilíbrio. */
  metaDoComercial: number | null;
  metaDoComercialAteHoje: number | null;
  metaDoComercialEmRitmo: boolean | null;
};

export function progressoDoMes(entrada: {
  fixoMensal: number;
  cmAcumulada: number;
  receitaMes: number;
  diaAtual: number;
  diasNoMes: number;
  metaDeVendas?: number;
}): ProgressoDoMes {
  const fixo = positivo(entrada.fixoMensal);
  const cm = Number(entrada.cmAcumulada) || 0;
  const receita = positivo(entrada.receitaMes);
  const diasNoMes = Math.max(1, Math.round(positivo(entrada.diasNoMes)) || 30);
  const diaAtual = Math.min(diasNoMes, Math.max(1, Math.round(positivo(entrada.diaAtual)) || 1));
  const metaDeVendas = positivo(entrada.metaDeVendas);

  const mcPctDoMes = receita > 0 ? duasCasas((cm / receita) * 100) : 0;
  const faturamentoParaEquilibrio =
    fixo > 0 && mcPctDoMes > 0 ? duasCasas(fixo / (mcPctDoMes / 100)) : null;

  const fracaoDoMes = diaAtual / diasNoMes;
  /* Ritmo: extrapolação linear dos dias corridos. No último dia, previsão é
     o próprio realizado — sem inventar futuro que não existe. */
  const previsaoCm = duasCasas((cm / diaAtual) * diasNoMes);
  const previsaoVenda = duasCasas((receita / diaAtual) * diasNoMes);

  return {
    progressoPct: fixo > 0 ? duasCasas((cm / fixo) * 100) : 0,
    lucroAtual: duasCasas(cm - fixo),
    bateu: fixo > 0 && cm >= fixo,
    diasRestantes: diasNoMes - diaAtual,
    cmNecessariaAteHoje: duasCasas(fixo * fracaoDoMes),
    emRitmo: cm >= fixo * fracaoDoMes,
    mcPctDoMes,
    faturamentoParaEquilibrio,
    metaVendaAteHoje:
      faturamentoParaEquilibrio != null ? duasCasas(faturamentoParaEquilibrio * fracaoDoMes) : null,
    previsaoCm,
    previsaoLucro: duasCasas(previsaoCm - fixo),
    previsaoVenda,
    metaDoComercial: metaDeVendas > 0 ? duasCasas(metaDeVendas) : null,
    metaDoComercialAteHoje: metaDeVendas > 0 ? duasCasas(metaDeVendas * fracaoDoMes) : null,
    metaDoComercialEmRitmo: metaDeVendas > 0 ? receita >= metaDeVendas * fracaoDoMes : null,
  };
}

export type ItemABC = { receita: number; cm: number };
export type ClassificadoABC<T> = T & { classe: "A" | "B" | "C"; participacaoPct: number };

/**
 * Curva ABC pela contribuição: A até 80% do acumulado, B até 95%, C o resto.
 *
 * A régua é a contribuição POSITIVA total — cliente de contribuição zero ou
 * negativa é C por definição: ele não está pagando a casa, está morando de
 * favor.
 */
export function curvaABC<T extends ItemABC>(itens: T[]): ClassificadoABC<T>[] {
  const ordenados = [...itens].sort((a, b) => b.cm - a.cm);
  const totalPositivo = ordenados.reduce((acc, i) => acc + Math.max(0, i.cm), 0);

  let acumulado = 0;
  return ordenados.map((item) => {
    if (item.cm <= 0 || totalPositivo <= 0) {
      return { ...item, classe: "C" as const, participacaoPct: 0 };
    }
    acumulado += item.cm;
    const acumuladoPct = (acumulado / totalPositivo) * 100;
    /* A classe olha o acumulado ANTES de cruzar a régua: o item que cruza os
       80% ainda é A — é ele que fecha o bloco. */
    const classe = acumuladoPct - (item.cm / totalPositivo) * 100 < 80
      ? "A" as const
      : acumuladoPct - (item.cm / totalPositivo) * 100 < 95
        ? "B" as const
        : "C" as const;
    return { ...item, classe, participacaoPct: duasCasas((item.cm / totalPositivo) * 100) };
  });
}

/**
 * Divide o custo de um item vendido em matéria-prima × mão de obra, na
 * proporção da ficha do produto.
 *
 * O custo congelado na venda é um número só; a ficha diz de que ele é feito.
 * Sem ficha (ou ficha zerada), TUDO é tratado como matéria-prima — o lado
 * conservador: contribuição menor, nunca maior do que a real.
 */
export function dividirCusto(
  custoDoItem: number,
  materiaisDaFicha: number,
  processosDaFicha: number
): { materiais: number; processos: number } {
  const custo = positivo(custoDoItem);
  const mat = positivo(materiaisDaFicha);
  const proc = positivo(processosDaFicha);
  const somaFicha = mat + proc;
  if (custo <= 0) return { materiais: 0, processos: 0 };
  if (somaFicha <= 0) return { materiais: custo, processos: 0 };
  const materiais = duasCasas(custo * (mat / somaFicha));
  return { materiais, processos: duasCasas(custo - materiais) };
}
