/**
 * Convierte lo que escribe la gente en una referencia a un documento.
 *
 *   "001-001-0000123", "1-1-123", "001 - 001 - 123"  → establecimiento, punto y número
 *   "0010010000123" (13 dígitos)                      → ídem
 *   "factura 123", "la 0000123"                       → solo número (se buscará en todos los puntos)
 *   CDC de 44 dígitos, con o sin espacios             → cdc
 *
 * Las fechas no se confunden con números: "15/10/2026" se ignora y
 * "15-10-2026" no se toma como 015-010-0002026.
 * Devuelve null si no hay una referencia clara; la máquina de estados pregunta.
 */
export type ReferenciaDocumento =
  | { clase: 'cdc'; cdc: string }
  | { clase: 'numero'; numero: string; establecimiento?: string; puntoExpedicion?: string };

const pad = (v: string, n: number) => v.replace(/^0+(?=\d)/, '').padStart(n, '0');

function pareceFecha(a: string, b: string, c: string): boolean {
  const dia = Number(a);
  const mes = Number(b);
  const anio = Number(c);
  return c.length === 4 && anio >= 1990 && anio <= 2100 && dia >= 1 && dia <= 31 && mes >= 1 && mes <= 12;
}

export function normalizarReferencia(texto: string | null | undefined): ReferenciaDocumento | null {
  if (!texto) return null;

  // CDC: 44 dígitos, a veces pegado en grupos de 4 separados por espacios.
  const compacto = texto.replace(/[\s.]/g, '');
  const cdc = compacto.match(/(?<!\d)\d{44}(?!\d)/);
  if (cdc) return { clase: 'cdc', cdc: cdc[0] };

  // Sacar fechas con barra y RUC con dígito verificador ("80144315-5") antes de buscar
  // números: del RUC quedaría el "5" como si fuera un número de documento.
  const sinFechas = texto
    .replace(/\b\d{1,2}\/\d{1,2}(\/\d{2,4})?\b/g, ' ')
    .replace(/(?<!\d)\d{5,8}\s*-\s*\d(?!\d)/g, ' ');

  const completo = sinFechas.match(/(?<!\d)(\d{1,3})\s*[-–]\s*(\d{1,3})\s*[-–]\s*(\d{1,7})(?!\d)/);
  if (completo) {
    const [, e, p, n] = completo;
    if (!pareceFecha(e, p, n) && Number(n) > 0) {
      return { clase: 'numero', establecimiento: pad(e, 3), puntoExpedicion: pad(p, 3), numero: pad(n, 7) };
    }
  }

  const trece = sinFechas.match(/(?<!\d)(\d{3})(\d{3})(\d{7})(?!\d)/);
  if (trece && Number(trece[3]) > 0) {
    return { clase: 'numero', establecimiento: trece[1], puntoExpedicion: trece[2], numero: trece[3] };
  }

  // Un único número suelto (1 a 7 dígitos). Si hay varios, es ambiguo.
  const sueltos = (
    sinFechas.replace(/(?<!\d)\d{1,2}[-–]\d{1,2}[-–]\d{4}(?!\d)/g, ' ').match(/(?<!\d)\d+(?!\d)/g) ?? ([] as string[])
  ).filter((n) => n.length <= 7 && Number(n) > 0);
  if (sueltos.length === 1) return { clase: 'numero', numero: pad(sueltos[0], 7) };

  return null;
}

export function formatearReferencia(ref: ReferenciaDocumento): string {
  if (ref.clase === 'cdc') return ref.cdc;
  return ref.establecimiento && ref.puntoExpedicion
    ? `${ref.establecimiento}-${ref.puntoExpedicion}-${ref.numero}`
    : ref.numero;
}

/**
 * Lectura tolerante del campo `referencia` que devuelve el extractor: ya se sabe
 * que es una referencia, así que si viene con palabras ("punto 002, número 593")
 * se toma el número que sigue a "número"/"nro", o el último número de hasta 7
 * dígitos. Para texto libre del cliente se usa normalizarReferencia, que es
 * estricta ("la 123 y la 456" sigue siendo ambiguo).
 */
export function normalizarReferenciaExtraida(ref: string | null | undefined): ReferenciaDocumento | null {
  const estricta = normalizarReferencia(ref);
  if (estricta || !ref) return estricta;
  const sinRuc = ref.replace(/(?<!\d)\d{5,8}\s*-\s*\d(?!\d)/g, ' ');
  const tras = sinRuc.match(/(?:n[uú]mero|nro\.?|n[°º])\s*:?\s*(\d{1,7})(?!\d)/i);
  const numeros = sinRuc.match(/(?<!\d)\d{1,7}(?!\d)/g) ?? [];
  const n = tras?.[1] ?? numeros.at(-1);
  return n && Number(n) > 0 ? { clase: 'numero', numero: n.replace(/^0+(?=\d)/, '').padStart(7, '0') } : null;
}
