/* inventory — deterministic procedural flight & hotel generation.
   Seeded by a stable hash of the query so the SAME search always yields the
   SAME results (no Math.random, no setTimeout fakes). Real geography + carriers
   from data.jsx drive plausible routes, durations and pricing. */

/* ---------- deterministic PRNG ---------- */
function hashStr(s) {
  let h = 2166136261;
  for (let i = 0; i < s.length; i++) h = Math.imul(h ^ s.charCodeAt(i), 16777619);
  return h >>> 0;
}
function mulberry32(a) {
  return function () {
    a |= 0; a = (a + 0x6D2B79F5) | 0;
    let t = Math.imul(a ^ (a >>> 15), 1 | a);
    t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
  };
}

/* ---------- geo / format helpers ---------- */
const _AP_BY = {};
(AIRPORTS || []).forEach(a => { _AP_BY[a.code] = a; });
function apByCode(code) { return _AP_BY[code] || null; }
function cityName(code, lang) { const a = apByCode(code); return a ? (lang === "ar" ? a.ar : a.city) : code; }

function haversine(a, b) {
  const R = 6371, toR = Math.PI / 180;
  const dLat = (b.lat - a.lat) * toR, dLng = (b.lng - a.lng) * toR;
  const la1 = a.lat * toR, la2 = b.lat * toR;
  const h = Math.sin(dLat / 2) ** 2 + Math.cos(la1) * Math.cos(la2) * Math.sin(dLng / 2) ** 2;
  return Math.round(2 * R * Math.asin(Math.min(1, Math.sqrt(h))));
}
function fmtTime(mins) {
  const m = ((mins % 1440) + 1440) % 1440;
  const h = Math.floor(m / 60), mm = m % 60;
  return String(h).padStart(2, "0") + ":" + String(mm).padStart(2, "0");
}
function fmtDur(mins) {
  const h = Math.floor(mins / 60), m = mins % 60;
  return ((h ? h + "h " : "") + (m || !h ? m + "m" : "")).trim();
}
function shuffle(arr, rng) {
  const a = arr.slice();
  for (let i = a.length - 1; i > 0; i--) { const j = Math.floor(rng() * (i + 1)); [a[i], a[j]] = [a[j], a[i]]; }
  return a;
}
function pickN(pool, n, rng) {
  return shuffle(pool, rng).slice(0, n);
}

/* ---------- carrier eligibility for a route ---------- */
function carriersForRoute(from, to) {
  const a = apByCode(from), b = apByCode(to);
  if (!a || !b) return CARRIERS.filter(c => c.saudi);
  const cand = CARRIERS.filter(c =>
    c.hubs.includes(from) || c.hubs.includes(to) ||
    (c.serves.includes(a.region) && c.serves.includes(b.region))
  );
  ["SV", "XY"].forEach(code => {
    if (!cand.find(c => c.code === code)) { const c = CARRIERS.find(x => x.code === code); if (c) cand.push(c); }
  });
  return cand;
}

