/* store — global app state: routing, search context, bookings, leads, tickets,
   saved cards, Mr. Wing conversations, admin keys. Catalog comes from data.jsx;
   live inventory from inventory.jsx. Persisted slices use localStorage. */
const StoreContext = React.createContext();

/* catalog references (defined in data.jsx, loaded first) */
const AIRPORTS = window.AIRPORTS;
const PACKAGES = window.PACKAGES;

function genItin() { return "TW" + Math.random().toString(36).slice(2, 8).toUpperCase(); }
function genRef(prefix) { return (prefix || "INQ") + "-" + Math.random().toString(36).slice(2, 7).toUpperCase(); }
function loadJSON(key, fallback) { try { const v = JSON.parse(localStorage.getItem(key) || "null"); return v == null ? fallback : v; } catch (e) { return fallback; } }

function StoreProvider({ children }) {
  const [route, setRoute] = React.useState(() => (location.hash.slice(1).split("?")[0] || "home"));
  const [params, setParams] = React.useState(() => parseHashParams());
  const [vw, setVw] = React.useState(() => (typeof window !== "undefined" ? window.innerWidth : 1280));
  React.useEffect(() => {
    const h = () => setVw(window.innerWidth);
    window.addEventListener("resize", h);
    return () => window.removeEventListener("resize", h);
  }, []);
  const isMobile = vw <= 760;
  const [wingOpen, setWingOpen] = React.useState(false);
  const [wingSeed, setWingSeed] = React.useState(null);

  // search context — every results page reads from here
  const [searchCtx, setSearchCtx] = React.useState({
    tab: "flights", from: "RUH", to: "JED", destination: "JED",
    depart: "2026-06-08", ret: "2026-06-11", travelers: 1,
    cabin: "any", tripType: "round", rooms: 1, guests: 1, packageId: "p1",
  });
  const [pendingBooking, setPendingBooking] = React.useState(null);

  // persisted slices
  const [cards, setCards] = React.useState(() => loadJSON("tw_cards", [
    { id: "c1", brand: "mada", last4: "4421", exp: "09/27", name: "Corporate · Finance", token: "tok_mada_3f2a4421", primary: true },
    { id: "c2", brand: "visa", last4: "1187", exp: "03/28", name: "GM Discretionary", token: "tok_visa_91c81187", primary: false },
  ]));
  const [bookings, setBookings] = React.useState(() => loadJSON("tw_bookings", SEED_BOOKINGS()));
  const [apiKeys, setApiKeys] = React.useState(() => mergeKeys(loadJSON("tw_keys", null)));
  const [leads, setLeads] = React.useState(() => loadJSON("tw_leads", []));
  const [tickets, setTickets] = React.useState(() => loadJSON("tw_tickets", []));
  const [conversations, setConversations] = React.useState(() => loadJSON("tw_convos", []));
  const [user, setUser] = React.useState(() => (window.twAuth ? twAuth.currentUser() : null));
  const [notice, setNotice] = React.useState(null); // transient toast: { type:"ok"|"error", key, vars? }

  React.useEffect(() => { localStorage.setItem("tw_cards", JSON.stringify(cards)); }, [cards]);
  React.useEffect(() => { localStorage.setItem("tw_bookings", JSON.stringify(bookings)); }, [bookings]);
  React.useEffect(() => { localStorage.setItem("tw_keys", JSON.stringify(apiKeys)); }, [apiKeys]);
  React.useEffect(() => { localStorage.setItem("tw_leads", JSON.stringify(leads)); }, [leads]);
  React.useEffect(() => { localStorage.setItem("tw_tickets", JSON.stringify(tickets)); }, [tickets]);
  React.useEffect(() => { localStorage.setItem("tw_convos", JSON.stringify(conversations)); }, [conversations]);

  // routing (hash + optional ?params)
  const nav = React.useCallback((to, p = {}) => {
    setParams(p);
    setRoute(to);
    const qs = Object.keys(p).length ? "?" + new URLSearchParams(p).toString() : "";
    const target = to + qs;
    if (location.hash.slice(1) !== target) location.hash = target;
    window.scrollTo({ top: 0, behavior: "instant" in window ? "instant" : "auto" });
  }, []);

  React.useEffect(() => {
    const onHash = () => { setRoute(location.hash.slice(1).split("?")[0] || "home"); setParams(parseHashParams()); };
    window.addEventListener("hashchange", onHash);
    return () => window.removeEventListener("hashchange", onHash);
  }, []);

  // booking lifecycle. Persist to Supabase (server-side) on top of localStorage;
  // a ref gives the []-dep callbacks the latest list. All server calls are
  // best-effort fire-and-forget — failures fall back to local-only silently.
  const bookingsRef = React.useRef(bookings);
  React.useEffect(() => { bookingsRef.current = bookings; }, [bookings]);
  const _acct = () => { try { const u = window.twAuth && twAuth.currentUser(); return u ? u.id : undefined; } catch (e) { return undefined; } };
  const _adminTok = () => { try { return sessionStorage.getItem("tw_admin") || undefined; } catch (e) { return undefined; } };

  const addBooking = React.useCallback((b) => {
    const rec = { id: genItin(), createdAt: Date.now(), status: "Confirmed", ...b };
    setBookings(prev => [rec, ...prev]);
    if (window.apiBookingCreate) { try { window.apiBookingCreate({ ...rec, accountId: _acct() }); } catch (e) {} }
    return rec;
  }, []);
  const cancelBooking = React.useCallback((id) => {
    const cur = (bookingsRef.current || []).find(b => b.id === id);
    const refunded = cur ? Math.round(cur.amount * (cur.type === "flight" ? 0.8 : 0.9)) : 0;
    setBookings(prev => prev.map(b => b.id === id ? { ...b, status: "Cancelled", refunded } : b));
    if (window.apiBookingUpdate) { try { window.apiBookingUpdate({ id, status: "Cancelled", refunded }, _adminTok()); } catch (e) {} }
  }, []);
  const modifyBooking = React.useCallback((id, patch) => {
    setBookings(prev => prev.map(b => b.id === id ? { ...b, ...patch, modifiedAt: Date.now() } : b));
    if (window.apiBookingUpdate && (patch.status || patch.date)) { try { window.apiBookingUpdate({ id, status: patch.status, date: patch.date }, _adminTok()); } catch (e) {} }
  }, []);

  // Tap Payments redirect return: verify the charge server-side and finalize the booking
  React.useEffect(() => {
    const tapId = new URLSearchParams(location.search).get("tap_id");
    if (!tapId || !window.apiPayVerify) return;
    (async () => {
      let pend = null;
      try { pend = JSON.parse(localStorage.getItem("tw_pendingPay") || "null"); } catch (e) {}
      // ignore stale pending-pay context (>24h) — the charge status is authoritative
      if (pend && pend.at && Date.now() - pend.at > 24 * 60 * 60 * 1000) pend = null;
      const v = await window.apiPayVerify(tapId);
      const ok = v && (v.status === "CAPTURED" || v.status === "AUTHORIZED");
      if (ok && pend) {
        addBooking({ type: pend.b.type, title: pend.b.title, who: pend.who, amount: pend.fare.total, channel: "Web", date: pend.b.date, paidWith: (v.brand || "Card") + " ••" + (v.last4 || ""), paymentRef: v.id, travelers: pend.b.travelers, nights: pend.b.nights, fare: pend.fare });
        setNotice({ type: "ok", key: "notice.paid" });
      } else {
        // never leave the user guessing — surface a clear, recoverable error
        setNotice({ type: "error", key: "notice.payFailed" });
      }
      localStorage.removeItem("tw_pendingPay");
      try { history.replaceState(null, "", location.pathname); } catch (e) {}
      nav("trips");
    })();
    // eslint-disable-next-line
  }, []);

  // leads / tickets
  const addLead = React.useCallback((lead) => {
    const rec = { id: genRef("INQ"), createdAt: Date.now(), status: "New", ...lead };
    setLeads(prev => [rec, ...prev]);
    if (window.apiLeadCreate) { try { window.apiLeadCreate(rec); } catch (e) {} }
    return rec;
  }, []);
  const addTicket = React.useCallback((ticket) => {
    const rec = { id: genRef("MSG"), createdAt: Date.now(), status: "New", ...ticket };
    setTickets(prev => [rec, ...prev]);
    if (window.apiTicketCreate) { try { window.apiTicketCreate(rec); } catch (e) {} }
    return rec;
  }, []);

  // Mr. Wing conversation persistence (upsert by session id)
  const saveConversation = React.useCallback((sessionId, messages, meta) => {
    setConversations(prev => {
      const idx = prev.findIndex(c => c.id === sessionId);
      const rec = { id: sessionId, updatedAt: Date.now(), messages, ...meta };
      if (idx >= 0) { const copy = prev.slice(); copy[idx] = { ...prev[idx], ...rec }; return copy; }
      return [{ startedAt: Date.now(), ...rec }, ...prev].slice(0, 50);
    });
    if (window.apiConvUpsert) { try { window.apiConvUpsert({ id: sessionId, messages, accountId: _acct(), ...meta }); } catch (e) {} }
  }, []);

  // saved cards
  const addCard = React.useCallback((card) => {
    const rec = { id: "c" + Date.now().toString(36), primary: false, ...card };
    setCards(prev => [...prev, rec]);
    return rec;
  }, []);
  const removeCard = React.useCallback((id) => { setCards(prev => prev.filter(c => c.id !== id)); }, []);
  const makePrimaryCard = React.useCallback((id) => { setCards(prev => prev.map(c => ({ ...c, primary: c.id === id }))); }, []);

  // accounts
  const signup = React.useCallback(async (d) => { const r = await window.twAuth.signup(d); if (r.ok) setUser(r.user); return r; }, []);
  const login = React.useCallback(async (d) => { const r = await window.twAuth.login(d); if (r.ok) setUser(r.user); return r; }, []);
  const logout = React.useCallback(() => { window.twAuth.logout(); setUser(null); nav("home"); }, [nav]);
  const updateProfile = React.useCallback((patch) => { const cur = window.twAuth.currentUser(); if (!cur) return null; const u = window.twAuth.updateUser(cur.id, patch); setUser(u); return u; }, []);
  const changePassword = React.useCallback((oldPw, newPw) => { const cur = window.twAuth.currentUser(); if (!cur) return Promise.resolve({ error: "no_account" }); return window.twAuth.changePassword(cur.id, oldPw, newPw); }, []);
  const deleteAccount = React.useCallback(() => { const cur = window.twAuth.currentUser(); if (cur) window.twAuth.deleteUser(cur.id); setUser(null); nav("home"); }, [nav]);

  const value = {
    route, nav, params, AIRPORTS, PACKAGES, isMobile,
    wingOpen, setWingOpen, wingSeed, setWingSeed,
    searchCtx, setSearchCtx, pendingBooking, setPendingBooking,
    cards, setCards, addCard, removeCard, makePrimaryCard, bookings, setBookings, addBooking, cancelBooking, modifyBooking,
    leads, addLead, tickets, addTicket, conversations, saveConversation,
    user, signup, login, logout, updateProfile, changePassword, deleteAccount,
    notice, setNotice,
    apiKeys, setApiKeys,
    airportByCode: (c) => (AIRPORTS || []).find(a => a.code === c) || { code: c, city: c, ar: c, name: "" },
    nights: window.nightsBetween ? window.nightsBetween(searchCtx.depart, searchCtx.ret) : 2,
    searchFlights: (ctx) => window.generateFlights ? window.generateFlights({ from: (ctx || searchCtx).from, to: (ctx || searchCtx).to, date: (ctx || searchCtx).depart }) : [],
    searchHotels: (ctx) => window.generateHotels ? window.generateHotels((ctx || searchCtx).destination || (ctx || searchCtx).to, (ctx || searchCtx).depart) : [],
  };
  return <StoreContext.Provider value={value}>{children}</StoreContext.Provider>;
}

