// app.jsx — root: routing, shared state, modals, tweaks
const {
  useState: useStateA,
  useEffect: useEffectA,
  useCallback: useCallbackA,
  useMemo: useMemoA,
} = React;

const SPECTRUM = "linear-gradient(95deg, #ff5d8f 0%, #ff8a3d 20%, #ffd23d 38%, #46e0a0 56%, #3db8ff 74%, #9b6bff 100%)";
const VIBES = {
  spectrum: { name: "Multicolor", c: ["#3d7bff", "#7c5cff"], grad: "linear-gradient(95deg,#ff5d8f 0 20%,#ff8a3d 20% 40%,#ffce3d 40% 56%,#46e0a0 56% 72%,#2fd4ff 72% 86%,#9b6bff 86% 100%)", multi: true },
  electric: { name: "Electric",   c: ["#39b6ff", "#8b7bff"] },
  sunset:   { name: "Sunset",     c: ["#ff7a45", "#ff4d8d"] },
  tropic:   { name: "Tropic",     c: ["#ffb33d", "#2fe0a0"] },
  lagoon:   { name: "Lagoon",     c: ["#1fd1ff", "#4d7bff"] },
  aurora:   { name: "Aurora",     c: ["#b15cff", "#ff6bd5"] },
};
const VIBE_KEYS = Object.keys(VIBES);

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "vibe": "electric",
  "globePalette": "accent",
  "light": false,
  "spinSpeed": 5,
  "carouselSpeed": 1.2
}/*EDITMODE-END*/;

