// App shell — strict brand theming + Tweaks panel.

const { useState, useEffect, useRef } = React;

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "theme": "light",
  "headlineLine1": "Mobile CCTV.",
  "headlineLine2": "Secure beyond limits.",
  "heroSub": "Solo designs and manufactures the rapidly-deployable surveillance platforms that protect Britain's hardest sites. Engineered in-house since 2019. Trusted by Tier-1 contractors and approved security resellers."
}/*EDITMODE-END*/;

/* Theme palettes — strict to brand book.
 * Primary:    #000000, #FFFFFF, #545454 (gradient)
 * Secondary:  #2B2B2B, #EAEAEA, #AFAFAF (gradient)
 */
const THEMES = {
  light: {
    "--bg":            "#FFFFFF",
    "--surface":       "#EAEAEA",  // brand secondary light
    "--surface-deep":  "#000000",
    "--black":         "#000000",
    "--fg":            "#000000",
    "--fg-soft":       "#2B2B2B",  // brand secondary dark
    "--fg-dim":        "#545454",  // brand primary mid
    "--fg-faint":      "#AFAFAF",  // brand secondary mid
    "--line":          "#EAEAEA",
    "--line-strong":   "#AFAFAF",
    "--invert-fg":      "#FFFFFF",
    "--invert-fg-dim":  "#AFAFAF",
    "--nav-bg":        "rgba(255,255,255,0.92)",
    "--grey700":       "#2B2B2B",
  },
  dark: {
    "--bg":            "#000000",
    "--surface":       "#1A1A1A",
    "--surface-deep":  "#000000",
    "--black":         "#000000",
    "--fg":            "#FFFFFF",
    "--fg-soft":       "#EAEAEA",
    "--fg-dim":        "#AFAFAF",
    "--fg-faint":      "#545454",
    "--line":          "#2B2B2B",
    "--line-strong":   "#545454",
    "--invert-fg":      "#FFFFFF",
    "--invert-fg-dim":  "#AFAFAF",
    "--nav-bg":        "rgba(0,0,0,0.92)",
    "--grey700":       "#EAEAEA",
  },
};

// Auth-gated pages that should never be restored from localStorage on a
// fresh tab — e.g. an admin visiting "/" in the same browser they used
// for "/admin" must land on the marketing home page, not back on the
// admin login/approvals screen (and vice versa for a reseller session).
// Reaching any of these always goes through an explicit onNavigate() call
// (or the /admin pathname check below), never a restored "last page".
const AUTH_GATED_PAGES = new Set([
  "reseller-login", "reseller-signup", "portal-dashboard", "portal-quotes", "support",
  "admin-login", "admin-approvals", "admin-assets", "admin-companies",
  "admin-pricing", "admin-quotes", "admin-orders", "admin-invoices", "admin-purchase-orders",
  "admin-spec-sheets",
  "admin-tickets",
  "admin-settings",
  "admin-staff",
  "unit-management",
  "mission-control",
  "reseller-forgot-password", "reseller-reset-password",
  "admin-forgot-password", "admin-reset-password", "admin-accept-invite",
]);

