<% if (i18nEnabled) { %>
import type { Metadata } from 'next';
<%- fontImport %>
import { StoreProvider } from '@/core/providers/store-provider';
import { AnnouncementBar } from '@/ui/layout/announcement-bar';
import { SiteHeader } from '@/ui/layout/site-header';
import { DiscountBannerStrip } from '@/ui/home/discount-banner-strip';
import { SiteFooter } from '@/ui/layout/site-footer';
import { BrainerceBotWidget } from '@/components/brainerce-bot';
import { TrackingBootstrap } from '@/components/tracking-bootstrap';
import { getServerClient, fetchStoreInfo } from '@/core/lib/brainerce.server';
import { resolveRegion } from '@/core/lib/region.server';
import { getDirection, getMessages, supportedLocales } from '@/i18n';
import { getNonce } from '@/core/lib/nonce';
import { getCanonicalSiteUrl } from '@/core/lib/site-url';
import { resolveStoreName, buildTitleTemplate } from '@/core/lib/store-name';
import { buildMetaDescription } from '@/core/lib/seo';
import '../globals.css';

<%- fontVariable %>

// Resolved per request rather than read from a hardcoded env default: the
// scaffolder cannot know the deploy domain, so `metadataBase` is the one
// value that must never be guessed. See core/lib/site-url.ts.
//
// The store NAME is resolved the same way, and for the same reason: under
// `--defer-connection` the scaffolder only knew the directory name. Live
// `storeInfo.name` wins (`fetchStoreInfo` is `cache()`-wrapped, so this shares
// the layout's fetch), then the build-time NEXT_PUBLIC_STORE_NAME, then the
// scaffold literal — see core/lib/store-name.ts.
export async function generateMetadata({
  params,
}: {
  params: Promise<{ locale: string }>;
}): Promise<Metadata> {
  const { locale } = await params;
  const [baseUrl, storeInfo] = await Promise.all([getCanonicalSiteUrl(), fetchStoreInfo(locale)]);
  const storeName = resolveStoreName(storeInfo);
  return {
  metadataBase: new URL(baseUrl),
  title: {
    default: storeName,
    template: buildTitleTemplate(storeName),
  },
  description: buildMetaDescription(storeInfo?.metaDescription) || storeName,
  alternates: {
    canonical: '/',
  },
  openGraph: {
    siteName: storeName,
    type: 'website',
  },
  robots: {
    index: true,
    follow: true,
  },
  };
}

function buildOrganizationJsonLd(baseUrl: string, storeName: string) {
  return {
    '@context': 'https://schema.org',
    '@type': 'Organization',
    name: storeName,
    url: baseUrl,
  };
}

export function generateStaticParams() {
  return supportedLocales.map((locale) => ({ locale }));
}

export default async function RootLayout({
  children,
  params,
}: {
  children: React.ReactNode;
  params: Promise<{ locale: string }>;
}) {
  const { locale } = await params;
  const dir = getDirection(locale);
  const nonce = await getNonce();
  const baseUrl = await getCanonicalSiteUrl();

  // Merchant-driven layout chrome — fetched server-side. Each call falls back
  // to null/[] on 404 so the layout never crashes when the merchant hasn't
  // seeded a particular content type yet. New stores ship with default rows
  // seeded by the backend (StoresService.seedDefaultContent).
  const client = await getServerClient(locale);
  const [announcements, siteHeader, siteFooter, storeInfo, messages, discountBanners, regionInfo] =
    await Promise.all([
    client.content.announcement.list(locale).catch(() => []),
    client.content.header.get('main', locale).catch(() => null),
    client.content.footer.get('main', locale).catch(() => null),
    // SSR-fetch store config so PriceDisplay / FreeShippingBar / upsell UI
    // render with the real currency, feature flags, and i18n config at frame 0
    // — without this, Googlebot sees USD/defaults baked into the HTML.
    fetchStoreInfo(locale),
    // SSR-fetch UI message strings so translated text renders at frame 0 —
    // without this, every page (and every locale switch) shows raw
    // "namespace.key" strings until the client-only fetch resolves.
    getMessages(locale),
    // Store-wide discount banners. Fetched here rather than on the home
    // page so they render below the header on EVERY page. `.catch(() => [])`
    // keeps a store with no active discounts (and an API hiccup) silent:
    // the strip itself returns null on an empty array.
    client.getDiscountBanners().catch(() => []),
    // Which region prices this request: the shopper's cookie, else the edge
    // geo header, else the store default. Resolved here so SSR and the client
    // agree from frame 0 and every page shares one round trip (it is
    // `cache()`-wrapped). Never throws: a store with no regions answers
    // `{ regions: [], region: null }` and every consumer then sends no
    // `regionId`, which is what this storefront did before regions existed.
    resolveRegion(),
  ]);
  // One resolved name for the JSON-LD, the header and the footer. Live data
  // first; the scaffold-time literal is only the last fallback.
  const storeName = resolveStoreName(storeInfo);

  return (
    <html lang={locale} dir={dir}>
      <head>
        <script
          type="application/ld+json"
          nonce={nonce}
          suppressHydrationWarning
          dangerouslySetInnerHTML={{
            __html: JSON.stringify(buildOrganizationJsonLd(baseUrl, storeName))
              .replace(/</g, '\\u003c')
              .replace(/>/g, '\\u003e')
              .replace(/&/g, '\\u0026'),
          }}
        />
        {/* Google Search Console verification — token set by the merchant in
            the dashboard (channel settings → Google site verification). Also
            what the Merchant Center website claim checks for. Renders nothing
            until a token is configured. */}
        {storeInfo?.seo?.googleSiteVerification ? (
          <meta name="google-site-verification" content={storeInfo.seo.googleSiteVerification} />
        ) : null}
        {/* RSS autodiscovery — feed readers and aggregators find the blog feed
            from any page. The route itself lives at /blog/rss.xml. */}
        <link rel="alternate" type="application/rss+xml" title="Blog" href="/blog/rss.xml" />
        {/* Brainerce cookieless traffic analytics — auto-tracks pageviews +
            SPA route changes. Cookieless (no consent banner needed); the
            merchant can toggle it per sales channel in the dashboard. */}
        <script
          defer
          src={`${process.env.NEXT_PUBLIC_BRAINERCE_PIXEL_URL || 'https://api.brainerce.com'}/t.js`}
          data-channel={
            process.env.NEXT_PUBLIC_BRAINERCE_SALES_CHANNEL_ID || '<%= connectionId %>'
          }
          nonce={nonce}
          suppressHydrationWarning
        />
      </head>
      <body className={font.className}>
        <StoreProvider
          locale={locale}
          initialStoreInfo={storeInfo}
          initialMessages={messages}
          initialRegions={regionInfo.regions}
          initialRegion={regionInfo.region}
          initialCountry={regionInfo.country}
        >
          {/* Mounted ahead of the page tree on purpose: React runs effects in
              tree order, so booting the tags here guarantees fbq/ttq exist by
              the time a page component fires its first e-commerce event. */}
          <TrackingBootstrap />
          <div className="min-h-screen flex flex-col">
            <AnnouncementBar announcements={announcements} />
            <SiteHeader header={siteHeader} storeName={storeName} />
            <DiscountBannerStrip banners={discountBanners} />
            <main className="flex-1">{children}</main>
            <SiteFooter footer={siteFooter} storeName={storeName} />
          </div>
          <BrainerceBotWidget />
        </StoreProvider>
      </body>
    </html>
  );
}
<% } else { %>
import type { Metadata } from 'next';
<%- fontImport %>
import { StoreProvider } from '@/core/providers/store-provider';
import { AnnouncementBar } from '@/ui/layout/announcement-bar';
import { SiteHeader } from '@/ui/layout/site-header';
import { DiscountBannerStrip } from '@/ui/home/discount-banner-strip';
import { SiteFooter } from '@/ui/layout/site-footer';
import { BrainerceBotWidget } from '@/components/brainerce-bot';
import { TrackingBootstrap } from '@/components/tracking-bootstrap';
import { getServerClient, fetchStoreInfo } from '@/core/lib/brainerce.server';
import { resolveRegion } from '@/core/lib/region.server';
import { getNonce } from '@/core/lib/nonce';
import { getCanonicalSiteUrl } from '@/core/lib/site-url';
import { resolveStoreName, buildTitleTemplate } from '@/core/lib/store-name';
import { buildMetaDescription } from '@/core/lib/seo';
import './globals.css';

<%- fontVariable %>

// Resolved per request rather than read from a hardcoded env default: the
// scaffolder cannot know the deploy domain, so `metadataBase` is the one
// value that must never be guessed. See core/lib/site-url.ts.
//
// The store NAME is resolved the same way, and for the same reason: under
// `--defer-connection` the scaffolder only knew the directory name. Live
// `storeInfo.name` wins (`fetchStoreInfo` is `cache()`-wrapped, so this shares
// the layout's fetch), then the build-time NEXT_PUBLIC_STORE_NAME, then the
// scaffold literal — see core/lib/store-name.ts.
export async function generateMetadata(): Promise<Metadata> {
  const [baseUrl, storeInfo] = await Promise.all([getCanonicalSiteUrl(), fetchStoreInfo()]);
  const storeName = resolveStoreName(storeInfo);
  return {
  metadataBase: new URL(baseUrl),
  title: {
    default: storeName,
    template: buildTitleTemplate(storeName),
  },
  description: buildMetaDescription(storeInfo?.metaDescription) || storeName,
  alternates: {
    canonical: '/',
  },
  openGraph: {
    siteName: storeName,
    locale: '<%= ogLocale %>',
    type: 'website',
  },
  robots: {
    index: true,
    follow: true,
  },
  };
}

function buildOrganizationJsonLd(baseUrl: string, storeName: string) {
  return {
    '@context': 'https://schema.org',
    '@type': 'Organization',
    name: storeName,
    url: baseUrl,
  };
}

export default async function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  const nonce = await getNonce();
  const baseUrl = await getCanonicalSiteUrl();

  // Merchant-driven layout chrome — fetched server-side. Each call falls back
  // to null/[] on 404 so the layout never crashes when the merchant hasn't
  // seeded a particular content type yet. New stores ship with default rows
  // seeded by the backend (StoresService.seedDefaultContent).
  const client = await getServerClient();
  const [announcements, siteHeader, siteFooter, storeInfo, discountBanners, regionInfo] =
    await Promise.all([
    client.content.announcement.list().catch(() => []),
    client.content.header.get('main').catch(() => null),
    client.content.footer.get('main').catch(() => null),
    // SSR-fetch store config so PriceDisplay / FreeShippingBar / upsell UI
    // render with the real currency, feature flags, and i18n config at frame 0
    // — without this, Googlebot sees USD/defaults baked into the HTML.
    fetchStoreInfo(),
    // Store-wide discount banners. Fetched here rather than on the home
    // page so they render below the header on EVERY page. `.catch(() => [])`
    // keeps a store with no active discounts (and an API hiccup) silent:
    // the strip itself returns null on an empty array.
    client.getDiscountBanners().catch(() => []),
    // Which region prices this request: the shopper's cookie, else the edge
    // geo header, else the store default. Resolved here so SSR and the client
    // agree from frame 0 and every page shares one round trip (it is
    // `cache()`-wrapped). Never throws: a store with no regions answers
    // `{ regions: [], region: null }` and every consumer then sends no
    // `regionId`, which is what this storefront did before regions existed.
    resolveRegion(),
  ]);
  // One resolved name for the JSON-LD, the header and the footer. Live data
  // first; the scaffold-time literal is only the last fallback.
  const storeName = resolveStoreName(storeInfo);

  return (
    <html lang="<%= language %>" dir="<%= direction %>">
      <head>
        <script
          type="application/ld+json"
          nonce={nonce}
          suppressHydrationWarning
          dangerouslySetInnerHTML={{
            __html: JSON.stringify(buildOrganizationJsonLd(baseUrl, storeName))
              .replace(/</g, '\\u003c')
              .replace(/>/g, '\\u003e')
              .replace(/&/g, '\\u0026'),
          }}
        />
        {/* Google Search Console verification — token set by the merchant in
            the dashboard (channel settings → Google site verification). Also
            what the Merchant Center website claim checks for. Renders nothing
            until a token is configured. */}
        {storeInfo?.seo?.googleSiteVerification ? (
          <meta name="google-site-verification" content={storeInfo.seo.googleSiteVerification} />
        ) : null}
        {/* RSS autodiscovery — feed readers and aggregators find the blog feed
            from any page. The route itself lives at /blog/rss.xml. */}
        <link rel="alternate" type="application/rss+xml" title="Blog" href="/blog/rss.xml" />
        {/* Brainerce cookieless traffic analytics — auto-tracks pageviews +
            SPA route changes. Cookieless (no consent banner needed); the
            merchant can toggle it per sales channel in the dashboard. */}
        <script
          defer
          src={`${process.env.NEXT_PUBLIC_BRAINERCE_PIXEL_URL || 'https://api.brainerce.com'}/t.js`}
          data-channel={
            process.env.NEXT_PUBLIC_BRAINERCE_SALES_CHANNEL_ID || '<%= connectionId %>'
          }
          nonce={nonce}
          suppressHydrationWarning
        />
      </head>
      <body className={font.className}>
        <StoreProvider
          initialStoreInfo={storeInfo}
          initialRegions={regionInfo.regions}
          initialRegion={regionInfo.region}
          initialCountry={regionInfo.country}
        >
          {/* Mounted ahead of the page tree on purpose: React runs effects in
              tree order, so booting the tags here guarantees fbq/ttq exist by
              the time a page component fires its first e-commerce event. */}
          <TrackingBootstrap />
          <div className="min-h-screen flex flex-col">
            <AnnouncementBar announcements={announcements} />
            <SiteHeader header={siteHeader} storeName={storeName} />
            <DiscountBannerStrip banners={discountBanners} />
            <main className="flex-1">{children}</main>
            <SiteFooter footer={siteFooter} storeName={storeName} />
          </div>
          <BrainerceBotWidget />
        </StoreProvider>
      </body>
    </html>
  );
}
<% } %>
