/**
 * IBAN validation (ISO 13616): character set, per-country length, MOD 97-10
 * check digits. Pure, dependency-free — used by the public signing/SEPA portal
 * (client) and re-checked by the server routes (import via ~~/app/utils/iban).
 */

/** IBAN length per country (ISO 13616 registry, SEPA + common non-SEPA). */
export const IBAN_LENGTHS: Record<string, number> = {
  AD: 24, AE: 23, AL: 28, AT: 20, AZ: 28, BA: 20, BE: 16, BG: 22, BH: 22, BR: 29, BY: 28,
  CH: 21, CR: 22, CY: 28, CZ: 24, DE: 22, DK: 18, DO: 28, EE: 20, EG: 29, ES: 24, FI: 18,
  FO: 18, FR: 27, GB: 22, GE: 22, GI: 23, GL: 18, GR: 27, GT: 28, HR: 21, HU: 28, IE: 22,
  IL: 23, IQ: 23, IS: 26, IT: 27, JO: 30, KW: 30, KZ: 20, LB: 28, LC: 32, LI: 21, LT: 20,
  LU: 20, LV: 21, LY: 25, MC: 27, MD: 24, ME: 22, MK: 19, MR: 27, MT: 31, MU: 30, NL: 18,
  NO: 15, PK: 24, PL: 28, PS: 29, PT: 25, QA: 29, RO: 24, RS: 22, SA: 24, SC: 31, SD: 18,
  SE: 24, SI: 19, SK: 24, SM: 27, ST: 25, SV: 28, TL: 23, TN: 24, TR: 26, UA: 29, VA: 22,
  VG: 24, XK: 20
}

/** SEPA scheme countries (EU/EEA + CH, GB, MC, SM, VA, AD, …). */
export const SEPA_COUNTRIES = new Set([
  'AD', 'AT', 'BE', 'BG', 'CH', 'CY', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GB', 'GI', 'GR', 'HR',
  'HU', 'IE', 'IS', 'IT', 'LI', 'LT', 'LU', 'LV', 'MC', 'MT', 'NL', 'NO', 'PL', 'PT', 'RO', 'SE', 'SI',
  'SK', 'SM', 'VA'
])

export type IbanReason = 'empty' | 'chars' | 'country' | 'length' | 'checksum' | 'ok'

export interface IbanResult {
  valid: boolean
  /** Upper-case, no spaces. */
  normalized: string
  /** Groups of four for display. */
  formatted: string
  country: string
  sepa: boolean
  reason: IbanReason
  expectedLength: number | null
}

export const normalizeIban = (raw: any): string => String(raw ?? '').replace(/\s+/g, '').toUpperCase()

export const formatIbanDisplay = (raw: any): string => normalizeIban(raw).replace(/(.{4})/g, '$1 ').trim()

/** MOD 97-10: move the first four chars to the end, letters → 10..35, remainder must be 1. */
const mod97 = (iban: string): number => {
  const rearranged = iban.slice(4) + iban.slice(0, 4)
  let remainder = 0
  for (const ch of rearranged) {
    const v = ch >= 'A' && ch <= 'Z' ? String(ch.charCodeAt(0) - 55) : ch
    for (const d of v) remainder = (remainder * 10 + Number(d)) % 97
  }
  return remainder
}

export const validateIban = (raw: any): IbanResult => {
  const normalized = normalizeIban(raw)
  const country = normalized.slice(0, 2)
  const base: Omit<IbanResult, 'valid' | 'reason'> = {
    normalized,
    formatted: formatIbanDisplay(normalized),
    country,
    sepa: SEPA_COUNTRIES.has(country),
    expectedLength: IBAN_LENGTHS[country] ?? null
  }
  if (!normalized) return { ...base, valid: false, reason: 'empty' }
  if (!/^[A-Z]{2}\d{2}[A-Z0-9]+$/.test(normalized)) return { ...base, valid: false, reason: 'chars' }
  if (!IBAN_LENGTHS[country]) return { ...base, valid: false, reason: 'country' }
  if (normalized.length !== IBAN_LENGTHS[country]) return { ...base, valid: false, reason: 'length' }
  if (mod97(normalized) !== 1) return { ...base, valid: false, reason: 'checksum' }
  return { ...base, valid: true, reason: 'ok' }
}

/** Human message for a validation reason (DE/EN). */
export const ibanReasonText = (r: IbanResult, lang: 'de' | 'en' = 'de'): string => {
  const de = lang === 'de'
  switch (r.reason) {
    case 'ok': return ''
    case 'empty': return de ? 'Bitte IBAN eingeben.' : 'Please enter the IBAN.'
    case 'chars': return de ? 'IBAN darf nur Buchstaben und Ziffern enthalten und muss mit dem Länderkürzel beginnen (z. B. DE…).' : 'The IBAN may only contain letters and digits and must start with the country code (e.g. DE…).'
    case 'country': return de ? `Unbekanntes Länderkürzel „${r.country}“.` : `Unknown country code “${r.country}”.`
    case 'length': return de ? `Eine ${r.country}-IBAN hat ${r.expectedLength} Zeichen (eingegeben: ${r.normalized.length}).` : `A ${r.country} IBAN has ${r.expectedLength} characters (entered: ${r.normalized.length}).`
    case 'checksum': return de ? 'Die Prüfziffer stimmt nicht – bitte IBAN prüfen (Zahlendreher?).' : 'The check digits do not match – please verify the IBAN (transposed digits?).'
  }
}

/** BIC/SWIFT: 8 or 11 chars (optional in SEPA since 2016 — validated only when given). */
export const validateBic = (raw: any): boolean => {
  const v = String(raw ?? '').replace(/\s+/g, '').toUpperCase()
  return !v || /^[A-Z]{6}[A-Z0-9]{2}(?:[A-Z0-9]{3})?$/.test(v)
}
