'use client';

import React, { createContext, useContext, useEffect, useState, useCallback } from 'react';
import { useRouter } from 'next/navigation';
import type { Cart, CustomerProfile, PublicRegion } from 'brainerce';
import { getCartTotals } from 'brainerce';
import { getClient, initClient } from '@/core/lib/brainerce';
import { pickRegion, readRegionCookie, writeRegionCookie } from '@/core/lib/region';
import { pickPublicStoreInfo, type PublicStoreInfo } from '@/core/lib/store-info';
import {
  pickPublicCapabilities,
  type PublicStoreCapabilities,
} from '@/core/lib/capabilities';
import { checkAuthStatus, proxyLogout } from '@/core/lib/auth';
<% if (i18nEnabled) { %>
import { MessagesContext } from '@/core/lib/translations';
import { getMessages, defaultLocale } from '@/i18n';
<% } %>

// ---- Store Info Context ----
interface StoreInfoContextValue {
  storeInfo: PublicStoreInfo | null;
  loading: boolean;
}

const StoreInfoContext = createContext<StoreInfoContextValue>({
  storeInfo: null,
  loading: true,
});

export function useStoreInfo() {
  return useContext(StoreInfoContext);
}

// ---- Store Capabilities Context ----
// What the merchant configured on this sales channel: low-stock warning and
// threshold, back-in-stock alerts, reservation timeout, and which optional
// features are switched on. Fetched ONCE for the whole app — it is per sales
// channel, not per product, so a page-level fetch would be repeated work.
interface StoreCapabilitiesContextValue {
  capabilities: PublicStoreCapabilities | null;
  capabilitiesLoading: boolean;
}

// The default is a real value, not a throw: a component rendered during SSR or
// outside <StoreProvider> reads `null` and falls back to its own default rather
// than crashing the tree.
const StoreCapabilitiesContext = createContext<StoreCapabilitiesContextValue>({
  capabilities: null,
  capabilitiesLoading: true,
});

export function useStoreCapabilities() {
  return useContext(StoreCapabilitiesContext);
}

// ---- Region Context ----
// Which region prices this session. Seeded from the server (see RootLayout,
// which calls resolveRegion()) so SSR and the client agree from frame 0
// instead of the client flipping the currency after hydration.
//
// ⛔ A single-region store is a NO-OP by design. `regions.length <= 1` means
// the switcher renders nothing and `regionId` is the default the backend
// would have picked anyway. Nothing about this is conditional on a capability
// flag: there ISN'T one for regions, and a storefront that skips `regionId`
// does not hide a feature, it shows every shopper the default region's prices.
interface RegionContextValue {
  /** Active regions, default first. Empty when the store has none. */
  regions: PublicRegion[];
  /** The region in force, or null when the store has no regions. */
  region: PublicRegion | null;
  /** Spread into SDK reads: `...(regionId ? { regionId } : {})`. */
  regionId: string | undefined;
  /** Buyer country from the edge, or null. Reused by the tax estimate. */
  buyerCountry: string | null;
  /** Switch region: persists the choice and re-fetches server + client data. */
  setRegion: (regionId: string) => void;
}

const RegionContext = createContext<RegionContextValue>({
  regions: [],
  region: null,
  regionId: undefined,
  buyerCountry: null,
  setRegion: () => {},
});

export function useRegion() {
  return useContext(RegionContext);
}

// ---- Auth Context ----
interface AuthContextValue {
  isLoggedIn: boolean;
  authLoading: boolean;
  customer: CustomerProfile | null;
  login: () => Promise<void>;
  logout: () => Promise<void>;
}

const AuthContext = createContext<AuthContextValue>({
  isLoggedIn: false,
  authLoading: true,
  customer: null,
  login: async () => {},
  logout: async () => {},
});

export function useAuth() {
  return useContext(AuthContext);
}

// ---- Cart Context ----
interface CartContextValue {
  cart: Cart | null;
  cartLoading: boolean;
  refreshCart: () => Promise<void>;
  itemCount: number;
  totals: { subtotal: number; discount: number; shipping: number; total: number };
}

const CartContext = createContext<CartContextValue>({
  cart: null,
  cartLoading: true,
  refreshCart: async () => {},
  itemCount: 0,
  totals: { subtotal: 0, discount: 0, shipping: 0, total: 0 },
});

export function useCart() {
  return useContext(CartContext);
}

