/* Contas puras do financeiro do assistente: sem banco, para poder testar. */

export type FormaDePagamento = "pix" | "cash" | "credit_card" | "debit_card" | "transfer";

/* Como a pessoa fala → como o banco guarda. */
export const FORMAS = ["pix", "dinheiro", "cartao_de_credito", "cartao_de_debito", "transferencia"] as const;
export type FormaFalada = (typeof FORMAS)[number];

const FORMA_NO_BANCO: Record<FormaFalada, FormaDePagamento> = {
  pix: "pix",
  dinheiro: "cash",
  cartao_de_credito: "credit_card",
  cartao_de_debito: "debit_card",
  transferencia: "transfer",
};

const NOME_DA_FORMA: Record<string, string> = {
  pix: "Pix",
  cash: "dinheiro",
  credit_card: "cartão de crédito",
  debit_card: "cartão de débito",
  transfer: "transferência",
  boleto: "boleto",
  manual: "lançamento manual",
  sem_forma: "sem forma informada",
};

export function formaNoBanco(forma: unknown): FormaDePagamento | null {
  return typeof forma === "string" && forma in FORMA_NO_BANCO ? FORMA_NO_BANCO[forma as FormaFalada] : null;
}

export function nomeDaForma(metodo: unknown): string {
  if (typeof metodo !== "string" || !metodo) return "sem forma informada";
  return NOME_DA_FORMA[metodo] ?? metodo;
}

/** Sem acento, sem maiúscula, sem espaço sobrando: para comparar nomes. */
export function normalizarNome(valor: unknown): string {
  return String(valor ?? "")
    .normalize("NFD")
    .replace(/[̀-ͯ]/g, "")
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, " ")
    .trim();
}

const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
export function ehId(valor: unknown): valor is string {
  return typeof valor === "string" && UUID.test(valor.trim());
}

export type Escolha<T> = { tipo: "achou"; item: T } | { tipo: "nenhum" } | { tipo: "varios"; itens: T[] };

/**
 * Acha um item pelo id ou pelo nome que a pessoa disse ("conta do Itaú",
 * "caixa"). Nome igual ganha; senão, um único nome que contém o que foi dito.
 * Mais de um candidato não é chute: volta a lista para a pessoa escolher.
 */
export function escolherPorNome<T extends { id: string }>(itens: T[], valor: unknown, nomesDe: (item: T) => Array<string | null | undefined>): Escolha<T> {
  if (ehId(valor)) {
    const item = itens.find((candidato) => candidato.id.toLowerCase() === valor.trim().toLowerCase());
    return item ? { tipo: "achou", item } : { tipo: "nenhum" };
  }
  const dito = normalizarNome(valor);
  if (!dito) return { tipo: "nenhum" };
  const nomes = (item: T) => nomesDe(item).map(normalizarNome).filter(Boolean);

  const iguais = itens.filter((item) => nomes(item).includes(dito));
  if (iguais.length === 1) return { tipo: "achou", item: iguais[0] };
  if (iguais.length > 1) return { tipo: "varios", itens: iguais };

  const parecidos = itens.filter((item) => nomes(item).some((nome) => nome.includes(dito) || dito.includes(nome)));
  if (parecidos.length === 1) return { tipo: "achou", item: parecidos[0] };
  if (parecidos.length > 1) return { tipo: "varios", itens: parecidos };
  return { tipo: "nenhum" };
}

/* ------------------------------------------------------------ situações */

/** A conta a pagar como a tela mostra: pendente com vencimento passado é vencida. */
export function situacaoDaContaAPagar(status: unknown, vencimento: unknown, hoje: string) {
  if (status === "PAID") return "paga";
  if (status === "CANCELED") return "cancelada";
  if (status === "OVERDUE" || (typeof vencimento === "string" && vencimento && vencimento < hoje)) return "vencida";
  return "em aberto";
}

export function situacaoDaContaAReceber(status: unknown, vencimento: unknown, hoje: string) {
  if (status === "PAID") return "recebida";
  if (status === "CANCELED") return "cancelada";
  const vencida = status === "OVERDUE" || (typeof vencimento === "string" && vencimento && vencimento < hoje);
  if (status === "PARTIALLY_PAID") return vencida ? "recebida em parte, vencida" : "recebida em parte";
  return vencida ? "vencida" : "em aberto";
}

const ORIGEM: Record<string, string> = {
  PEDIDO: "pagamentos de pedidos",
  RECEBER: "contas a receber",
  PAGAR: "contas a pagar",
  AJUSTE: "ajustes e transferências entre contas",
  MANUAL: "lançamentos manuais",
  UNKNOWN: "outros",
};

export function nomeDaOrigem(origem: unknown) {
  return ORIGEM[String(origem ?? "UNKNOWN")] ?? String(origem);
}

const TIPO_DE_CONTA: Record<string, string> = {
  CAIXA: "caixa físico",
  CONTA_CORRENTE: "conta corrente",
  CONTA_POUPANCA: "poupança",
  DIGITAL: "conta digital",
};

export function nomeDoTipoDeConta(tipo: unknown) {
  return TIPO_DE_CONTA[String(tipo ?? "").toUpperCase()] ?? "conta bancária";
}

/* --------------------------------------------------------------- datas */

/** "2026-10" vira o primeiro dia, o último e o dia seguinte ao último. */
export function intervaloDoMes(mes: string): { inicio: string; fim: string; depois: string } | null {
  const achado = /^(\d{4})-(\d{2})$/.exec(mes.trim());
  if (!achado) return null;
  const ano = Number(achado[1]);
  const numero = Number(achado[2]);
  if (numero < 1 || numero > 12) return null;
  const inicio = new Date(Date.UTC(ano, numero - 1, 1)).toISOString().slice(0, 10);
  const fim = new Date(Date.UTC(ano, numero, 0)).toISOString().slice(0, 10);
  const depois = new Date(Date.UTC(ano, numero, 1)).toISOString().slice(0, 10);
  return { inicio, fim, depois };
}

export function mesAnterior(mes: string) {
  const [ano, numero] = mes.split("-").map(Number);
  const data = new Date(Date.UTC(ano, numero - 2, 1));
  return data.toISOString().slice(0, 7);
}

const MESES = ["janeiro", "fevereiro", "março", "abril", "maio", "junho", "julho", "agosto", "setembro", "outubro", "novembro", "dezembro"];
export function nomeDoMes(mes: string) {
  const [ano, numero] = mes.split("-").map(Number);
  return `${MESES[numero - 1] ?? mes} de ${ano}`;
}

/** Centavos inteiros: compara dinheiro sem o erro da vírgula flutuante. */
export function centavos(valor: number) {
  return Math.round((valor + Number.EPSILON) * 100);
}

/** Porcentagem com uma casa, para leitura. */
export function porcento(parte: number, todo: number) {
  return todo > 0 ? Math.round((parte / todo) * 1000) / 10 : 0;
}
