/*
  Estoque v2 na loja — as contas puras (sem banco, sem React).

  Para a empresa que está no estoque v2, quem diz se dá para vender é o banco
  (estoque_disponivel / estoque_conferir_itens), a MESMA conta do PDV, do
  orçamento e do assistente: por combinação exata, somando por célula, e
  nunca bloqueando produto que não controla ou que permite negativo
  (`limita = false`). Aqui só se traduz a resposta para a tela da loja.

  Fora do v2 nada daqui é usado: a loja segue com a regra de sempre
  (estoqueLimitaAVenda em lib/loja/regras-da-loja.ts).

  Ao cliente a loja nunca mostra o nome dos insumos nem números da
  composição: diz a combinação que ele escolheu e se está esgotada ou quanto
  dá para levar. O detalhe ("Falta Camiseta lisa (G / Preta): tem 0") é do
  painel.

  Importável pelo navegador, pelo servidor e pelo teste (node --test).
*/

/** A resposta do banco (estoque_disponivel). */
export type RespostaDoEstoque = {
  v2?: boolean;
  modo?: string | null;
  controla?: boolean | null;
  limita?: boolean | null;
  pedido?: number | null;
  cabe?: boolean | null;
  disponivel?: number | null;
  motivo?: string | null;
  itens?: Array<{
    nome?: string | null;
    rotulo?: string | null;
    saldo?: number | null;
    precisa?: number | null;
    permite_negativo?: boolean | null;
    sem_celula?: boolean | null;
    falta_escolher?: string | null;
  }> | null;
  erro?: string | null;
};

/** O que a página do produto precisa saber da escolha atual. */
export type EstoqueDaEscolha = {
  v2: true;
  /** O saldo limita a venda desta escolha? false = vende sem teto (não controla / permite negativo). */
  limita: boolean;
  /** Maior quantidade que cabe (já com a escolha). null = sem teto. */
  disponivel: number | null;
  /** A quantidade pedida cabe? */
  cabe: boolean;
  /** "falta_escolher" quando ainda falta tamanho/cor (ou a escolha não existe no estoque). */
  motivo: string | null;
  /** Frase para o cliente, ou null. */
  aviso: string | null;
};

/** O resumo de cada produto na vitrine (sem escolha, quantidade 1). */
export type EstoqueDaVitrine = {
  modo?: string | null;
  controla?: boolean | null;
  limita?: boolean | null;
  cabe?: boolean | null;
  motivo?: string | null;
};

function numero(valor: unknown): number | null {
  if (valor === null || valor === undefined || valor === "") return null;
  const n = Number(valor);
  return Number.isFinite(n) ? n : null;
}

function quantidadeEscrita(valor: number): string {
  const arredondado = Number(valor.toFixed(3));
  return Number.isInteger(arredondado)
    ? String(arredondado)
    : new Intl.NumberFormat("pt-BR", { maximumFractionDigits: 3 }).format(arredondado);
}

type OpcaoEscolhida = { attribute_name?: unknown; label?: unknown; affects_stock?: unknown };

/**
 * "G / Preta": as escolhas de atributo do cliente (sem os serviços, que
 * viajam no mesmo `options` mas não têm atributo do produto). Vazio = "".
 */
export function combinacaoDaEscolha(selected: unknown, atributosDoProduto?: string[] | null): string {
  const opcoes = (selected as { options?: unknown[] } | null)?.options;
  if (!Array.isArray(opcoes)) return "";
  const nomes = Array.isArray(atributosDoProduto)
    ? new Set(atributosDoProduto.map((nome) => String(nome || "").trim().toLowerCase()))
    : null;
  return opcoes
    .filter((opcao): opcao is OpcaoEscolhida => Boolean(opcao) && typeof opcao === "object")
    .filter((opcao) => !nomes || nomes.has(String(opcao.attribute_name || "").trim().toLowerCase()))
    .map((opcao) => String(opcao.label || "").trim())
    .filter(Boolean)
    .join(" / ");
}

/** O que falta escolher, dito pelo banco ("tamanho e cor"), ou null. */
function faltaEscolher(resposta: RespostaDoEstoque): string | null {
  for (const item of resposta.itens || []) {
    if (item?.sem_celula && item.falta_escolher) return String(item.falta_escolher);
  }
  return null;
}

/**
 * A resposta do banco vira o que a página mostra. `combinacao` é o texto da
 * escolha ("G / Preta") para a frase; `escolhaCompleta` diz se o cliente já
 * escolheu tudo que é obrigatório (antes disso, "falta escolher" não é aviso).
 */