// A password-reset email link points at "/?resetToken=...&resetType=admin|reseller"
// (see the resetUrl built in routes/portal.ts and routes/admin.ts) rather
// than a real per-page route (the app has no real URL routing — see the
// history-state-only comment below), so the very first thing on mount is
// to notice that query string and treat it as "start on the matching
// reset-password page", exactly like the existing /admin pathname check
// does for the admin login entry point. Read once at module scope (not
// inside App()) so it's stable across re-renders; the query string is
// only ever meaningful on the initial page load.
//
// "admin-invite" is a distinct resetType from "admin" — same underlying
// admin_users token space (routes/admin-staff.ts's sendInviteEmail still
// calls createPasswordResetToken with user_type "admin"), but a brand new
// Solo Staff hire following their invite link should land on "Set your
// password" copy, not "Reset your password" — see AdminAcceptInvitePage
// in admin-staff-page.jsx.
function readResetLinkFromLocation() {
  if (typeof window === "undefined") return null;
  const params = new URLSearchParams(window.location.search);
  const token = params.get("resetToken");
  const type = params.get("resetType");
  if (!token || (type !== "admin" && type !== "reseller" && type !== "admin-invite")) return null;
  return { token, type };
}
const INITIAL_RESET_LINK = readResetLinkFromLocation();

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [resetToken, setResetToken] = useState(INITIAL_RESET_LINK ? INITIAL_RESET_LINK.token : null);
  const [page, setPage] = useState(() => {
    // A password-reset link takes priority over everything else on
    // first mount — someone who just clicked a reset email should land
    // straight on the reset-password form, not the marketing homepage.
    if (INITIAL_RESET_LINK) {
      if (INITIAL_RESET_LINK.type === "admin-invite") return "admin-accept-invite";
      return INITIAL_RESET_LINK.type === "admin" ? "admin-reset-password" : "reseller-reset-password";
    }
    // Solo staff reach the admin area via a bookmarkable /admin URL rather
    // than a public nav link — the SPA fallback (see wrangler.jsonc's
    // not_found_handling) serves this same index.html for that path, we
    // just need to notice it on first mount and start there instead of
    // whatever page was last saved to localStorage. AdminLoginPage itself
    // checks for an existing session and skips straight to the approvals
    // queue if there is one.
    if (typeof window !== "undefined" && window.location.pathname.startsWith("/admin")) {
      // Xero's OAuth callback (routes/admin-xero.ts's /xero/callback) redirects
      // the browser to /admin?xero=connected|denied|error|state_mismatch —
      // land straight on Settings (already gated by requireAdmin server-side,
      // so a logged-out admin still gets bounced to login by AdminSettingsPage's
      // own /api/admin/me check) rather than the default approvals queue, so
      // the banner it reads from that query string is actually seen.
      if (new URLSearchParams(window.location.search).has("xero")) {
        return "admin-settings";
      }
      return "admin-login";
    }
    const saved = localStorage.getItem("solo-page") || "home";
    return AUTH_GATED_PAGES.has(saved) ? "home" : saved;
  });
  const [productId, setProductId] = useState(() => localStorage.getItem("solo-product") || "pro-tower");
  const [sectorId, setSectorId] = useState(() => localStorage.getItem("solo-sector") || "construction");
  const [serviceId, setServiceId] = useState(() => localStorage.getItem("solo-service") || "contract-manufacturing");
  // Optional subject pre-fill for any form page (contact, careers, press,
  // investors, partners reseller form). Buttons set this via onNavigate.
  const [formSubject, setFormSubject] = useState("");
  // Which SupportPage tab is showing ("raise" | "my-tickets"). Lives here
  // (not as SupportPage-local state) so ResellerShell's nav — and
  // ResellerPortalDashboard's "Raise a ticket"/"My tickets" buttons — can
  // land on a specific tab via onNavigate("support", { tab: "..." })
  // without a full page remount always resetting back to "raise".
  const [supportTab, setSupportTab] = useState("raise");
  // Which unit + viewer type (reseller | admin) System Management is
  // currently showing. Not persisted to localStorage — like every other
  // AUTH_GATED_PAGES entry, this is always reached via an explicit
  // onNavigate("unit-management", { unitId, viewerType }) call from a
  // specific asset row, never restored across reloads.
  const [unitId, setUnitId] = useState(null);
  const [unitViewerType, setUnitViewerType] = useState("reseller");
  // Which purchase order to auto-open on the admin Purchase Orders page —
  // set only via an explicit onNavigate("admin-purchase-orders", { poId })
  // call (e.g. the "PO ..." link on an asset unit row); not persisted, and
  // cleared the moment that page has consumed it (see AdminPurchaseOrdersPage).
  const [poId, setPoId] = useState(null);

  // Strip ?resetToken=...&resetType=... from the visible URL once we've
  // captured it into state above — it's a one-time entry point, not
  // something that should linger in the address bar (or get shared/
  // bookmarked accidentally with a token that'll expire in 45 minutes
  // anyway). Uses replaceState so this doesn't add a spurious Back-button
  // history entry.
  useEffect(() => {
    if (INITIAL_RESET_LINK && typeof window !== "undefined") {
      window.history.replaceState(window.history.state, "", window.location.pathname);
    }
  }, []); // eslint-disable-line react-hooks/exhaustive-deps -- mount-only

  useEffect(() => {
    if (!AUTH_GATED_PAGES.has(page)) localStorage.setItem("solo-page", page);
  }, [page]);
  useEffect(() => { localStorage.setItem("solo-product", productId); }, [productId]);
  useEffect(() => { localStorage.setItem("solo-sector", sectorId); }, [sectorId]);
  useEffect(() => { localStorage.setItem("solo-service", serviceId); }, [serviceId]);

  // ── Browser Back/Forward support ──────────────────────────────────────
  // The app's "routing" is pure React state (no real URL changes), so by
  // default the browser history stack has no idea pages ever changed —
  // pressing Back just leaves the site entirely. To fix that we:
  //   1. Stamp a history entry (via replaceState) on first mount so there's
  //      always a state object to pop back to.
  //   2. Have onNavigate() push a new history entry on every in-app nav.
  //   3. Listen for `popstate` (fired on Back/Forward) and restore the
  //      page/product/sector/service/subject from the popped state.
  // The visible URL is intentionally left unchanged (still "/") — this is
  // history-state-only navigation, not real per-page URLs.
  useEffect(() => {
    window.history.replaceState(
      { page, productId, sectorId, serviceId, formSubject: "", unitId, unitViewerType },
      ""
    );

    const onPopState = (event) => {
      const s = event.state;
      if (!s) return; // nothing we recognise (e.g. entry from before app loaded)
      setPage(s.page || "home");
      if (s.productId) setProductId(s.productId);
      if (s.sectorId)  setSectorId(s.sectorId);
      if (s.serviceId) setServiceId(s.serviceId);
      setFormSubject(s.formSubject || "");
      if (s.page === "unit-management") {
        setUnitId(s.unitId != null ? s.unitId : null);
        setUnitViewerType(s.unitViewerType || "reseller");
      }
      window.scrollTo({ top: 0, behavior: "instant" });
    };
    window.addEventListener("popstate", onPopState);
    return () => window.removeEventListener("popstate", onPopState);
  }, []); // eslint-disable-line react-hooks/exhaustive-deps -- intentionally mount-only

  // ── SEO ── Update <title>, meta description, canonical, OG tags on
  // every navigation so bots and social previews see the right content.
  // Logic + per-page copy lives in shared/seo.js.
  useEffect(() => {
    if (!window.applyPageSEO) return;
    // For product-detail + sector-detail pages we pass the sub-id so
    // the SEO map can pull product/sector-specific title + description.
    const subId =
      page === "product-detail" ? productId :
      page === "sector"         ? sectorId  :
      page === "sector-detail"  ? sectorId  :
      null;
    window.applyPageSEO(page, subId);
  }, [page, productId, sectorId]);

  // onNavigate(target, idOrOpts)
  //   idOrOpts:
  //     - a string  → product/sector id (legacy callers)
  //     - an object → { subject?: string, productId?: string, sectorId?: string }
  const onNavigate = (next, idOrOpts) => {
    let opts;
    if (typeof idOrOpts === "string") {
      // legacy positional id arg
      if (next === "product-detail" || next === "product") opts = { productId: idOrOpts };
      else if (next === "sector") opts = { sectorId: idOrOpts };
      else opts = {};
    } else {
      opts = idOrOpts || {};
    }
    const nextProductId = opts.productId || productId;
    const nextSectorId  = opts.sectorId  || sectorId;
    const nextServiceId = opts.serviceId || serviceId;

    setPage(next);
    if (opts.productId) setProductId(opts.productId);
    if (opts.sectorId)  setSectorId(opts.sectorId);
    if (opts.serviceId) setServiceId(opts.serviceId);
    // Reset or update the form subject. A fresh navigation without a subject
    // arg should clear stale pre-fill from a previous click.
    setFormSubject(opts.subject || "");
    // Support-tab targeting (see ResellerShell / ResellerPortalDashboard).
    // Only reset to the default "raise" tab when actually navigating TO
    // the support page without an explicit tab — navigating to some other
    // page shouldn't clobber supportTab, since it'll just be read again
    // next time "support" is reached.
    if (next === "support") setSupportTab(opts.tab || "raise");
    // System Management target unit (see unit-management-page.jsx).
    if (next === "unit-management") {
      setUnitId(opts.unitId != null ? opts.unitId : null);
      setUnitViewerType(opts.viewerType || "reseller");
    }
    // Deep-link to a specific purchase order (see admin-assets-page.jsx's
    // unit-row PO link).
    if (next === "admin-purchase-orders") {
      setPoId(opts.poId != null ? opts.poId : null);
    }

    // Push a browser history entry so Back/Forward can step through
    // in-app page changes instead of leaving the site. See the mount
    // effect above for the matching `popstate` listener.
    window.history.pushState(
      {
        page: next, productId: nextProductId, sectorId: nextSectorId, serviceId: nextServiceId,
        formSubject: opts.subject || "",
        unitId: next === "unit-management" ? (opts.unitId != null ? opts.unitId : null) : unitId,
        unitViewerType: next === "unit-management" ? (opts.viewerType || "reseller") : unitViewerType,
      },
      ""
    );
    // If the caller specified an in-page anchor to scroll to (e.g. "support"
    // for the support-ticket section), defer the scroll until React has
    // rendered the new page. Otherwise scroll to top.
    if (opts.scrollTo) {
      // Allow a paint tick for the destination page to mount
      setTimeout(() => {
        const el = document.getElementById(opts.scrollTo);
        if (el) el.scrollIntoView({ behavior: "smooth", block: "start" });
        else window.scrollTo({ top: 0, behavior: "instant" });
      }, 60);
    } else {
      window.scrollTo({ top: 0, behavior: "instant" });
    }
  };

  // Apply theme vars to :root (also stamp data-theme so SoloLogo can swap variants)
  useEffect(() => {
    const theme = THEMES[t.theme] || THEMES.light;
    const root = document.documentElement;
    Object.entries(theme).forEach(([k, v]) => root.style.setProperty(k, v));
    root.dataset.theme = t.theme;
    root.style.setProperty("--font-display", "'Michroma', 'Eurostile Extended', 'Eurostile', system-ui, sans-serif");
    root.style.setProperty("--font-body",    "'Helvetica Neue', 'Helvetica', 'Inter', system-ui, sans-serif");
  }, [t.theme]);

  const hero = {
    headline: [t.headlineLine1, t.headlineLine2],
    sub: t.heroSub,
  };

  return (
    <div style={{
      minHeight: "100vh",
      background: "var(--bg)",
      color: "var(--fg)",
      fontFamily: "var(--font-body)",
    }}>
      <TopNav page={page} onNavigate={onNavigate} />
      {page === "home"           && <HomePage           onNavigate={onNavigate} hero={hero} />}
      {page === "product"        && <ProductsIndexPage  onNavigate={onNavigate} />}
      {page === "product-detail" && <ProductPage        onNavigate={onNavigate} productId={productId} />}
      {page === "services"       && <ServicesPage       onNavigate={onNavigate} />}
      {page === "service-detail" && <ServiceDetailPage  onNavigate={onNavigate} serviceId={serviceId} />}
      {page === "sector"         && <SectorPage         onNavigate={onNavigate} sectorId={sectorId} />}
      {page === "regions"        && <RegionsPage        onNavigate={onNavigate} />}
      {page === "about"          && <AboutPage          onNavigate={onNavigate} />}
      {page === "our-story"      && <OurStoryPage       onNavigate={onNavigate} />}
      {page === "leadership"     && <LeadershipPage     onNavigate={onNavigate} />}
      {page === "manufacturing"  && <ManufacturingPage  onNavigate={onNavigate} />}
      {page === "press"          && <PressPage          onNavigate={onNavigate} formSubject={formSubject} />}
      {page === "investors"      && <InvestorsPage      onNavigate={onNavigate} formSubject={formSubject} />}
      {page === "esg"            && <ESGPage            onNavigate={onNavigate} />}
      {page === "partners"            && <PartnersPage          onNavigate={onNavigate} formSubject={formSubject} />}
      {page === "exclusive-regions"   && <ExclusiveRegionsPage  onNavigate={onNavigate} />}
      {page === "gold-partners"       && <GoldPartnersPage      onNavigate={onNavigate} />}
      {page === "platinum-partners"   && <PlatinumPartnersPage  onNavigate={onNavigate} />}
      {page === "sponsors"       && <SponsorsPage       onNavigate={onNavigate} />}
      {page === "sponsor-archie" && <SponsorArchiePage  onNavigate={onNavigate} />}
      {page === "sponsor-tommy"  && <SponsorTommyPage   onNavigate={onNavigate} />}
      {page === "contact"        && <ContactPage        onNavigate={onNavigate} formSubject={formSubject} />}
      {page === "support"        && <SupportPage        onNavigate={onNavigate} activeTab={supportTab} onTabChange={setSupportTab} />}
      {page === "reseller-login" && <ResellerLoginPage  onNavigate={onNavigate} />}
      {page === "reseller-forgot-password" && <ResellerForgotPasswordPage onNavigate={onNavigate} />}
      {page === "reseller-reset-password" && <ResellerResetPasswordPage onNavigate={onNavigate} token={resetToken} />}
      {page === "reseller-signup" && <ResellerSignupPage onNavigate={onNavigate} />}
      {page === "portal-dashboard" && <ResellerPortalDashboard onNavigate={onNavigate} />}
      {page === "portal-quotes" && <ResellerQuotesPage onNavigate={onNavigate} />}
      {page === "unit-management" && <UnitManagementPage onNavigate={onNavigate} unitId={unitId} viewerType={unitViewerType} />}
      {page === "admin-login"    && <AdminLoginPage     onNavigate={onNavigate} />}
      {page === "admin-forgot-password" && <AdminForgotPasswordPage onNavigate={onNavigate} />}
      {page === "admin-reset-password" && <AdminResetPasswordPage onNavigate={onNavigate} token={resetToken} />}
      {page === "admin-approvals" && <AdminApprovalsPage onNavigate={onNavigate} />}
      {page === "admin-assets" && <AdminAssetsPage onNavigate={onNavigate} />}
      {page === "admin-companies" && <AdminCompaniesPage onNavigate={onNavigate} />}
      {page === "admin-pricing" && <AdminPricingPage onNavigate={onNavigate} />}
      {page === "admin-quotes" && <AdminQuotesPage onNavigate={onNavigate} />}
      {page === "admin-orders" && <AdminOrdersPage onNavigate={onNavigate} />}
      {page === "admin-invoices" && <AdminInvoicesPage onNavigate={onNavigate} />}
      {page === "admin-purchase-orders" && <AdminPurchaseOrdersPage onNavigate={onNavigate} initialPoId={poId} />}
      {page === "admin-spec-sheets" && <AdminSpecSheetsPage onNavigate={onNavigate} />}
      {page === "admin-tickets" && <AdminTicketsPage onNavigate={onNavigate} />}
      {page === "admin-settings" && <AdminSettingsPage onNavigate={onNavigate} />}
      {page === "admin-staff" && <AdminStaffPage onNavigate={onNavigate} />}
      {page === "mission-control" && <MissionControlPage onNavigate={onNavigate} />}
      {page === "admin-accept-invite" && <AdminAcceptInvitePage onNavigate={onNavigate} token={resetToken} />}
      {page === "builder"        && <BuilderPage        onNavigate={onNavigate} />}
      {page === "careers"        && <CareersPage        onNavigate={onNavigate} formSubject={formSubject} />}
      {page === "sustainability" && <SustainabilityPage onNavigate={onNavigate} />}
      {page === "cases"          && <CasesPage          onNavigate={onNavigate} />}

      {/* ── Legal ── */}
      {page === "privacy"         && <PrivacyPage         onNavigate={onNavigate} />}
      {page === "terms"           && <TermsPage           onNavigate={onNavigate} />}
      {page === "warranty"        && <WarrantyPage        onNavigate={onNavigate} />}
      {page === "modern-slavery"  && <ModernSlaveryPage   onNavigate={onNavigate} />}
      {page === "cookie-policy"   && <CookiePolicyPage    onNavigate={onNavigate} />}

      <Footer onNavigate={onNavigate} />

      <SoloTweaks
        t={t} setTweak={setTweak}
        page={page} onNavigate={onNavigate}
        productId={productId} sectorId={sectorId}
        setProductId={setProductId} setSectorId={setSectorId}
      />

      {/* Live chat bubble — floats over every page. Placeholder UI today;
          swap for Intercom / Crisp / Tawk widget when ready. */}
      <ChatBubble onNavigate={onNavigate} />
    </div>
  );
}