function parseHashParams() {
  const h = location.hash.slice(1);
  const qi = h.indexOf("?");
  if (qi < 0) return {};
  const out = {};
  new URLSearchParams(h.slice(qi + 1)).forEach((v, k) => { out[k] = v; });
  return out;
}

function useStore() { return React.useContext(StoreContext); }

function SEED_BOOKINGS() {
  const now = Date.now();
  return [
    { id: "TW7F2K9A", type: "flight", title: "RUH → JED · Saudia SV1021", who: "Ministry of Health · Dr. A. Qahtani", amount: 940, status: "Confirmed", channel: "Mr. Wing", createdAt: now - 1000 * 60 * 42, date: "2026-06-08" },
    { id: "TW3M8C1B", type: "hotel", title: "Fairmont Riyadh · 2 nights", who: "Saudi Aramco · Procurement", amount: 1980, status: "Confirmed", channel: "Web", createdAt: now - 1000 * 60 * 60 * 5, date: "2026-06-09" },
    { id: "TW9QX4P2", type: "flight", title: "JED → DXB · flynas XY880", who: "Al Faisal Holding", amount: 720, status: "Confirmed", channel: "Mr. Wing", createdAt: now - 1000 * 60 * 60 * 26, date: "2026-06-12" },
    { id: "TW5RT0L7", type: "hotel", title: "The Ritz-Carlton · 3 nights", who: "Royal Court Protocol (VVIP)", amount: 5550, status: "Escalated", channel: "Egypt Hub", createdAt: now - 1000 * 60 * 60 * 49, date: "2026-06-15" },
    { id: "TW1JD6N3", type: "flight", title: "RUH → CAI · Saudia SV305", who: "King Faisal Hospital", amount: 1340, status: "Cancelled", channel: "Mr. Wing", createdAt: now - 1000 * 60 * 60 * 73, date: "2026-06-04", refunded: 1072 },
  ];
}

