/* payments — real client-side card handling for the Saudi/GCC market.
   Tap Payments is an unavailable backend, so this is a faithful client equivalent:
   Luhn validation, card-brand + mada BIN detection, expiry/CVV checks, and
   deterministic tokenization (PAN is never stored — only brand + last4 + token). */

/* representative mada BIN6 prefixes (Saudi domestic debit network) */
const MADA_BINS = [
  "446404", "457865", "968201", "968202", "968203", "968204", "968205", "968206", "968207", "968208", "968209",
  "588845", "588848", "588850", "440647", "440795", "419593", "410685", "432328", "428671", "428672", "428673",
  "442463", "521076", "588982", "535825", "529415", "543085", "524130", "532013", "549760", "455708", "484783",
  "417633", "468540", "468541", "468542", "468543", "462220", "636120", "508160", "585265", "604906", "999999",
];

function digitsOnly(s) { return (s || "").replace(/\D+/g, ""); }

/* FNV-1a 32-bit over the full string — a collision-resistant fallback for the
   rare case window.hashStr (inventory.jsx) hasn't loaded. Never length-based. */
function _fnv1a(s) {
  let h = 2166136261;
  for (let i = 0; i < s.length; i++) h = Math.imul(h ^ s.charCodeAt(i), 16777619);
  return h >>> 0;
}

/* Luhn mod-10 checksum */
function luhnValid(num) {
  const d = digitsOnly(num);
  if (d.length < 12) return false;
  let sum = 0, alt = false;
  for (let i = d.length - 1; i >= 0; i--) {
    let n = +d[i];
    if (alt) { n *= 2; if (n > 9) n -= 9; }
    sum += n; alt = !alt;
  }
  return sum % 10 === 0;
}

/* brand detection — mada first (it co-badges Visa/MC, so BIN must win) */
function detectBrand(num) {
  const d = digitsOnly(num);
  if (!d) return "unknown";
  if (MADA_BINS.includes(d.slice(0, 6))) return "mada";
  if (/^4/.test(d)) return "visa";
  if (/^(5[1-5]|2(2[2-9]|[3-6]\d|7[01]|720))/.test(d)) return "mc";
  if (/^3[47]/.test(d)) return "amex";
  if (/^(6011|65|64[4-9]|622)/.test(d)) return "discover";
  return "unknown";
}

function brandMeta(brand) {
  const map = {
    mada: { label: "mada", cvvLen: 3, lengths: [16] },
    visa: { label: "VISA", cvvLen: 3, lengths: [16, 19] },
    mc: { label: "MC", cvvLen: 3, lengths: [16] },
    amex: { label: "AMEX", cvvLen: 4, lengths: [15] },
    discover: { label: "Discover", cvvLen: 3, lengths: [16] },
    unknown: { label: "", cvvLen: 3, lengths: [16, 19] },
  };
  return map[brand] || map.unknown;
}

/* group digits for display (4-4-4-4, amex 4-6-5) */
function formatCardNumber(raw) {
  const d = digitsOnly(raw).slice(0, 19);
  const amex = detectBrand(d) === "amex";
  const groups = amex ? [4, 6, 5] : [4, 4, 4, 4, 3];
  const out = []; let idx = 0;
  for (const g of groups) { if (idx >= d.length) break; out.push(d.slice(idx, idx + g)); idx += g; }
  return out.join(" ");
}

/* expiry MM/YY (or MM/YYYY) — must be a real month and not in the past */
function validateExpiry(exp) {
  const m = (exp || "").match(/^(\d{1,2})\s*\/\s*(\d{2}|\d{4})$/);
  if (!m) return false;
  const mm = +m[1]; let yy = +m[2];
  if (mm < 1 || mm > 12) return false;
  if (yy < 100) yy += 2000;
  const now = new Date();
  if (yy > now.getFullYear() + 20) return false; // reject unrealistic far-future years
  const expEnd = new Date(yy, mm, 0, 23, 59, 59); // last day of expiry month
  return expEnd >= new Date(now.getFullYear(), now.getMonth(), now.getDate());
}
function formatExpiry(raw) {
  const d = digitsOnly(raw).slice(0, 4);
  if (d.length <= 2) return d;
  return d.slice(0, 2) + "/" + d.slice(2);
}
function validateCVV(cvv, brand) {
  const d = digitsOnly(cvv);
  return d.length === brandMeta(brand).cvvLen;
}

/* validate a full new-card payload → { ok, errors:{field:key} } */
function validateCard(card) {
  const errors = {};
  const brand = detectBrand(card.number);
  if (!luhnValid(card.number) || brand === "unknown") errors.number = "pay.err.number";
  if (!validateExpiry(card.exp)) errors.exp = "pay.err.exp";
  if (!validateCVV(card.cvv, brand)) errors.cvv = "pay.err.cvv";
  if (!(card.name || "").trim() || (card.name || "").trim().length < 2) errors.name = "pay.err.name";
  return { ok: Object.keys(errors).length === 0, errors, brand };
}

/* deterministic, irreversible token — never store the PAN */
function tokenize(card) {
  const d = digitsOnly(card.number);
  const brand = detectBrand(d);
  const seed = d + "|" + (card.name || "") + "|" + (card.exp || "");
  const h = window.hashStr ? window.hashStr(seed) : _fnv1a(seed);
  const ref = h.toString(36).padStart(7, "0").slice(0, 7);
  const last4 = d.slice(-4);
  return {
    token: "tok_" + brand + "_" + ref + last4,
    brand, last4, exp: card.exp,
    name: (card.name || "").trim(),
  };
}

/* a real itinerary/payment reference */
function paymentRef() {
  const seed = String(Date.now()) + ":" + (window.performance ? performance.now() : 0);
  const t = (window.hashStr ? window.hashStr(seed) : _fnv1a(seed)).toString(36).toUpperCase();
  return "PAY-" + t.slice(0, 4) + "-" + t.slice(4, 8).padEnd(4, "0");
}

/* Apple Pay availability (real capability check) */
function applePayAvailable() {
  try { return typeof window.ApplePaySession !== "undefined" && window.ApplePaySession && window.ApplePaySession.canMakePayments && window.ApplePaySession.canMakePayments(); }
  catch (e) { return false; }
}

Object.assign(window, {
  luhnValid, detectBrand, brandMeta, formatCardNumber, validateExpiry, formatExpiry,
  validateCVV, validateCard, tokenize, paymentRef, applePayAvailable, MADA_BINS,
});