function SoloTweaks({ t, setTweak, page, onNavigate, productId, sectorId, setProductId, setSectorId }) {
  return (
    <TweaksPanel title="Tweaks">
      <TweakSection title="Theme">
        <TweakRadio
          label="Mode"
          value={t.theme}
          options={[
            { label: "Light", value: "light" },
            { label: "Dark",  value: "dark" },
          ]}
          onChange={(v) => setTweak("theme", v)}
        />
      </TweakSection>

      <TweakSection title="Page">
        <TweakSelect
          label="Current page"
          value={page}
          options={[
            { label: "Home",            value: "home" },
            { label: "Products (index)", value: "product" },
            { label: "Product detail",  value: "product-detail" },
            { label: "Services",        value: "services" },
            { label: "Sectors",         value: "sector" },
            { label: "Company",         value: "about" },
            { label: "  · Our Story",     value: "our-story" },
            { label: "  · Leadership",    value: "leadership" },
            { label: "  · Manufacturing", value: "manufacturing" },
            { label: "  · Press & Media", value: "press" },
            { label: "  · Investors",     value: "investors" },
            { label: "  · ESG",           value: "esg" },
            { label: "Partners",        value: "partners" },
            { label: "  · Exclusive Regions",  value: "exclusive-regions" },
            { label: "  · Gold Partners",      value: "gold-partners" },
            { label: "  · Platinum Partners",  value: "platinum-partners" },
            { label: "  · Regions overview",   value: "regions" },
            { label: "Our Sponsorships",      value: "sponsors" },
            { label: "  · Archie Davies",     value: "sponsor-archie" },
            { label: "  · Tommy Lovejoy",     value: "sponsor-tommy" },
            { label: "Contact",         value: "contact" },
            { label: "Careers",         value: "careers" },
            { label: "Sustainability",  value: "sustainability" },
            { label: "Deployments",     value: "cases" },
          ]}
          onChange={(v) => onNavigate(v)}
        />
        {page === "product-detail" && (
          <TweakSelect
            label="Product"
            value={productId}
            options={window.SOLO_DATA.products.map(p => ({ label: p.name, value: p.id }))}
            onChange={(v) => setProductId(v)}
          />
        )}
        {page === "sector" && (
          <TweakSelect
            label="Sector"
            value={sectorId}
            options={window.SOLO_DATA.sectors.map(s => ({ label: s.name, value: s.id }))}
            onChange={(v) => setSectorId(v)}
          />
        )}
      </TweakSection>

      <TweakSection title="Hero copy">
        <TweakText label="Headline line 1" value={t.headlineLine1} onChange={(v) => setTweak("headlineLine1", v)} />
        <TweakText label="Headline line 2" value={t.headlineLine2} onChange={(v) => setTweak("headlineLine2", v)} />
        <TweakText label="Sub" value={t.heroSub} onChange={(v) => setTweak("heroSub", v)} multiline />
      </TweakSection>

      <TweakSuggestionBar suggestions={[
        "Make all dividers thicker — feels more brand-correct",
        "Add the brand wave/particle pattern behind the hero",
        "Drop in real photos for the hero + product cards",
        "Try the dark theme for the whole site",
        "Tighten the section numerals — make them smaller",
        "Add downloadable PDF spec sheets for each product",
      ]} />
    </TweaksPanel>
  );
}

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