// ---- Provider Component ----
<% if (i18nEnabled) { %>
export function StoreProvider({
  children,
  locale,
  initialStoreInfo = null,
  initialMessages,
  initialRegions = [],
  initialRegion = null,
  initialCountry = null,
}: {
  children: React.ReactNode;
  locale?: string;
  initialStoreInfo?: PublicStoreInfo | null;
  initialMessages?: Record<string, Record<string, string>>;
  initialRegions?: PublicRegion[];
  initialRegion?: PublicRegion | null;
  initialCountry?: string | null;
}) {
<% } else { %>
export function StoreProvider({
  children,
  initialStoreInfo = null,
  initialRegions = [],
  initialRegion = null,
  initialCountry = null,
}: {
  children: React.ReactNode;
  initialStoreInfo?: PublicStoreInfo | null;
  initialRegions?: PublicRegion[];
  initialRegion?: PublicRegion | null;
  initialCountry?: string | null;
}) {
<% } %>
  // Seed from server-rendered data when available so SSR consumers (PriceDisplay,
  // FreeShippingBar, locale-aware UI, etc.) render with real values from frame 0
  // instead of falling back to USD/defaults until hydration completes.
  const [storeInfo, setStoreInfo] = useState<PublicStoreInfo | null>(initialStoreInfo);
  const [storeLoading, setStoreLoading] = useState(!initialStoreInfo);
  const [capabilities, setCapabilities] = useState<PublicStoreCapabilities | null>(null);
  const [capabilitiesLoading, setCapabilitiesLoading] = useState(true);
  const [isLoggedIn, setIsLoggedIn] = useState(false);
  const [customer, setCustomer] = useState<CustomerProfile | null>(null);
  const [authLoading, setAuthLoading] = useState(true);
  const [cart, setCart] = useState<Cart | null>(null);
  const [cartLoading, setCartLoading] = useState(true);
  const [regions, setRegions] = useState<PublicRegion[]>(initialRegions);
  const [region, setRegionState] = useState<PublicRegion | null>(initialRegion);
  const router = useRouter();
<% if (i18nEnabled) { %>
  // Seed from server-rendered messages (see RootLayout) so translated text
  // renders at frame 0 instead of a window of raw "namespace.key" strings
  // before the client-only fetch in the effect below resolves — this also
  // closes the same gap on every locale switch, since RootLayout re-runs
  // server-side per locale and StoreProvider remounts with fresh initial data.
  const [messages, setMessages] = useState<Record<string, Record<string, string>>>(
    initialMessages ?? {}
  );
<% } %>

  // Check auth status via httpOnly cookie (server-side validation)
  const refreshAuth = useCallback(async () => {
    try {
      const status = await checkAuthStatus();
      setIsLoggedIn(status.isLoggedIn);
      setCustomer(status.isLoggedIn ? (status.customer as CustomerProfile) : null);
    } catch {
      setIsLoggedIn(false);
      setCustomer(null);
    } finally {
      setAuthLoading(false);
    }
  }, []);

  // Initialize client, check auth, and fetch store info
  useEffect(() => {
    const client = initClient();
<% if (i18nEnabled) { %>

    // Set locale on SDK client for translated product content
    if (locale) {
      client.setLocale(locale);
    }

    // Load UI message strings for current locale
    getMessages(locale || defaultLocale).then(setMessages);
<% } %>

    // Optimistic check: if brainerce_logged_in cookie exists, assume logged in
    // while we validate the actual token server-side
    if (typeof document !== 'undefined' && document.cookie.includes('brainerce_logged_in=1')) {
      setIsLoggedIn(true);
    }

    // Validate auth token server-side
    refreshAuth();

    // Fetch store info only when the server didn't already provide it. The
    // happy path is that <RootLayout> SSR'd fetchStoreInfo() and seeded us via
    // initialStoreInfo; this branch is the failure fallback (server fetch
    // returned null) so user-facing components don't render forever as 'USD'.
    if (!initialStoreInfo) {
      client
        .getStoreInfo()
        .then((raw) => setStoreInfo(pickPublicStoreInfo(raw)))
        .catch(console.error)
        .finally(() => setStoreLoading(false));
    }
<% if (i18nEnabled) { %>
  }, [refreshAuth, locale, initialStoreInfo]);
<% } else { %>
  }, [refreshAuth, initialStoreInfo]);
<% } %>

  // Fetch the channel's capabilities once, on mount. Deliberately its own
  // effect with no dependencies: the payload carries no translated content, so
  // folding it into the effect above would refetch it on every locale switch
  // for nothing.
  //
  // ⛔ This must never break the store. A brand-new store is empty and this
  // call can fail (offline, a channel not yet live, a backend hiccup). On
  // failure we keep `capabilities` at null and every consumer falls back to its
  // own default, which is the behaviour the storefront had before capabilities
  // existed. Nothing here throws and nothing renders an empty box.
  useEffect(() => {
    let cancelled = false;
    getClient()
      .getStoreCapabilities()
      .then((raw) => {
        if (!cancelled) setCapabilities(pickPublicCapabilities(raw));
      })
      .catch(() => {
        // Swallowed on purpose. See above. Consumers use their defaults.
      })
      .finally(() => {
        if (!cancelled) setCapabilitiesLoading(false);
      });
    return () => {
      cancelled = true;
    };
  }, []);

  // Regions. The happy path is that RootLayout resolved them server-side and
  // seeded us, so this effect does nothing at all. It is the fallback for a
  // failed SSR resolve, and it deliberately mirrors the capabilities effect:
  //
  // ⛔ It must never break the store. An empty store, a channel that is not
  // live yet and a backend hiccup all land in the catch, leave `regions` at
  // `[]` and `region` at null, and every consumer then sends no `regionId`,
  // exactly what this storefront did before regions existed. Nothing throws,
  // and the switcher renders nothing rather than an empty dropdown.
  useEffect(() => {
    if (initialRegions.length > 0) return;
    let cancelled = false;
    getClient()
      .getStoreRegions()
      .then((result) => {
        if (cancelled) return;
        const list = Array.isArray(result?.data) ? result.data : [];
        if (list.length === 0) return;
        setRegions(list);
        // The cookie is the shopper's own choice and outranks the country
        // guess. A stale id (region deleted or deactivated) is ignored.
        const chosenId = readRegionCookie();
        const chosen = chosenId ? list.find((r) => r.id === chosenId) : undefined;
        setRegionState(chosen ?? pickRegion(list, initialCountry));
      })
      .catch(() => {
        // Swallowed on purpose. See above.
      });
    return () => {
      cancelled = true;
    };
  }, [initialRegions, initialCountry]);

  // Switching region has to move BOTH halves of the app: the cookie so the
  // next server render prices the same way, `router.refresh()` so the server
  // components already on screen re-fetch, and local state so client hooks
  // (product listing, home grid) refetch immediately instead of waiting.
  const setRegion = useCallback(
    (regionId: string) => {
      const next = regions.find((r) => r.id === regionId);
      if (!next) return;
      writeRegionCookie(regionId);
      setRegionState(next);
      router.refresh();
    },
    [regions, router]
  );

  // Cart management
  const refreshCart = useCallback(async () => {
    try {
      setCartLoading(true);
      const client = getClient();
      // The SDK owns cart identity: smartGetCart() picks the guest, session or
      // customer cart on its own. Never mirror the id into localStorage.
      const c = await client.smartGetCart();
      setCart(c);
    } catch (err) {
      console.error('Failed to load cart:', err);
    } finally {
      setCartLoading(false);
    }
  }, []);

  useEffect(() => {
    refreshCart();
  }, [refreshCart, isLoggedIn]);

  // Called after successful login (cookie already set by proxy)
  const login = useCallback(async () => {
    // Refresh auth state from server (reads httpOnly cookie)
    await refreshAuth();

    // Merge guest session cart into customer cart
    const client = getClient();
    client.syncCartOnLogin().catch(console.error);
  }, [refreshAuth]);

  const logout = useCallback(async () => {
    // Clear httpOnly cookie server-side
    await proxyLogout();

    const client = getClient();
    client.onLogout();
    setIsLoggedIn(false);
    setCustomer(null);
    setCart(null);
    refreshCart();
  }, [refreshCart]);

  const itemCount = cart
    ? cart.items.reduce((sum, item) => sum + item.quantity, 0)
    : 0;

  const totals = cart ? getCartTotals(cart) : { subtotal: 0, discount: 0, shipping: 0, total: 0 };

