import { sortProductPriceTiers, validateAndNormalizePriceTiers, validatePriceTiersAgainstSaleLimits, type ProductPriceTier } from "../../price-tiers.ts";

/**
 * As regras do cadastro de produto pelo assistente, sem banco.
 *
 * O painel grava produto por uma rota grande, que recebe a ficha inteira. O
 * assistente mexe só num pedaço dela (nome, preço, descrição, categoria,
 * custo, códigos, prazo, quantidade mínima, faixas de preço e a loja), e as
 * regras desse pedaço moram aqui, com as mesmas conferências do painel. Fotos,
 * variações, ficha técnica, estoque e dados fiscais continuam só no painel.
 *
 * Só regra, para o teste ao lado rodar sozinho.
 */

export const VENDIDO_POR = {
  unidade: "unit",
  "metro quadrado": "square_meter",
  "metro linear": "linear_meter",
  metro: "meter",
  caixa: "box",
  resma: "ream",
} as const;

export const OPCOES_DE_VENDA = Object.keys(VENDIDO_POR) as Array<keyof typeof VENDIDO_POR>;

export const NOME_DA_UNIDADE: Record<string, string> = Object.fromEntries(Object.entries(VENDIDO_POR).map(([nome, unidade]) => [unidade, nome]));

export const UNIDADES_DO_PRAZO = {
  horas: "hours",
  dias: "days",
  "dias úteis": "business_days",
  semanas: "weeks",
} as const;

export const OPCOES_DO_PRAZO = Object.keys(UNIDADES_DO_PRAZO) as Array<keyof typeof UNIDADES_DO_PRAZO>;

const NOME_DO_PRAZO: Record<string, [string, string]> = {
  hours: ["hora", "horas"],
  days: ["dia", "dias"],
  business_days: ["dia útil", "dias úteis"],
  weeks: ["semana", "semanas"],
};

/** O teto de qualquer valor em reais que o assistente grava num produto. */
export const VALOR_MAXIMO = 10_000_000;

export function emReais(valor: number) {
  return (Math.round(valor * 100) / 100).toLocaleString("pt-BR", { style: "currency", currency: "BRL" });
}

/** Quantidade como a pessoa escreve: sem casas quando é inteira, com vírgula quando não é. */
export function quantidade(valor: number) {
  return Number.isInteger(valor) ? String(valor) : valor.toLocaleString("pt-BR", { maximumFractionDigits: 3 });
}

export function textoDoPrazo(valor: number | null | undefined, unidade: string | null | undefined): string | null {
  const numero = Number(valor ?? 0);
  if (!(numero > 0) || !unidade || !NOME_DO_PRAZO[unidade]) return null;
  const [singular, plural] = NOME_DO_PRAZO[unidade];
  return `${quantidade(numero)} ${numero === 1 ? singular : plural}`;
}

/** "R$ 35,00 por unidade", "R$ 80,00 por metro quadrado", ou "sob consulta". */
export function textoDoPreco(preco: number, unidade: string | null | undefined, sobConsulta: boolean): string {
  if (sobConsulta) return "sob consulta";
  return `${emReais(preco)} por ${NOME_DA_UNIDADE[unidade || "unit"] ?? "unidade"}`;
}

/** A margem sobre o preço de venda. Sem preço ou sem custo, não há o que dizer. */
export function textoDaMargem(preco: number, custo: number): string | null {
  if (!(preco > 0) || !(custo > 0)) return null;
  const margem = ((preco - custo) / preco) * 100;
  return `${margem.toLocaleString("pt-BR", { maximumFractionDigits: 1 })}%`;
}

export type FaixaDoAssistente = { a_partir_de?: unknown; ate?: unknown; preco_unitario?: unknown };

export type LimitesDeVenda = {
  min_sale_qty_enabled?: boolean | null;
  min_sale_qty?: number | null;
  max_sale_qty_enabled?: boolean | null;
  max_sale_qty?: number | null;
  allowed_sale_qty_enabled?: boolean | null;
  allowed_sale_quantities?: number[] | null;
};

/**
 * As faixas de preço por quantidade, do jeito que o assistente manda ("a partir
 * de 100, R$ 1,20 cada"), conferidas pelas mesmas regras do painel: valores
 * positivos, sem sobrepor, e dentro das quantidades mínima e máxima de venda.
 */
