/**
 * A língua da compra e a conversão para a língua do consumo.
 *
 * O sistema inteiro pensa na unidade de consumo — m², folha, metro, ml — mas
 * ninguém compra "um metro quadrado de lona": compra-se uma bobina de
 * 1,60 × 50 m, uma resma de 500 folhas, uma caixa com 36 canecas. Este módulo
 * guarda os formatos e faz a conversão que antes acontecia na calculadora do
 * usuário: quanto UMA embalagem contém, e quanto custa a unidade de consumo.
 *
 * A conversão vive aqui, e não espalhada nas telas, porque o requisito é um
 * só: os dados precisam ser corretos e precisos — e conta repetida em três
 * lugares é conta que diverge.
 */

export type FormatoDeCompra =
  | "unidade"
  | "rolo"
  | "bobina"
  | "resma"
  | "chapa"
  | "galao"
  | "pacote"
  | "caixa";

export type EspecificacaoDeCompra = {
  formato: FormatoDeCompra;
  /* Bobina: largura fixa. */
  larguraM?: number | null;
  /* Bobina e rolo: comprimento típico de uma unidade de compra. */
  comprimentoM?: number | null;
  /* Resma/caixa/galão/pacote: conteúdo de UMA embalagem na unidade de
     consumo. */
  conteudoDaEmbalagem?: number | null;
  /* Folha e chapa: dimensões. */
  folhaLarguraM?: number | null;
  folhaAlturaM?: number | null;
};

export const FORMATOS: Record<FormatoDeCompra, {
  rotulo: string;
  /* Como se chama UMA unidade de compra, para a tela perguntar direito. */
  embalagem: string;
  embalagens: string;
  /* A unidade de consumo que faz sentido para o formato. Sugestão, não
     imposição: tinta pode consumir em ml ou em l. */
  unidadeSugerida: string;
}> = {
  unidade: { rotulo: "Unidade avulsa", embalagem: "unidade", embalagens: "unidades", unidadeSugerida: "un" },
  rolo:    { rotulo: "Rolo (metros)", embalagem: "rolo", embalagens: "rolos", unidadeSugerida: "m" },
  bobina:  { rotulo: "Bobina (largura × comprimento)", embalagem: "bobina", embalagens: "bobinas", unidadeSugerida: "m2" },
  resma:   { rotulo: "Resma / pacote de folhas", embalagem: "resma", embalagens: "resmas", unidadeSugerida: "folha" },
  chapa:   { rotulo: "Chapa", embalagem: "chapa", embalagens: "chapas", unidadeSugerida: "m2" },
  galao:   { rotulo: "Galão / frasco", embalagem: "galão", embalagens: "galões", unidadeSugerida: "l" },
  pacote:  { rotulo: "Pacote por peso", embalagem: "pacote", embalagens: "pacotes", unidadeSugerida: "kg" },
  caixa:   { rotulo: "Caixa de unidades", embalagem: "caixa", embalagens: "caixas", unidadeSugerida: "un" },
};

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

export function formatoValido(v: unknown): FormatoDeCompra {
  return typeof v === "string" && v in FORMATOS ? (v as FormatoDeCompra) : "unidade";
}

/**
 * Quanto UMA embalagem contém, na unidade de consumo.
 *
 * Bobina e chapa derivam das dimensões (largura × comprimento); os demais
 * leem o conteúdo declarado. Zero significa "não sei" — e conversão sobre o
 * não-sabido não acontece: melhor pedir o dado que inventar um.
 */
export function conteudoDaEmbalagem(spec: EspecificacaoDeCompra): number {
  switch (spec.formato) {
    case "unidade":
      return 1;
    case "bobina":
      return quatroCasas(positivo(spec.larguraM) * positivo(spec.comprimentoM));
    case "chapa":
      return quatroCasas(positivo(spec.folhaLarguraM) * positivo(spec.folhaAlturaM));
    case "rolo":
      return positivo(spec.comprimentoM);
    default:
      return positivo(spec.conteudoDaEmbalagem);
  }
}

/**
 * Quanto de bobina uma peça realmente ocupa.
 *
 * A impressora ocupa a largura inteira da bobina: um banner de 1,00 × 0,70
 * numa bobina de 1,60 m não gasta 0,70 m² — gasta a largura vezes o
 * comprimento ocupado. O sistema escolhe a orientação que gasta menos, e
 * quando a peça não cabe em nenhuma orientação (fachada maior que a bobina),
 * divide em faixas de emenda, como a produção faz.
 *
 *     ocupação = min( ceil(H/L)·L·W , ceil(W/L)·L·H )
 *
 * É deliberadamente o consumo de UMA peça rodada avulsa — o pior caso honesto.
 * Rodagem casada só existe no chão de fábrica; o ajuste de contagem reconcilia.
 * Esta função é usada na precificação E na baixa de estoque: uma conta, duas
 * pontas, nenhuma divergência.
 */
export function ocupacaoNaBobina(
  larguraBobinaM: number,
  larguraPecaM: number,
  alturaPecaM: number
): number {
  const L = positivo(larguraBobinaM);
  const w = positivo(larguraPecaM);
  const h = positivo(alturaPecaM);
  if (L <= 0 || w <= 0 || h <= 0) return 0;

  const orientacao1 = Math.ceil(h / L) * L * w;
  const orientacao2 = Math.ceil(w / L) * L * h;
  return quatroCasas(Math.min(orientacao1, orientacao2));
}

/**
 * A perda de rodagem em percentual: o que se paga de bobina além da área da
 * peça. Preenche o campo "Perda %" da linha — calculado em vez de chutado,
 * editável por quem roda casado.
 */
export function perdaDeRodagem(
  larguraBobinaM: number,
  larguraPecaM: number,
  alturaPecaM: number
): number {
  const area = positivo(larguraPecaM) * positivo(alturaPecaM);
  const ocupacao = ocupacaoNaBobina(larguraBobinaM, larguraPecaM, alturaPecaM);
  if (area <= 0 || ocupacao <= 0) return 0;
  return Math.round(((ocupacao - area) / area) * 1000) / 10;
}

export type CompraConvertida = {
  /* O que entra no estoque, na unidade de consumo. */
  quantidade: number;
  /* O que custa a unidade de consumo. É o número que alimenta a média
     ponderada e que a precificação lê. */
  custoUnitario: number;
};

/**
 * A conta da calculadora, aposentada: N embalagens de tanto conteúdo, a tanto
 * por embalagem. "2 resmas de 500 folhas por R$ 45" → 1.000 folhas a R$ 0,09.
 */
export function converterCompra(
  embalagens: number,
  conteudoPorEmbalagem: number,
  precoPorEmbalagem: number
): CompraConvertida | null {
  const n = positivo(embalagens);
  const conteudo = positivo(conteudoPorEmbalagem);
  const preco = Number(precoPorEmbalagem);
  if (n <= 0 || conteudo <= 0 || !Number.isFinite(preco) || preco < 0) return null;

  return {
    quantidade: quatroCasas(n * conteudo),
    custoUnitario: quatroCasas(preco / conteudo),
  };
}
