/*
 * Regras puras das ferramentas de operação (agenda, cupons, clientes,
 * relatórios, pedidos). Sem banco: é o que dá para testar sozinho.
 */

/* ------------------------------------------------------------------ cupons */

/** O código como a loja procura: maiúsculo, sem espaço. Null = não serve. */
export function codigoDoCupom(valor: unknown): string | null {
  const codigo = String(valor ?? "").trim().toUpperCase();
  /* Letras, números e hífen: a loja procura o código com "parecido com", em
     que o sublinhado vale por qualquer letra. */
  if (!/^[A-Z0-9-]{3,30}$/.test(codigo)) return null;
  return codigo;
}

export type RegrasDoCupom = {
  type: "percent" | "value";
  value: number;
  min_total: number;
  min_items: number;
  starts_at: string | null;
  ends_at: string | null;
};

/** Confere as regras do cupom como a tela confere. Devolve o erro, ou null. */
export function erroDoCupom(regras: RegrasDoCupom): string | null {
  if (!Number.isFinite(regras.value) || regras.value <= 0) return "O valor do desconto precisa ser maior que zero.";
  if (regras.type === "percent" && regras.value > 100) return "Desconto em porcentagem vai no máximo até 100%.";
  if (!Number.isFinite(regras.min_total) || regras.min_total < 0) return "O pedido mínimo não pode ser negativo.";
  if (!Number.isInteger(regras.min_items) || regras.min_items < 0) return "A quantidade mínima de itens precisa ser um número inteiro, zero ou mais.";
  if (regras.starts_at && regras.ends_at && regras.ends_at < regras.starts_at) return "O fim da validade é antes do começo.";
  return null;
}

/** Começo e fim do dia no horário de Brasília, como a loja compara. */
export function comecoDoDia(iso: string) {
  return `${iso}T00:00:00-03:00`;
}
export function fimDoDia(iso: string) {
  return `${iso}T23:59:59-03:00`;
}

/** O dia (AAAA-MM-DD) de um instante, no horário de Brasília. */
export function diaEmBrasilia(instante: string | null | undefined): string | null {
  if (!instante) return null;
  const data = new Date(instante);
  if (Number.isNaN(data.getTime())) return null;
  return new Intl.DateTimeFormat("en-CA", { timeZone: "America/Sao_Paulo", year: "numeric", month: "2-digit", day: "2-digit" }).format(data);
}

export function situacaoDoCupom(
  cupom: { active: boolean | null; starts_at: string | null; ends_at: string | null },
  agora: number = Date.now()
): string {
  if (cupom.active === false) return "desligado";
  if (cupom.starts_at && new Date(cupom.starts_at).getTime() > agora) return "ainda não começou";
  if (cupom.ends_at && new Date(cupom.ends_at).getTime() < agora) return "vencido";
  return "valendo";
}

/* ------------------------------------------------------------------ pedidos */

/** Venda do PDV: tem cancelamento próprio (estorno) e não passa pela produção. Mesma regra da tela do pedido. */
export function ehVendaDoPdv(pedido: { internal_notes?: string | null; quoteTitle?: string | null; quoteInternalNotes?: string | null }) {
  const titulo = String(pedido.quoteTitle || "").toLowerCase();
  const notasDoOrcamento = String(pedido.quoteInternalNotes || "").toLowerCase();
  const notasDoPedido = String(pedido.internal_notes || "").toLowerCase();
  return titulo.includes("venda pdv") || notasDoOrcamento.includes("origem:pdv") || notasDoPedido.includes("pdv");
}

export type EtapaDoQuadro = { id: string; label: string; status: string; position: number };

const SITUACOES_DO_QUADRO = new Set(["pending_production", "in_production", "finished", "delivered"]);

/** As etapas que a empresa criou no quadro da produção (profiles.production_kanban_columns). */
export function etapasDoQuadro(bruto: unknown): EtapaDoQuadro[] {
  if (!Array.isArray(bruto)) return [];
  return bruto
    .map((item, indice) => {
      if (!item || typeof item !== "object") return null;
      const registro = item as Record<string, unknown>;
      /* Sem id a tela inventa um a cada carga: não há como apontar para ela. */
      if (typeof registro.id !== "string" || !registro.id) return null;
      const rotulo = typeof registro.label === "string" && registro.label.trim() ? registro.label.trim() : `Etapa ${indice + 1}`;
      const status = typeof registro.status === "string" && SITUACOES_DO_QUADRO.has(registro.status) ? registro.status : "in_production";
      const posicao = Number.isFinite(Number(registro.position)) ? Number(registro.position) : indice;
      return { id: registro.id, label: rotulo, status, position: posicao };
    })
    .filter((etapa): etapa is EtapaDoQuadro => Boolean(etapa))
    .sort((a, b) => a.position - b.position);
}