export function lerFaixas(faixas: FaixaDoAssistente[], limites: LimitesDeVenda): { ok: true; faixas: ProductPriceTier[] } | { ok: false; erro: string } {
  for (const faixa of faixas) {
    if (Number(faixa.preco_unitario) > VALOR_MAXIMO) return { ok: false, erro: `O preço de uma faixa passou do máximo de ${emReais(VALOR_MAXIMO)}.` };
  }
  const { tiers, error } = validateAndNormalizePriceTiers(
    faixas.map((faixa) => ({ min_qty: faixa.a_partir_de, max_qty: faixa.ate ?? null, unit_price: faixa.preco_unitario }))
  );
  if (error) return { ok: false, erro: error };
  const contraOsLimites = validatePriceTiersAgainstSaleLimits(tiers, limites);
  if (contraOsLimites.error) return { ok: false, erro: contraOsLimites.error };
  return { ok: true, faixas: tiers };
}

/** "de 100 a 499: R$ 1,20 cada", "a partir de 500: R$ 0,90 cada". */
export function textoDasFaixas(faixas: ReadonlyArray<Pick<ProductPriceTier, "min_qty" | "max_qty" | "unit_price" | "active">>): string[] {
  return sortProductPriceTiers(faixas.filter((faixa) => faixa.active !== false).map((faixa) => ({ ...faixa }))).map((faixa) => {
    const de = quantidade(Number(faixa.min_qty));
    const preco = `${emReais(Number(faixa.unit_price))} cada`;
    return faixa.max_qty === null || faixa.max_qty === undefined ? `a partir de ${de}: ${preco}` : `de ${de} a ${quantidade(Number(faixa.max_qty))}: ${preco}`;
  });
}

/**
 * O que a pessoa precisa saber sobre o preço antes de confirmar.
 *
 * Faixa que começa em 1 manda no preço de qualquer quantidade, e o preço base
 * deixa de aparecer; quantidades fixas também são faixas. Mudar o preço base
 * sem saber disso é mudar um número que ninguém vê.
 */
export function avisosDoPreco(estado: { preco: number; sobConsulta: boolean; faixas: ReadonlyArray<Pick<ProductPriceTier, "min_qty" | "active">>; quantidadesFixas: boolean; mudouOPreco: boolean }): string[] {
  const avisos: string[] = [];
  if (estado.sobConsulta || !estado.mudouOPreco) return avisos;
  const ativas = estado.faixas.filter((faixa) => faixa.active !== false);
  if (ativas.some((faixa) => Number(faixa.min_qty) <= 1)) {
    avisos.push("Este produto tem uma faixa de preço que começa em 1 unidade: é ela que vale nos orçamentos e na loja, e não o preço base.");
  } else if (ativas.length) {
    avisos.push("Este produto tem faixas de preço por quantidade: o preço base só vale abaixo da primeira faixa.");
  }
  if (estado.quantidadesFixas) {
    avisos.push("Este produto vende só em quantidades fixas, cada uma com o próprio preço: o preço base pode não aparecer na loja.");
  }
  return avisos;
}

export type Mudanca = { campo: string; antes: string | null; depois: string | null };

/** Só entra o que de fato muda. A ordem é a da tela do produto. */
export function soOQueMuda(mudancas: Mudanca[]): Mudanca[] {
  return mudancas.filter((mudanca) => (mudanca.antes ?? "") !== (mudanca.depois ?? ""));
}

/** A linha do registro de atividade: "preço R$ 35,00 → R$ 39,90; na loja: não → sim". */
export function resumoDasMudancas(mudancas: Mudanca[], maximo = 3): string {
  const partes = mudancas.slice(0, maximo).map((mudanca) => `${mudanca.campo}: ${mudanca.antes ?? "vazio"} → ${mudanca.depois ?? "vazio"}`);
  const resto = mudancas.length - partes.length;
  return resto > 0 ? `${partes.join("; ")} e mais ${resto}` : partes.join("; ");
}

/** O texto da descrição para a prévia: sem marcação e com limite. */
export function descricaoParaLer(html: string | null | undefined, maximo = 300): string | null {
  if (!html) return null;
  const limpo = html
    .replace(/<\s*(br|\/p|\/li|\/h[1-6])\s*\/?>/gi, "\n")
    .replace(/<[^>]*>/g, " ")
    .replace(/&nbsp;/gi, " ")
    .replace(/&amp;/gi, "&")
    .replace(/&lt;/gi, "<")
    .replace(/&gt;/gi, ">")
    .replace(/&quot;/gi, '"')
    .replace(/&#39;/gi, "'")
    .replace(/[ \t]+/g, " ")
    .replace(/\s*\n\s*/g, "\n")
    .trim();
  if (!limpo) return null;
  return limpo.length > maximo ? `${limpo.slice(0, maximo)}…` : limpo;
}