function DEFAULT_KEYS() {
  return [
    { id: "tap", group: "Payments", label: "Tap Payments", desc: "Mada · Visa · Mastercard · Apple Pay · tokenization", env: "Live", placeholder: "sk_live_••••••••••••", value: "", status: "disconnected", masked: "" },
    { id: "gds", group: "Flights", label: "Flight GDS — Amadeus", desc: "Global flight content & fares", env: "Live", placeholder: "AMA-API-••••", value: "", status: "disconnected", masked: "" },
    { id: "sabre", group: "Flights", label: "Sabre (failover)", desc: "Secondary flight source", env: "Test", placeholder: "sabre_••••", value: "", status: "disconnected", masked: "" },
    { id: "hotelbeds", group: "Hotels", label: "Hotelbeds Supplier API", desc: "Global hotel inventory & corporate rates", env: "Live", placeholder: "hb_••••", value: "", status: "disconnected", masked: "" },
    { id: "ai", group: "AI Model", label: "Mr. Wing — Model Provider", desc: "OpenAI-compatible provider (OpenAI · OpenRouter · Groq · Together · Anthropic · local). Set a base URL and key, connect, then pick a model.", env: "Live", placeholder: "sk-…", value: "", status: "disconnected", masked: "", model: "", baseUrl: "https://api.openai.com/v1", kind: "openai" },
  ];
}

/* merge persisted keys over defaults so new fields (model) appear for existing users */
function mergeKeys(saved) {
  const defs = DEFAULT_KEYS();
  if (!Array.isArray(saved)) return defs;
  return defs.map(d => { const s = saved.find(k => k.id === d.id); return s ? { ...d, ...s } : d; });
}

Object.assign(window, { StoreContext, StoreProvider, useStore, genItin, genRef });
