const { useState, useEffect, useCallback, useMemo, useRef } = React;

function AppShell() {
  const [bootState, setBootState] = useState("loading"); // loading | auth | pick-site | ready | error
  const [bootError, setBootError] = useState("");
  const [user, setUser] = useState(null);
  const [sites, setSites] = useState([]);
  const [activeSiteId, setActiveSiteId] = useState(null);
  const [live, setLive] = useState(null);
  const [screen, setScreen] = useState("home");
  const [review, setReview] = useState(null);
  const [obStep, setObStep] = useState(0);
  const [obDismissed, setObDismissed] = useState(false);
  const manualObOpenRef = useRef(false);
  const navGuardRef = useRef(null);
  const [busy, setBusy] = useState(false);
  const [generatingArticleId, setGeneratingArticleId] = useState(null);
  const [mobileNavOpen, setMobileNavOpen] = useState(false);
  const [wpGateDone, setWpGateDone] = useState(false);

  const navigateScreen = useCallback((next) => {
    setScreen(next);
    setMobileNavOpen(false);
    try {
      window.scrollTo({ top: 0, left: 0, behavior: "auto" });
      document.documentElement.scrollTop = 0;
      document.body.scrollTop = 0;
    } catch {
      window.scrollTo(0, 0);
    }
  }, []);

  function goToScreen(next) {
    if (navGuardRef.current) navGuardRef.current(next);
    else navigateScreen(next);
  }

  const isWebsiteSite = live?.session?.site?.platform === "WEBSITE";
  const wordpressConnected = !!live?.session?.wordpressConnection?.connected;
  const onboardingCompleted = !!live?.config?.configuration?.onboardingCompleted;
  const showWpGate =
    bootState === "ready" &&
    isWebsiteSite &&
    !wordpressConnected &&
    !wpGateDone &&
    !onboardingCompleted;
  const showOnboarding = !showWpGate && !obDismissed && obStep > 0 && obStep <= 4;
  const showMascot = true;

  useEffect(() => {
    try {
      window.scrollTo({ top: 0, left: 0, behavior: "auto" });
      document.documentElement.scrollTop = 0;
      document.body.scrollTop = 0;
    } catch {
      window.scrollTo(0, 0);
    }
  }, [screen]);

  const refresh = useCallback(async () => {
    const bootstrap = await GuyAPI.bootstrap();
    setLive(bridgeBootstrap(bootstrap));
    return bootstrap;
  }, []);

  const init = useCallback(async () => {
    setBootState("loading");
    setBootError("");
    try {
      await GuyAPI.health();
      let session;
      try {
        session = await GuyAPI.session();
      } catch {
        setUser(null);
        setBootState("auth");
        return;
      }
      setUser(session.user);
      const { sites: list, activeSiteId: active } = await GuyAPI.listSites();
      setSites(list);
      setActiveSiteId(active);
      if (!list.length) {
        setBootState("pick-site");
        return;
      }
      if (!active) {
        if (list.length === 1) {
          await GuyAPI.selectSite(list[0].id);
          setActiveSiteId(list[0].id);
          await refresh();
          setBootState("ready");
          return;
        }
        setBootState("pick-site");
        return;
      }
      await refresh();
      setBootState("ready");
    } catch (e) {
      setBootError(e.message || "Erreur de connexion");
      setBootState("error");
    }
  }, [refresh]);

  useEffect(() => { init(); }, [init]);

  const articlesGeneratingCount =
    live?.home?.dashboard?.articlesGeneratingCount ?? 0;

  useEffect(() => {
    if (bootState !== "ready" || articlesGeneratingCount === 0) return undefined;
    const id = window.setInterval(() => {
      refresh().catch(() => null);
    }, 8000);
    return () => window.clearInterval(id);
  }, [bootState, articlesGeneratingCount, refresh]);

  useEffect(() => {
    if (bootState !== "ready") return undefined;
    if (live?.billing?.current?.status !== "pending") return undefined;

    let cancelled = false;
    async function poll() {
      try {
        await GuyAPI.syncBilling();
        if (!cancelled) await refresh();
      } catch {
        /* ignore */
      }
    }
    poll();
    const timer = window.setInterval(poll, 4000);
    return () => {
      cancelled = true;
      window.clearInterval(timer);
    };
  }, [bootState, live?.billing?.current?.status, refresh]);

  useEffect(() => {
    const params = new URLSearchParams(window.location.search);
    const authError = params.get("auth_error");
    if (!authError) return;
    const messages = {
      access_denied: "Connexion Google annulée.",
      no_shop_access: "Aucune boutique SEO GUY rattachée à ce compte Google.",
      email_unavailable: "Impossible de récupérer l'email Google.",
      invalid_state: "Session Google expirée. Réessayez.",
      missing_code_or_state: "Connexion Google incomplète. Réessayez.",
    };
    setBootError(messages[authError] || `Connexion Google échouée (${authError}).`);
    setBootState("auth");
    params.delete("auth_error");
    const qs = params.toString();
    const next = window.location.pathname + (qs ? `?${qs}` : "");
    window.history.replaceState({}, "", next);
  }, []);

  useEffect(() => {
    if (bootState !== "ready") return;
    const params = new URLSearchParams(window.location.search);
    const screenParam = params.get("screen");
    if (screenParam) setScreen(screenParam);
    if (params.get("billing") === "ok" || params.get("session_id")) {
      GuyAPI.syncBilling().then(() => refresh()).catch(() => null);
    }
  }, [bootState, refresh]);

  useEffect(() => {
    if (!live?.config?.configuration) return;
    if (manualObOpenRef.current) return;
    const cfg = live.config.configuration;
    if (cfg.onboardingCompleted) {
      setObStep(0);
      return;
    }
    const saved = typeof cfg.currentStep === "number" ? cfg.currentStep : 0;
    setObStep(Math.min(Math.max(saved + 1, 1), 4));
  }, [live?.config?.configuration]);

  const obInsights = useMemo(() => {
    const kwList = live?.keywords?.list ?? [];
    const sorted = [...kwList].sort((a, b) => (b.volume ?? 0) - (a.volume ?? 0));
    const top = sorted[0];
    const cfgTop = live?.keywords?.opportunities?.[0];
    const topKeyword = top
      ? { keyword: top.keyword, volume: top.volume, difficulty: top.difficulty }
      : cfgTop
        ? { keyword: cfgTop.keyword || cfgTop.title, volume: cfgTop.volume, difficulty: cfgTop.difficulty }
        : null;
    return {
      productCount: null,
      keywordCount: kwList.length,
      topKeyword,
    };
  }, [live]);

  useEffect(() => {
    if (!showOnboarding || obInsights.productCount != null) return;
    if (live?.session?.site?.platform === "WEBSITE") return;
    GuyAPI.shopifyProducts()
      .then((res) => {
        const count = res?.products?.length ?? 0;
        setLive((prev) => {
          if (!prev) return prev;
          return {
            ...prev,
            onboardingInsights: { ...obInsights, productCount: count },
          };
        });
      })
      .catch(() => undefined);
  }, [showOnboarding, obInsights, live?.session?.site?.platform]);

  async function selectSite(siteId) {
    setBusy(true);
    setBootError("");
    try {
      await GuyAPI.selectSite(siteId);
      setActiveSiteId(siteId);
      await refresh();
      setBootState("ready");
    } catch (e) {
      setBootError(e.message || "Sélection impossible");
    } finally {
      setBusy(false);
    }
  }

  async function handleLogin({ email, password }) {
    setBusy(true);
    setBootError("");
    try {
      const res = await GuyAPI.login(email, password);
      setUser(res.user);
      const { sites: list, activeSiteId: active } = await GuyAPI.listSites();
      setSites(list);
      setActiveSiteId(active);
      if (!list.length) {
        setBootState("pick-site");
        return;
      }
      if (!active) {
        if (list.length === 1) {
          await GuyAPI.selectSite(list[0].id);
          setActiveSiteId(list[0].id);
          await refresh();
          setBootState("ready");
          return;
        }
        setBootState("pick-site");
        return;
      }
      await refresh();
      setBootState("ready");
    } catch (e) {
      setBootError(e.message || "Connexion impossible");
    } finally {
      setBusy(false);
    }
  }

  async function handleGoogleLogin() {
    const returnTo = `${window.location.origin}${window.location.pathname}`;
    GuyAPI.googleLogin(returnTo);
  }

  async function handleDiscoverShops(email) {
    const res = await GuyAPI.discoverShops(email);
    return res.shops || [];
  }

  async function finishAuthSession(res) {
    setUser(res.user);
    const { sites: list, activeSiteId: active } = await GuyAPI.listSites();
    setSites(list);
    setActiveSiteId(active);
    if (!list.length) {
      setBootState("pick-site");
      return;
    }
    if (!active) {
      if (list.length === 1) {
        await GuyAPI.selectSite(list[0].id);
        setActiveSiteId(list[0].id);
        await refresh();
        setBootState("ready");
        return;
      }
      setBootState("pick-site");
      return;
    }
    await refresh();
    setBootState("ready");
  }

  async function handleRegister(payload) {
    setBusy(true);
    setBootError("");
    try {
      const res = await GuyAPI.register(payload);
      await finishAuthSession(res);
    } catch (e) {
      setBootError(e.message || "Inscription impossible");
    } finally {
      setBusy(false);
    }
  }

  async function handleConnectWebsite({ siteUrl, siteName }) {
    setBusy(true);
    setBootError("");
    try {
      const res = await GuyAPI.connectWebsite({ siteUrl, siteName });
      const siteId = res.site?.id;
      if (!siteId) throw new Error("Site créé mais identifiant manquant.");
      await GuyAPI.selectSite(siteId);
      setActiveSiteId(siteId);
      const { sites: list } = await GuyAPI.listSites();
      setSites(list);
      await refresh();
      setBootState("ready");
    } catch (e) {
      setBootError(e.message || "Rattachement impossible");
      throw e;
    } finally {
      setBusy(false);
    }
  }

  async function handleLinkShopify(merchantEmail) {
    setBusy(true);
    setBootError("");
    try {
      const res = await GuyAPI.linkShopifyAccount(merchantEmail);
      if (!res.linked) {
        throw new Error(
          "Aucune boutique trouvée pour cet email. Installez l'app SEO GUY sur Shopify.",
        );
      }
      const { sites: list, activeSiteId: active } = await GuyAPI.listSites();
      setSites(list);
      setActiveSiteId(active);
      if (list.length === 1 && !active) {
        await GuyAPI.selectSite(list[0].id);
        setActiveSiteId(list[0].id);
        await refresh();
        setBootState("ready");
        return;
      }
      setBootState("pick-site");
    } catch (e) {
      setBootError(e.message || "Rattachement impossible");
      throw e;
    } finally {
      setBusy(false);
    }
  }

  async function handleLogout() {
    setBusy(true);
    setBootError("");
    try {
      await GuyAPI.logout();
      setUser(null);
      setSites([]);
      setActiveSiteId(null);
      setLive(null);
      setBootState("auth");
    } catch (e) {
      setBootError(e.message || "Déconnexion impossible");
    } finally {
      setBusy(false);
    }
  }

  const queue = live?.home?.queue ?? [];
  const published = live?.home?.publishedThisMonth ?? 0;
  const articleQuota = live?.quota ?? null;
  const articlesList = live?.articles?.list ?? [];
  const savedSuggestions = live?.articles?.saved ?? [];
  const keywords = live?.keywords?.list ?? [];
  const alerts = live?.alerts ?? [];
  const kpis = live?.home?.kpis ?? mapKpisFromPerformance(live?.performance?.data);

  async function runApi(fn) {
    setBusy(true);
    try {
      await fn();
      await refresh();
    } catch (e) {
      alert(e.message || "Erreur");
    } finally {
      setBusy(false);
    }
  }

  async function publishArticle(articleId, bodyHtml) {
    if (bodyHtml?.trim()) {
      await GuyAPI.articleAction(articleId, { action: "save_draft", bodyHtml });
    }
    const res = await GuyAPI.articleAction(articleId, { action: "publish" });
    if (res?.wordpressWarning) alert(res.wordpressWarning);
    if (res?.wordpressUrl) window.open(res.wordpressUrl, "_blank", "noopener,noreferrer");
  }

  async function saveDraftArticle(articleId, bodyHtml) {
    await GuyAPI.articleAction(articleId, { action: "save_draft", bodyHtml });
  }

  async function scheduleArticle(articleId, scheduledFor, bodyHtml) {
    if (bodyHtml?.trim()) {
      await GuyAPI.articleAction(articleId, { action: "save_draft", bodyHtml });
    }
    await GuyAPI.articleAction(articleId, { action: "schedule", scheduledFor });
  }

  function openArticleReview(item, articleFromApi) {
    const raw = articleFromApi
      ? { ...(item.raw || {}), ...articleFromApi, id: articleFromApi.id || item.id }
      : item.raw || { id: item.id, title: item.title };
    setReview({
      id: raw.id || item.id,
      title: articleFromApi?.title || item.title,
      keyword: item.keyword || item.kw || raw.keywords || "",
      volume: item.volume || "",
      readMin: item.readMin || 5,
      score: item.score ?? null,
      intro: item.intro || "",
      bodyHtml: articleFromApi?.bodyHtml || item.bodyHtml || raw.bodyHtml || "",
      imageUrl: articleFromApi?.imageUrl || item.imageUrl || raw.imageUrl || null,
      raw,
    });
  }

  async function assertQuotaForGeneration() {
    const { quota } = await GuyAPI.getArticleQuota();
    if (quota && !quota.unlimited && !quota.canGenerate) {
      throw new Error(`Quota hebdomadaire atteint (${quota.used}/${quota.limit}).`);
    }
    return quota;
  }

  async function generateArticleFromSuggestion(item) {
    const id = item.raw?.id || item.id;
    if (!id) return;
    try {
      await assertQuotaForGeneration();
    } catch (e) {
      alert(e.message);
      return;
    }
    setGeneratingArticleId(id);
    setBusy(true);
    setBootError("");
    try {
      const hasContent = item.raw?.hasContent || item.bodyHtml?.trim();
      if (!hasContent) {
        await GuyAPI.writeArticle(id);
        await refresh();
        setLive((prev) => {
          if (!prev?.articles?.queue) return prev;
          return {
            ...prev,
            articles: {
              ...prev.articles,
              queue: prev.articles.queue.filter(
                (q) => (q.raw?.id || q.id) !== id,
              ),
            },
          };
        });
      }
      const article = await GuyAPI.pollArticleTextReady(id);
      await refresh();
      openArticleReview(item, article);
    } catch (e) {
      setBootError(e.message || "Génération impossible");
      alert(e.message || "Génération impossible");
    } finally {
      setBusy(false);
      setGeneratingArticleId(null);
    }
  }

  async function validateAndPublishNext(item) {
    const id = item?.raw?.id || item?.id;
    if (!id) throw new Error("Article introuvable.");

    setBusy(true);
    try {
      const { article: current } = await GuyAPI.getArticle(id);
      let article = current;

      if (!(current?.status === "content_ready" && current?.bodyHtml?.trim())) {
        await assertQuotaForGeneration();
        await GuyAPI.writeArticle(id);
        await refresh();
        setLive((prev) => {
          if (!prev?.articles?.queue) return prev;
          return {
            ...prev,
            articles: {
              ...prev.articles,
              queue: prev.articles.queue.filter(
                (q) => (q.raw?.id || q.id) !== id,
              ),
            },
          };
        });
        article = await GuyAPI.pollArticleTextReady(id);
      }

      const html = article?.bodyHtml?.trim();
      if (!html) throw new Error("Aucun contenu à publier.");

      const res = await GuyAPI.articleAction(id, { action: "publish" });
      if (res?.wordpressWarning) alert(res.wordpressWarning);
      if (res?.wordpressUrl) window.open(res.wordpressUrl, "_blank", "noopener,noreferrer");
      await refresh();
    } finally {
      setBusy(false);
    }
  }

  async function generateTitles() {
    const cap =
      articleQuota && !articleQuota.unlimited
        ? Math.min(
            articleQuota.remaining > 0 ? articleQuota.remaining : 0,
            articleQuota.limit > 0 ? articleQuota.limit : 8,
          )
        : 8;
    const count = cap > 0 ? cap : 8;
    await runApi(() => GuyAPI.generateTitles(count));
  }

  function startDemarrageAssistant() {
    manualObOpenRef.current = true;
    setObDismissed(false);
    setScreen("home");
    setObStep(1);
  }

  const heroScreens = ["articles", "performance"];
  const showHero = heroScreens.includes(screen);

  const heroMeta = useMemo(() => ({
    articles: {
      eyebrow: "SEO GUY · Articles",
      title: "Articles SEO",
      tip: "Générez des titres, relisez chaque article puis publiez ou programmez.",
      pills: [`${articlesList.length} articles`, live?.articles?.meta?.canPublishLocally ? "Publication Guy" : "Blog à configurer"],
      actions: (
        <>
          <button type="button" className="sg-btn-primary sm" disabled={busy} onClick={generateTitles}>Générer des titres</button>
          <button type="button" className="sg-btn-ghost sm" onClick={() => goToScreen("articles")}>Voir la liste</button>
        </>
      ),
      guy: POSE.working,
    },
    performance: {
      eyebrow: "SEO GUY · Performance",
      title: "Performance globale",
      tip: "Suivez visites SEO, publications et l'impact de Guy sur votre trafic.",
      pills: ["30 jours", live?.gsc?.connected ? "GSC connectée" : "GSC"],
      actions: <button type="button" className="sg-btn-ghost sm" onClick={() => goToScreen("gsc")}>Opportunités GSC</button>,
      guy: POSE.hero,
    },
  }), [articlesList, live, busy]);

  function renderScreen() {
    switch (screen) {
      case "home":
        return (
          <>
            <AlertList alerts={alerts} onNav={goToScreen} />
            <HomeScreen
              queue={queue}
              quota={articleQuota}
              onValidate={validateAndPublishNext}
              onReview={openArticleReview}
              publishedThisMonth={published}
              showMascot={showMascot}
              obStep={obStep}
              obDismissed={obDismissed}
              onboardingIncomplete={!live?.config?.configuration?.onboardingCompleted}
              onResumeOnboarding={() => {
                setObDismissed(false);
                setObStep((s) => (s < 1 ? 1 : s));
              }}
              liveDashboard={live?.home?.dashboard}
              kpis={kpis}
              busy={busy}
              clientName={
                (live?.session?.account?.displayName || "").trim().split(/\s+/)[0] ||
                (live?.session?.site?.shopName || "").trim() ||
                ""
              }
              articlesPerWeek={live?.quota?.limit}
              gscConnected={!!live?.gsc?.connected}
              shopifyConnected={!!live?.session?.shopifyConnection?.connected}
            />
          </>
        );
      case "assistant-start":
      case "assistants":
        return (
          <AssistantsHubScreen
            onOpen={goToScreen}
            onStart={startDemarrageAssistant}
            segmentsCount={live?.config?.configuration?.targetAudiences?.length ?? 0}
            articlesCount={live?.articles?.list?.length ?? 0}
            onboardingCompleted={!!live?.config?.configuration?.onboardingCompleted}
            editorialPlanAllowed={!!live?.settings?.editorialPlanAllowed}
            publicationScheduleAllowed={!!live?.settings?.publicationScheduleAllowed}
            planCode={live?.settings?.planCode || live?.billing?.current?.code || ""}
          />
        );
      case "performance":
        return (
          <PerformanceScreen
            publishedThisMonth={published}
            performance={live?.performance}
            kpis={live?.performance?.kpis}
            traffic={live?.performance?.traffic}
          />
        );
      case "articles":
        return (
          <ArticlesScreen
            articles={articlesList}
            queue={live?.articles?.queue ?? queue}
            titleProposals={live?.articles?.titleProposals}
            onReview={openArticleReview}
            onGenerate={generateArticleFromSuggestion}
            saved={savedSuggestions}
            onGenerateTitles={generateTitles}
            busy={busy}
            generatingId={generatingArticleId}
            quota={articleQuota}
            proposalTotal={live?.articles?.proposalTotal}
            onCoverUploaded={() => { void refresh(); }}
          />
        );
      case "gsc":
        return <GscScreen live={live?.gsc} onRefresh={refresh} busy={busy} />;
      case "config":
        return <ConfigScreen configuration={live?.config?.configuration} shopifyConnected={!!live?.session?.shopifyConnection?.connected} wordpressConnected={!!live?.session?.wordpressConnection?.connected} onSave={(payload) => runApi(() => GuyAPI.saveConfig(payload))} busy={busy} />;
      case "billing":
        return (
          <BillingScreen
            plans={live?.plans}
            current={live?.billing?.current}
            onSelectPlan={async (code, cycle, coupon) => {
              const res = await GuyAPI.selectPlan({
                planCode: code,
                cycle: cycle || "monthly",
                couponCode: coupon || undefined,
              });
              if (res?.redirectUrl) {
                window.location.href = res.redirectUrl;
                return;
              }
              await refresh();
            }}
            busy={busy}
          />
        );
      case "account":
        return (
          <AccountScreen
            sites={live?.sites}
            account={live?.account}
            onSelectSite={selectSite}
            onLogout={handleLogout}
            onRefresh={refresh}
            busy={busy}
          />
        );
      case "settings":
        return (
          <SettingsScreen
            publicationSchedule={live?.settings?.publicationSchedule}
            editorialQuota={live?.settings?.editorialQuota}
            planCode={live?.settings?.planCode || live?.billing?.current?.code || ""}
            planLabel={live?.session?.plan?.name || live?.billing?.current?.name}
            personas={live?.personas ?? []}
            targetAudiences={live?.config?.configuration?.targetAudiences ?? []}
            configuration={live?.config?.configuration}
            publicationScheduleAllowed={!!live?.settings?.publicationScheduleAllowed}
            live={live?.settings}
            shopify={{
              publishingMode: live?.articles?.meta?.publishingMode,
              canPublishOnShopify: live?.articles?.meta?.canPublishOnShopify,
              pendingSyncCount: live?.articles?.meta?.shopifySync?.pendingCount,
              withContentCount: live?.articles?.meta?.shopifySync?.withContentCount,
              shop: live?.session?.site?.shop,
              connected: live?.session?.shopifyConnection?.connected,
              hasAccessToken: live?.session?.shopifyConnection?.hasAccessToken,
            }}
            onSave={async (payload) => {
              await runApi(() => GuyAPI.saveSettings(payload));
              await refresh();
            }}
            onSaveSchedule={async (payload) => {
              await GuyAPI.savePublicationSchedule(payload);
              await refresh();
            }}
            onSaveConfig={async (payload) => {
              await runApi(() => GuyAPI.saveConfig(payload));
              await refresh();
            }}
            onResyncShopify={async () => {
              const res = await GuyAPI.resyncShopifyArticles();
              await refresh();
              return res;
            }}
            onSaveShopifyConnection={async (payload) => {
              await runApi(() => GuyAPI.saveShopifyConnection(payload));
              await refresh();
            }}
            onTestShopifyConnection={(payload) => GuyAPI.testShopifyConnection(payload)}
            onDisconnectShopify={async () => {
              await runApi(() => GuyAPI.disconnectShopifyConnection());
              await refresh();
            }}
            wordpress={live?.session?.wordpressConnection || live?.settings?.wordpressConnection}
            onSaveWordpressConnection={async (payload) => {
              await runApi(() => GuyAPI.saveWordpressConnection(payload));
              await refresh();
            }}
            onTestWordpressConnection={(payload) => GuyAPI.testWordpressConnection(payload)}
            onDisconnectWordpress={async () => {
              await runApi(() => GuyAPI.disconnectWordpressConnection());
              await refresh();
            }}
            busy={busy}
          />
        );
      case "results":
        return <ResultsScreen performance={live?.performance} gscQueries={live?.gsc?.queries || []} />;
      default:
        return null;
    }
  }

  if (bootState === "loading") {
    return (
      <div className="sg-login">
        <div className="sg-login-card">
          <img src={POSE.avatar} alt="" className="sg-login-logo" />
          <p>Chargement du back-office…</p>
        </div>
      </div>
    );
  }

  if (bootState === "auth" || bootState === "pick-site" || bootState === "error") {
    const loginMode =
      bootState === "pick-site" || (bootState === "error" && user)
        ? "pick-site"
        : "auth";
    return (
      <LoginScreen
        mode={loginMode}
        user={user}
        sites={sites}
        activeSiteId={activeSiteId}
        onLogin={handleLogin}
        onRegister={handleRegister}
        onDiscoverShops={handleDiscoverShops}
        onLinkShopify={handleLinkShopify}
        onConnectWebsite={handleConnectWebsite}
        onGoogleLogin={handleGoogleLogin}
        onSelect={selectSite}
        onLogout={handleLogout}
        loading={busy}
        error={bootError}
      />
    );
  }

  const hero = heroMeta[screen];

  return (
    <UnsavedChangesProvider onNavigate={navigateScreen}>
      <NavGuardBridge bridgeRef={navGuardRef} />
      <div className={`sg-app${mobileNavOpen ? " is-nav-open" : ""}`} data-density="confort">
        <MobileTopBar onMenu={() => setMobileNavOpen(true)} />
        <Sidebar
          active={screen}
          onNav={goToScreen}
          showMascot={showMascot}
          onAccount={() => goToScreen("account")}
          siteLabel={live?.session?.site?.shopName || live?.session?.site?.shop}
          planLabel={live?.session?.plan?.name}
          mobileOpen={mobileNavOpen}
          onClose={() => setMobileNavOpen(false)}
        />
        <main className="sg-main">
          {showHero && hero && (
            <HeroBand
              showMascot={showMascot}
              guyImage={hero.guy}
              eyebrow={hero.eyebrow}
              title={hero.title}
              tip={hero.tip}
              pills={hero.pills}
              actions={hero.actions}
            />
          )}
          {renderScreen()}
        </main>

        {review && (
          <ReviewPanel
            article={review}
            rawArticle={review.raw}
            shop={live?.session?.site?.shop}
            publishingMode={live?.articles?.meta?.publishingMode ?? "standalone"}
            hasFalKey={live?.articles?.meta?.hasFalKey}
            quota={articleQuota}
            onClose={() => setReview(null)}
            busy={busy}
            onRefresh={refresh}
            onPublish={(id, html) => publishArticle(id, html)}
            onSaveDraft={(id, html) => saveDraftArticle(id, html)}
            onSchedule={(id, when, html) => scheduleArticle(id, when, html)}
          />
        )}
        {showWpGate && ReactDOM.createPortal(
          <WordpressConnectStep
            suggestedUrl={
              live?.session?.site?.shopDomain
                ? `https://${String(live.session.site.shopDomain).replace(/^https?:\/\//, "")}`
                : (live?.session?.site?.shop || "").replace(/^website:/, "https://")
            }
            onConnected={async () => {
              setWpGateDone(true);
              await refresh();
            }}
            onSkip={() => setWpGateDone(true)}
          />,
          document.body,
        )}
        {showOnboarding && ReactDOM.createPortal(
          <OnboardingFlow
            initialStep={obStep}
            onStepChange={setObStep}
            onComplete={() => {
              manualObOpenRef.current = false;
              setObDismissed(false);
              setObStep(0);
              refresh();
            }}
            onExit={() => {
              manualObOpenRef.current = true;
              setObDismissed(true);
              refresh();
            }}
            onOpenReview={openArticleReview}
            insights={live?.onboardingInsights || obInsights}
            plans={live?.plans ?? PLANS}
            currentPlanCode={live?.session?.plan?.code || live?.billing?.current?.code}
            onboardingCompleted={!!live?.config?.configuration?.onboardingCompleted}
            featuredProducts={live?.config?.configuration?.featuredProducts ?? []}
            businessSummary={live?.config?.configuration?.editorialCharter ?? ""}
            personas={live?.config?.configuration?.targetAudiences ?? live?.personas}
            keywords={live?.keywords?.list ?? keywords}
            gscConnected={live?.gsc?.connected}
            quota={articleQuota}
            isWebsite={live?.session?.site?.platform === "WEBSITE"}
          />,
          document.body,
        )}
      </div>
    </UnsavedChangesProvider>
  );
}

function NavGuardBridge({ bridgeRef }) {
  const requestNavigate = useGuardedNavigate();
  useEffect(() => {
    bridgeRef.current = requestNavigate;
    return () => {
      bridgeRef.current = null;
    };
  }, [requestNavigate, bridgeRef]);
  return null;
}

function App() {
  return <AppShell />;
}

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