export function semAcento(valor: unknown) {
  return String(valor ?? "")
    .normalize("NFD")
    .replace(/[̀-ͯ]/g, "")
    .trim()
    .toLowerCase();
}

/** A etapa pelo nome, sem ligar para acento e maiúscula. */
export function acharEtapa(etapas: EtapaDoQuadro[], nome: string): EtapaDoQuadro | null {
  const procurado = semAcento(nome);
  return etapas.find((etapa) => semAcento(etapa.label) === procurado) ?? null;
}

/**
 * Mensagem vinda do banco ou de uma rota: passa à pessoa só se for frase em
 * português, sem detalhe técnico. Senão, a frase reserva.
 */
export function mensagemParaPessoa(mensagem: unknown, reserva: string) {
  const texto = String(mensagem ?? "").trim();
  if (!texto || texto.length > 300) return reserva;
  if (/violates|constraint|relation|column|function|syntax|duplicate key|null value|PGRST|uuid|jwt|permission denied|row-level/i.test(texto)) return reserva;
  return texto;
}

/* ----------------------------------------------------------------- clientes */

export const soDigitos = (valor: unknown) => String(valor ?? "").replace(/\D/g, "");

export function documentoFormatado(digitos: string) {
  if (digitos.length === 11) return digitos.replace(/(\d{3})(\d{3})(\d{3})(\d{2})/, "$1.$2.$3-$4");
  if (digitos.length === 14) return digitos.replace(/(\d{2})(\d{3})(\d{3})(\d{4})(\d{2})/, "$1.$2.$3/$4-$5");
  return digitos;
}

/** O WhatsApp como o painel grava: +55 e os dígitos com DDD. */
export function whatsappNoPadrao(valor: unknown): string | null | "invalido" {
  let digitos = soDigitos(valor);
  if (!digitos) return null;
  if (digitos.startsWith("55") && digitos.length >= 12) digitos = digitos.slice(2);
  if (digitos.length !== 10 && digitos.length !== 11) return "invalido";
  return `+55${digitos}`;
}

export function emailValido(email: string) {
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}

/** Etiquetas: sem repetição, sem espaço sobrando. */
export function etiquetasLimpas(lista: unknown): string[] {
  if (!Array.isArray(lista)) return [];
  const vistas = new Set<string>();
  const saida: string[] = [];
  for (const item of lista) {
    const etiqueta = String(item ?? "").trim().slice(0, 40);
    const chave = etiqueta.toLowerCase();
    if (!etiqueta || vistas.has(chave)) continue;
    vistas.add(chave);
    saida.push(etiqueta);
  }
  return saida.slice(0, 30);
}

/**
 * As marcas de um cadastro que denunciam repetição: documento, e-mail e o fim
 * do celular. Duas linhas com a mesma marca são, quase sempre, a mesma pessoa.
 */
export function marcasDoCliente(cliente: { document?: string | null; email?: string | null; whatsapp?: string | null; phone?: string | null }) {
  const marcas: Array<{ tipo: "documento" | "email" | "telefone"; valor: string }> = [];
  const documento = soDigitos(cliente.document);
  if (documento.length === 11 || documento.length === 14) marcas.push({ tipo: "documento", valor: `d:${documento}` });
  const email = String(cliente.email ?? "").trim().toLowerCase();
  if (email) marcas.push({ tipo: "email", valor: `e:${email}` });
  for (const numero of [cliente.whatsapp, cliente.phone]) {
    const fim = soDigitos(numero).slice(-8);
    if (fim.length === 8) marcas.push({ tipo: "telefone", valor: `t:${fim}` });
  }
  return marcas;
}

/* --------------------------------------------------------------- planilhas */

/**
 * Uma célula de CSV. Texto que começa com = + - @ vira fórmula quando a
 * planilha abre; ganha um apóstrofo na frente para ficar texto.
 */
export function celulaCsv(valor: unknown) {
  if (valor === null || valor === undefined) return "";
  let texto = typeof valor === "number" ? String(valor).replace(".", ",") : String(valor);
  if (typeof valor === "string" && /^[=+\-@\t\r]/.test(texto)) texto = `'${texto}`;
  return /[";\n\r]/.test(texto) ? `"${texto.replace(/"/g, '""')}"` : texto;
}

/** CSV com ponto e vírgula e a marca do UTF-8: abre certo no Excel brasileiro. */
export function montarCsv(cabecalho: string[], linhas: unknown[][]) {
  const corpo = [cabecalho, ...linhas].map((linha) => linha.map(celulaCsv).join(";")).join("\r\n");
  return `﻿${corpo}\r\n`;
}

/** O mesmo cuidado com fórmula, para a planilha do Excel. */
export function celulaDePlanilha(valor: unknown) {
  if (typeof valor === "string" && /^[=+\-@\t\r]/.test(valor)) return `'${valor}`;
  return valor ?? "";
}