/* ---------- flights ---------- */
function generateFlights(opts) {
  opts = opts || {};
  const from = opts.from, to = opts.to, date = opts.date || "";
  if (!from || !to || from === to) return [];
  const a = apByCode(from), b = apByCode(to);
  const dist = (a && b) ? haversine(a, b) : 900;
  const rng = mulberry32(hashStr("F|" + from + "|" + to + "|" + date));
  const cand = shuffle(carriersForRoute(from, to), rng);
  const count = Math.min(cand.length, 5 + Math.floor(rng() * 4)); // 5–8
  const haul = dist > 3500 ? "long" : dist > 1500 ? "medium" : "short";
  const out = [];
  for (let i = 0; i < count; i++) {
    const c = cand[i];
    const cabin = rng() < (i < 2 ? 0.5 : 0.32) ? "Business" : "Economy";
    const depMin = 360 + Math.floor(rng() * (22 * 60 - 360)); // 06:00–22:00
    const speed = dist > 3000 ? 820 : dist > 1200 ? 760 : 680;
    const buffer = 35 + Math.floor(rng() * 25);
    let stops = 0, layover = "", layMin = 0;
    const longStop = dist > 4200 && rng() < 0.45;
    const medStop = !longStop && dist > 2200 && rng() < 0.16;
    if (longStop || medStop) {
      stops = 1;
      const hubs = LAYOVER_HUBS.filter(h => h !== from && h !== to);
      const hub = hubs[Math.floor(rng() * hubs.length)] || "DXB";
      layMin = (longStop ? 55 : 50) + Math.floor(rng() * (longStop ? 95 : 70));
      layover = hub + " " + layMin + "m";
    }
    const flightMin = Math.round(dist / speed * 60) + buffer;
    const durMin = flightMin + (stops ? layMin : 0);
    const arrMin = depMin + durMin;
    const econ = 180 + dist * 0.42;
    const cabinMult = cabin === "Business" ? 2.55 : 1.0;
    const tod = (depMin < 420 || depMin > 1230) ? 0.92 : 1.0;
    const stopDisc = stops ? 0.86 : 1.04;
    let price = econ * cabinMult * c.tier * tod * stopDisc * (0.86 + rng() * 0.4);
    price = Math.max(120, Math.round(price / 10) * 10);
    const ac = AIRCRAFT[haul][Math.floor(rng() * AIRCRAFT[haul].length)];
    const num = 100 + Math.floor(rng() * 899);
    out.push({
      id: c.code + num, airline: c.name, airlineAr: c.ar, code: c.code, color: c.color,
      from, to, dep: fmtTime(depMin), arr: fmtTime(arrMin), depMin, durMin, dur: fmtDur(durMin),
      stops, layover, price, cabin, aircraft: ac, on: 84 + Math.floor(rng() * 16),
      distance: dist, nextDay: arrMin >= 1440,
    });
  }
  return out;
}

/* ---------- hotels ---------- */
function generateHotels(cityCode, checkin) {
  cityCode = cityCode || "RUH";
  const ap = apByCode(cityCode);
  const pool = HOTEL_CITIES[cityCode];
  const cName = ap ? ap.city : cityCode;
  const cAr = ap ? ap.ar : cityCode;
  const tier = pool ? pool.tier : (ap ? ap.tier : 1.0);
  const brands = pool ? pool.brands : HOTEL_FALLBACK.brands;
  const districts = pool ? pool.districts : HOTEL_FALLBACK.districts;
  const rng = mulberry32(hashStr("H|" + cityCode + "|" + (checkin || "")));
  const target = 6 + Math.floor(rng() * 4); // 6–9
  const used = new Set();
  const list = [];
  let guard = 0;
  while (list.length < target && guard++ < 60) {
    const b = brands[Math.floor(rng() * brands.length)];
    const d = districts[Math.floor(rng() * districts.length)];
    const key = b[0] + "|" + d[0];
    if (used.has(key)) continue;
    used.add(key);
    const rating = Math.round((4.1 + rng() * 0.8) * 10) / 10;
    const reviews = 180 + Math.floor(rng() * 2480);
    const ratingFactor = 0.78 + (rating - 4.1) / 0.8 * 0.75;
    const base = 640 * tier * ratingFactor * (0.85 + rng() * 0.5);
    const price = Math.round(base / 10) * 10;
    const corp = Math.round(price * 0.79 / 10) * 10;
    const tags = pickN(HOTEL_AMENITIES, 3, rng);
    list.push({
      id: "h_" + cityCode + "_" + list.length, name: b[0] + " " + cName, ar: b[1] + " " + cAr,
      brand: b[0], area: d[0] + ", " + cName, areaAr: d[1] + "، " + cAr,
      rating, reviews, price, corp, tags, img: b[0] + " " + cName + " — " + d[0], city: cityCode,
    });
  }
  return list;
}

/* nights between two ISO dates (min 1) */
function nightsBetween(checkin, checkout) {
  const a = new Date(checkin), b = new Date(checkout);
  const n = Math.round((b - a) / 86400000);
  return Number.isFinite(n) && n > 0 ? n : 1;
}

Object.assign(window, {
  hashStr, mulberry32, haversine, apByCode, cityName, generateFlights, generateHotels,
  carriersForRoute, nightsBetween, fmtTime, fmtDur,
});