export function estoqueDaEscolha(
  resposta: RespostaDoEstoque | null | undefined,
  opcoes: { combinacao?: string; escolhaCompleta?: boolean } = {}
): EstoqueDaEscolha | null {
  if (!resposta || resposta.v2 !== true || resposta.erro) return null;
  const limita = resposta.limita === true;
  const motivo = resposta.motivo ? String(resposta.motivo) : null;
  const disponivelBruto = numero(resposta.disponivel);
  const disponivel = limita ? Math.max(0, disponivelBruto ?? 0) : null;
  const cabe = resposta.cabe !== false;
  const sufixo = opcoes.combinacao ? ` em ${opcoes.combinacao}` : "";
  let aviso: string | null = null;
  if (motivo === "falta_escolher") {
    if (opcoes.escolhaCompleta) {
      const falta = faltaEscolher(resposta);
      aviso = falta ? `Escolha ${falta} para ver se tem em estoque.` : "Esta combinação não está disponível.";
    }
  } else if (limita && disponivel !== null && disponivel <= 0) {
    aviso = `Esgotado${sufixo}.`;
  } else if (limita && !cabe && disponivel !== null) {
    aviso = `Só ${quantidadeEscrita(disponivel)} ${disponivel === 1 ? "disponível" : "disponíveis"}${sufixo}.`;
  }
  return { v2: true, limita, disponivel, cabe, motivo, aviso };
}

/**
 * Esgotado na vitrine (selo e "esconder esgotados"): só quando o banco diz
 * que limita e não cabe nem 1, SEM faltar escolha. Produto que precisa de
 * tamanho/cor não é "esgotado" na lista — a página dele diz por combinação.
 */
export function esgotadoNaVitrine(resumo: EstoqueDaVitrine | null | undefined): boolean {
  if (!resumo) return false;
  return resumo.limita === true && resumo.cabe === false && !resumo.motivo;
}

/** As marcas que a vitrine leva em cada produto de empresa no v2. */
export function marcarProduto<T extends Record<string, unknown>>(
  produto: T,
  resumo: EstoqueDaVitrine | null | undefined
): T & { estoque_v2: true; estoque_v2_limita: boolean; estoque_v2_esgotado: boolean } {
  return {
    ...produto,
    estoque_v2: true,
    estoque_v2_limita: resumo?.limita === true && !resumo?.motivo,
    estoque_v2_esgotado: esgotadoNaVitrine(resumo),
  };
}

type Problema = { indice?: number | null; disponivel?: number | null; titulo?: string | null; celula_id?: string | null };
type ItemDoCarrinho = { title?: string | null; name?: string | null; selected_options?: unknown };

/**
 * As faltas do carrinho (estoque_conferir_itens), em frase para o cliente:
 * o produto e a combinação dele, esgotado ou quanto dá. Uma frase por item.
 */
export function avisosDoCarrinho(problemas: Problema[] | null | undefined, itens: ItemDoCarrinho[]): Map<number, string> {
  const avisos = new Map<number, string>();
  for (const problema of Array.isArray(problemas) ? problemas : []) {
    const indice = Number(problema?.indice);
    if (!Number.isInteger(indice) || indice < 0 || avisos.has(indice)) continue;
    const item = itens[indice];
    const nome = String(item?.title || item?.name || problema?.titulo || "Produto").trim();
    const combinacao = combinacaoDaEscolha(item?.selected_options);
    const quem = combinacao ? `${nome} (${combinacao})` : nome;
    const disponivel = Math.max(0, Math.floor(numero(problema?.disponivel) ?? 0));
    if (!problema?.celula_id) {
      avisos.set(indice, `${quem}: esta combinação não está disponível no momento.`);
    } else if (disponivel <= 0) {
      avisos.set(indice, `${quem}: esgotado.`);
    } else {
      avisos.set(indice, `${quem}: só ${disponivel} ${disponivel === 1 ? "disponível" : "disponíveis"}.`);
    }
  }
  return avisos;
}

/**
 * A resposta de estoque_conferir_itens veio do estoque v2? Lá todo problema
 * traz `celula_id` (mesmo nulo); na regra antiga a chave não existe.
 */
export function problemasDoV2(problemas: unknown): boolean {
  return Array.isArray(problemas) && problemas.some((p) => p && typeof p === "object" && "celula_id" in p);
}

/** Junta as frases do carrinho numa só (checkout/cobrança). null = cabe. */
export function fraseDoCarrinho(avisos: Map<number, string>): string | null {
  const lista = Array.from(avisos.values());
  if (!lista.length) return null;
  const primeiras = lista.slice(0, 2).join(" ");
  const resto = lista.length > 2 ? ` E mais ${lista.length - 2} item(ns).` : "";
  return `${primeiras}${resto} Ajuste o carrinho e tente de novo.`;
}