// Mirrors Store state into React so components re-render on any store change.
function useStore() {
  const [user, setUser]   = useStateA(Store.getAuth());
  const [esims, setEsims] = useStateA(Store.getEsims());
  useEffectA(() => {
    const sync = () => {
      setUser(Store.getAuth());
      setEsims(Store.getEsims());
    };
    window.addEventListener("journey-store", sync);
    return () => window.removeEventListener("journey-store", sync);
  }, []);
  return { user, esims };
}

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const { user, esims } = useStore();

  // Plans are fetched async; re-render when they arrive.
  const [plans, setPlans] = useStateA({ all: [], country: [], bundles: [], meta: PLAN_CATALOG_META, filters: PLAN_FILTERS });
  useEffectA(() => {
    const sync = () => setPlans({
      all:     [...ESIM_PLANS, ...ESIM_BUNDLES],
      country: ESIM_PLANS,
      bundles: ESIM_BUNDLES,
      meta:    PLAN_CATALOG_META,
      filters: PLAN_FILTERS,
    });
    window.addEventListener("journey-plans-loaded", sync);
    sync();
    return () => window.removeEventListener("journey-plans-loaded", sync);
  }, []);

  // Globe plans — real prices merged with coordinate + photo data.
  // Each entry uses the real plan's id so purchases work, plus a photoId for the image lookup.
  const GLOBE_ANCHORS = [
    { country: "Japan",          photoId: "jp",     lat:  35.7,  lng:  139.7, popular: true  },
    { country: "United States",  photoId: "us",     lat:  38.9,  lng:  -77.0, popular: true  },
    { country: "Brazil",         photoId: "br",     lat: -22.9,  lng:  -43.2,  popular: true  },
    { country: "Argentina",      photoId: "ar",     lat: -34.6,  lng:  -58.4,  popular: false },
    { country: "United Kingdom", photoId: "uk",     lat:  51.5,  lng:   -0.1, popular: true  },
    { country: "France",         photoId: "fr",     lat:  48.85, lng:    2.35, popular: false },
    { country: "Germany",        photoId: "de",     lat:  52.5,  lng:   13.4,  popular: false },
    { country: "Italy",          photoId: "it",     lat:  41.9,  lng:   12.5,  popular: false },
    { country: "Spain",          photoId: "es",     lat:  40.4,  lng:   -3.7,  popular: false },
    { country: "Thailand",       photoId: "th",     lat:  13.7,  lng:  100.5,  popular: true  },
    { country: "Singapore",      photoId: "sg",     lat:   1.35, lng:  103.8,  popular: false },
    { country: "South Korea",    photoId: "kr",     lat:  37.5,  lng:  127.0,  popular: true  },
    { country: "Australia",      photoId: "au",     lat: -33.8,  lng:  151.2,  popular: false },
    { country: "South Africa",   photoId: "za",     lat: -33.9,  lng:   18.4,  popular: true  },
    { country: "Kenya",          photoId: "ke",     lat:  -1.3,  lng:   36.8,  popular: false },
    { country: "Morocco",        photoId: "ma",     lat:  31.6,  lng:   -7.9,  popular: false },
  ];

  const globePlans = useMemoA(() => {
    if (!plans.country.length) return FEATURED_PLANS;
    return GLOBE_ANCHORS.map(anchor => {
      const countryPlans = plans.country.filter(p => p.country === anchor.country);
      if (!countryPlans.length) return FEATURED_PLANS.find(p => p.country === anchor.country) || null;
      // Pick the most bookable plan: >=1 GB, >=7 days, lowest price
      const candidates = countryPlans.filter(p => (p.dataMb || 0) >= 1024 && p.days >= 7);
      const best = (candidates.length ? candidates : countryPlans).sort((a, b) => a.price - b.price)[0];
      return {
        ...best,
        photoId:  anchor.photoId,
        lat:      anchor.lat,
        lng:      anchor.lng,
        popular:  anchor.popular,
        flag:     FEATURED_PLANS.find(p => p.country === anchor.country)?.flag || best.flag || "🌐",
      };
    }).filter(Boolean);
  }, [plans.country]);

  // Routing
  const [route, setRoute] = useStateA(() => {
    const p = new URLSearchParams(location.search);
    if (p.has("auth_ok") && localStorage.getItem("pendingPlanId")) return "redirecting";
    return location.hash.replace("#", "") || "home";
  });
  const go = useCallbackA((r) => {
    setRoute(r);
    location.hash = r;
    window.scrollTo({ top: 0 });
  }, []);
  useEffectA(() => {
    const f = () => setRoute(location.hash.replace("#", "") || "home");
    window.addEventListener("hashchange", f);
    return () => window.removeEventListener("hashchange", f);
  }, []);

  const isMarketplaceRoute = route === "marketplace" || route.startsWith("marketplace/");

  // Load plans on boot so the globe has real prices immediately.
  useEffectA(() => { loadPlans(); }, []);

  // Save ?ref= referral code from URL to localStorage
  useEffectA(() => {
    const p = new URLSearchParams(location.search);
    const ref = p.get("ref");
    if (ref) {
      localStorage.setItem("pendingReferralCode", ref.trim().toUpperCase());
      history.replaceState(null, "", location.pathname + location.hash);
    }
  }, []);

  // After Google OAuth redirect, the server adds ?auth_ok=1; clear it.
  // After Stripe Checkout, the server adds ?checkout_success=SESSION_ID.
  useEffectA(() => {
    const p = new URLSearchParams(location.search);

    if (p.has("auth_ok")) {
      history.replaceState(null, "", location.pathname + location.hash);
      Store.fetchMe().then(async user => {
        if (!user) return;
        await Store.fetchEsims();
        const pendingPlanId = localStorage.getItem("pendingPlanId");
        if (pendingPlanId) {
          localStorage.removeItem("pendingPlanId");
          try {
            const { url } = await API.post("/api/create-checkout-session", { planId: pendingPlanId });
            window.location.href = url;
            return;
          } catch (_) {}
        }
        setTimeout(() => go("myesims"), 200);
      });
    }

    if (p.has("checkout_success")) {
      const sessionId = p.get("checkout_success");
      history.replaceState(null, "", location.pathname + location.hash);
      Store.fetchMe().then(async user => {
        if (!user) return;
        try {
          const res = await API.post("/api/fulfill-checkout", { sessionId });
          if (res.esim) {
            await Store.fetchEsims();
            go("myesims");
            if (res.esim.status === "provisioning_pending") {
              toast("Payment received. Your eSIM is pending provider provisioning.");
            } else if (res.esim.status === "provisioning_failed") {
              toast("Payment received, but provider provisioning needs attention.");
            } else {
              toast("Your eSIM is ready in My eSIMs");
            }
          }
        } catch (e) {
          console.error("[journey] Checkout fulfillment failed:", e);
          toast(e.message || "Checkout fulfillment failed");
        }
      });
    }
  }, []);

  // On boot, resolve the session from the server once.
  useEffectA(() => {
    Store.fetchMe().then(u => { if (u) Store.fetchEsims(); });
  }, []);

  // Modals
  const [login, setLogin] = useStateA(false);
  const [sheet, setSheet] = useStateA(null);
  const [freeConfirm, setFreeConfirm] = useStateA(null); // { plan, opts } pending free checkout confirm
  const [toasts, setToasts] = useStateA([]);

  // Theme
  const vibeDef = VIBES[t.vibe] || VIBES.spectrum;
  const vibe    = vibeDef.c;
  const accent  = vibe[0];
  useEffectA(() => {
    const r = document.documentElement.style;
    r.setProperty("--accent",   vibe[0]);
    r.setProperty("--accent-2", vibe[1]);
    r.setProperty("--grad", vibeDef.grad || `linear-gradient(110deg, ${vibe[0]}, ${vibe[1]})`);
    document.body.classList.toggle("light",    !!t.light);
    document.body.classList.toggle("vibrant",  t.vibe !== "spectrum" && t.vibe !== "electric");
    document.body.classList.toggle("spectrum", !!vibeDef.multi);
  }, [t.vibe, t.light]);

  const toast = useCallbackA((msg) => {
    const id = Math.random().toString(36).slice(2);
    setToasts(ts => [...ts, { id, msg }]);
    setTimeout(() => setToasts(ts => ts.filter(x => x.id !== id)), 2800);
  }, []);

  const buy = useCallbackA(async (plan, opts = {}) => {
    if (!Store.getAuth()) {
      localStorage.setItem("pendingPlanId", plan.id);
      setSheet(null);
      setLogin(true);
      return;
    }
    if (opts.isFree) {
      setSheet(null);
      setFreeConfirm({ plan, opts });
      return;
    }
    try {
      const res = await API.post("/api/create-checkout-session", {
        planId: plan.id,
        applyCredit: opts.applyCredit || false,
        referralCode: opts.referralCode || null,
      });
      if (res.free) {
        setSheet(null);
        setFreeConfirm({ plan, opts: { ...opts, ...res } });
        return;
      }
      window.location.href = res.url;
    } catch (e) {
      toast(e.message?.includes("Stripe is not configured")
        ? "Payments not configured yet — add Stripe keys to .env"
        : "Could not start checkout — please try again");
    }
  }, [toast]);

  const activate = useCallbackA(async (uid) => {
    try {
      const esim = await Store.activateEsim(uid);
      if (esim) toast(`${esim.flag} ${esim.country} eSIM is now active`);
      return esim;
    } catch (e) {
      toast(e.message || "Activation failed — please try again");
      return null;
    }
  }, [toast]);

  const refreshUsage = useCallbackA(async (uid) => {
    try {
      const esim = await Store.refreshEsimUsage(uid);
      if (esim) toast(esim.usageMessage || (esim.status === "provisioning_pending" ? "Provider status refreshed" : "Usage updated"));
      return esim;
    } catch (e) {
      toast(e.message || "Could not refresh usage");
      return null;
    }
  }, [toast]);

  const logout = useCallbackA(async () => {
    await Store.logout();
    toast("Signed out");
    go("home");
  }, [go, toast]);

  return (
    <>
      <div className="app-bg"></div>
      <div className="grain"></div>
      <Nav route={route} go={go} user={user}
        onLogin={() => setLogin(true)}
        onLogout={logout} />

      {route === "redirecting" && (
        <div style={{ display: "flex", alignItems: "center", justifyContent: "center", height: "80vh", flexDirection: "column", gap: "16px", color: "var(--muted)" }}>
          <div style={{ width: 32, height: 32, border: "3px solid var(--accent)", borderTopColor: "transparent", borderRadius: "50%", animation: "spin 0.7s linear infinite" }} />
          Completing checkout…
        </div>
      )}
      {route === "home" && (
        <Landing t={t} accent={accent} go={go} user={user}
          plans={globePlans}
          onSelectPlan={setSheet}
          onLogin={() => setLogin(true)} />
      )}
      {isMarketplaceRoute && (
        <Marketplace plans={plans.all} meta={plans.meta} filters={plans.filters} route={route} go={go} onSelectPlan={setSheet} />
      )}
      {route === "myesims" && (
        <MyEsims user={user} esims={esims} onActivate={activate}
          onRefreshUsage={refreshUsage}
          go={go} onLogin={() => setLogin(true)} toast={toast} />
      )}
      {route === "referrals" && (
        <Referrals user={user} go={go} toast={toast} />
      )}

      {sheet && (
        <PlanSheet plan={sheet} user={user}
          onClose={() => setSheet(null)} onBuy={buy} />
      )}

      {freeConfirm && (
        <FreeConfirmModal
          plan={freeConfirm.plan}
          opts={freeConfirm.opts}
          onClose={() => setFreeConfirm(null)}
          onConfirm={async () => {
            const { plan: p, opts: o } = freeConfirm;
            setFreeConfirm(null);
            try {
              const res = await API.post("/api/free-checkout", {
                planId: p.id,
                creditUsedCents: o.creditUsedCents || 0,
                discountCents: o.discountCents || 0,
                referralCode: o.referralCode || null,
              });
              if (res.esim) {
                await Store.fetchMe();
                await Store.fetchEsims();
                go("myesims");
                localStorage.removeItem("pendingReferralCode");
                toast(res.esim.status === "provisioning_pending"
                  ? "Your eSIM is being provisioned — check My eSIMs"
                  : "Your free eSIM is ready in My eSIMs!");
              }
            } catch (e) {
              toast(e.message || "Free checkout failed — please try again");
            }
          }}
        />
      )}
      {login && (
        <LoginModal onClose={() => setLogin(false)} />
      )}
      <ToastHost toasts={toasts} />

      <TweaksPanel>
        <TweakSection label="Accent vibe" />
        <div className="tw-vibes">
          {VIBE_KEYS.map(k => (
            <button key={k} type="button"
              className={"tw-vibe" + (t.vibe === k ? " on" : "")}
              onClick={() => setTweak("vibe", k)}>
              <span className="tw-vibe-sw"
                style={{ background: VIBES[k].grad || `linear-gradient(135deg, ${VIBES[k].c[0]}, ${VIBES[k].c[1]})` }}></span>
              {VIBES[k].name}
            </button>
          ))}
        </div>
        <TweakToggle label="Light mode" value={t.light} onChange={v => setTweak("light", v)} />
        <TweakSection label="Globe" />
        <TweakSelect label="Coloring" value={t.globePalette} options={["accent", "terra", "daylight", "relief", "emerald"]} onChange={v => setTweak("globePalette", v)} />
        <TweakSlider label="Spin speed" value={t.spinSpeed} min={0} max={10} step={1} onChange={v => setTweak("spinSpeed", v)} />
        <TweakSection label="Carousel" />
        <TweakSlider label="Scroll speed" value={t.carouselSpeed} min={0.4} max={3} step={0.1} unit="×" onChange={v => setTweak("carouselSpeed", v)} />
      </TweaksPanel>
    </>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
