/**
 * Receipt date parser. Printed dates are DACH (day-first) by default;
 * month-first only when the receipt is clearly US or the numbers force it.
 */

export interface DateParseHint {
  currency?: string | null;
  taxId?: string | null;
  address?: string | null;
}

function isValidYmd(year: number, month: number, day: number): boolean {
  if (year < 1990 || year > 2100) return false;
  if (month < 1 || month > 12) return false;
  if (day < 1 || day > 31) return false;
  const date = new Date(Date.UTC(year, month - 1, day));
  return (
    date.getUTCFullYear() === year &&
    date.getUTCMonth() === month - 1 &&
    date.getUTCDate() === day
  );
}

function formatIso(year: number, month: number, day: number): string | null {
  if (!isValidYmd(year, month, day)) return null;
  return `${String(year).padStart(4, "0")}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
}

function expandYear(raw: string): number {
  if (raw.length === 2) {
    const yy = Number(raw);
    return 2000 + yy;
  }
  return Number(raw);
}

function preferMonthFirst(hint?: DateParseHint): boolean {
  const currency = (hint?.currency || "").trim().toUpperCase();
  if (currency === "USD") return true;
  const text = `${hint?.address || ""} ${hint?.taxId || ""}`.toLowerCase();
  if (/\bunited states\b|\busa\b|\bu\.s\.a\b/.test(text)) return true;
  return false;
}

/**
 * Convert a printed receipt date to YYYY-MM-DD, or null if it cannot be parsed.
 *
 * Rules:
 * - Already-ISO YYYY-MM-DD is kept when the calendar date is valid.
 * - Year-first (YYYY.MM.DD / YYYY/MM/DD) is always Y-M-D.
 * - Dotted DACH dates (16.08.2026) are day-first.
 * - Slashed dates: if one side is > 12 that side is the day; otherwise
 *   USD/US → month-first, everything else (EUR/CHF/DACH) → day-first.
 */
export function parseReceiptDate(raw: string, hint?: DateParseHint): string | null {
  if (typeof raw !== "string") return null;
  const trimmed = raw.trim();
  if (!trimmed) return null;

  const iso = trimmed.match(/^(\d{4})-(\d{2})-(\d{2})$/);
  if (iso) {
    return formatIso(Number(iso[1]), Number(iso[2]), Number(iso[3]));
  }

  const yearFirst = trimmed.match(/^(\d{4})[./](\d{1,2})[./](\d{1,2})$/);
  if (yearFirst) {
    return formatIso(Number(yearFirst[1]), Number(yearFirst[2]), Number(yearFirst[3]));
  }

  const dotted = trimmed.match(/^(\d{1,2})\.(\d{1,2})\.(\d{2,4})$/);
  if (dotted) {
    const a = Number(dotted[1]);
    const b = Number(dotted[2]);
    const year = expandYear(dotted[3]);
    if (a > 12 && b <= 12) return formatIso(year, b, a);
    if (b > 12 && a <= 12) return formatIso(year, a, b);
    return formatIso(year, b, a);
  }

  const slashed = trimmed.match(/^(\d{1,2})[/.-](\d{1,2})[/.-](\d{2,4})$/);
  if (slashed) {
    const a = Number(slashed[1]);
    const b = Number(slashed[2]);
    const year = expandYear(slashed[3]);
    if (a > 12 && b <= 12) return formatIso(year, b, a);
    if (b > 12 && a <= 12) return formatIso(year, a, b);
    if (preferMonthFirst(hint)) return formatIso(year, a, b);
    return formatIso(year, b, a);
  }

  return null;
}