<% if (i18nEnabled) { %>
  return (
    <MessagesContext.Provider value={messages}>
      <StoreInfoContext.Provider value={{ storeInfo, loading: storeLoading }}>
        <StoreCapabilitiesContext.Provider value={{ capabilities, capabilitiesLoading }}>
          <RegionContext.Provider
            value={{
              regions,
              region,
              regionId: region?.id,
              buyerCountry: initialCountry,
              setRegion,
            }}
          >
            <AuthContext.Provider value={{ isLoggedIn, authLoading, customer, login, logout }}>
              <CartContext.Provider
                value={{ cart, cartLoading, refreshCart, itemCount, totals }}
              >
                {children}
              </CartContext.Provider>
            </AuthContext.Provider>
          </RegionContext.Provider>
        </StoreCapabilitiesContext.Provider>
      </StoreInfoContext.Provider>
    </MessagesContext.Provider>
  );
<% } else { %>
  return (
    <StoreInfoContext.Provider value={{ storeInfo, loading: storeLoading }}>
      <StoreCapabilitiesContext.Provider value={{ capabilities, capabilitiesLoading }}>
        <RegionContext.Provider
          value={{
            regions,
            region,
            regionId: region?.id,
            buyerCountry: initialCountry,
            setRegion,
          }}
        >
          <AuthContext.Provider value={{ isLoggedIn, authLoading, customer, login, logout }}>
            <CartContext.Provider
              value={{ cart, cartLoading, refreshCart, itemCount, totals }}
            >
              {children}
            </CartContext.Provider>
          </AuthContext.Provider>
        </RegionContext.Provider>
      </StoreCapabilitiesContext.Provider>
    </StoreInfoContext.Provider>
  );
<% } %>
}
