import "./globals.css";
import type { Metadata, Viewport } from "next";
import { unstable_cache } from "next/cache";
import { appConfig } from "@/config/app.config";
import { Geist_Mono, Inter } from "next/font/google";
import { AppProviders } from "@/providers/app-providers";
import type { InitialAppSettings } from "@/providers/app-settings-provider";
import { connectDB } from "@/lib/db";
import { getSettings } from "@/models/settings.model";
import { resolveShareSettings } from "@/lib/share-config";
import {
  getStorefrontIcons,
  getStorefrontMetadataSettings,
} from "@/lib/storefront-metadata";
import {
  DEFAULT_ACCENT_COLOR,
  DEFAULT_CURRENCY,
  DEFAULT_PRIMARY_COLOR,
  DEFAULT_SECONDARY_COLOR,
  DEFAULT_STORE_NAME,
  normalizeThemeMode,
  resolveFaviconUrl,
} from "@/config/branding.config";
import { CACHE_TAGS } from "@/lib/cache-invalidation";
import { buildCustomColorVars } from "@/lib/appearance-colors";
import { normalizeCountryAvailability } from "@/lib/country-availability";

const geistMono = Geist_Mono({
  variable: "--font-geist-mono",
  subsets: ["latin"],
});

const inter = Inter({
  variable: "--font-inter",
  subsets: ["latin"],
});

// Resolved per request so the app identity — name and icon — is the store's,
// never this app's. Icons are omitted entirely when no favicon is configured:
// a `<link rel="icon">` pointing at a missing file would send browsers and
// link-preview crawlers back to `/favicon.ico`.
export async function generateMetadata(): Promise<Metadata> {
  const { storeName, storeDescription, faviconUrl } =
    await getStorefrontMetadataSettings();

  return {
    title: {
      default: storeName,
      template: `%s | ${storeName}`,
    },
    description: storeDescription || appConfig.description,
    manifest: "/manifest.webmanifest",
    applicationName: storeName,
    appleWebApp: {
      capable: true,
      statusBarStyle: "default",
      title: storeName,
    },
    formatDetection: {
      telephone: false,
    },
    icons: getStorefrontIcons(faviconUrl),
  };
}

export const viewport: Viewport = {
  themeColor: "#111111",
  // Light only. Declaring "light dark" would let the UA render native widgets
  // (scrollbars, form controls, the pre-paint canvas) from the OS preference
  // before hydration; the app never follows the OS. ThemeProvider raises
  // `style.color-scheme: dark` on <html> when dark is explicitly chosen, which
  // outranks this declaration.
  colorScheme: "light",
};

const getInitialAppSettings = unstable_cache(
  async (): Promise<InitialAppSettings> => {
    try {
      await connectDB();
      const settings = await getSettings();
      const general = settings.general;
      const appearance = settings.appearance;

      return {
        isMultiVendor: Boolean(settings.multiVendorMode?.enabled),
        isLoading: false,
        posEnabled: Boolean(settings.pos?.enabled),
        storeName:
          typeof general?.storeName === "string" && general.storeName.trim()
            ? general.storeName.trim()
            : DEFAULT_STORE_NAME,
        storeDescription: general?.storeDescription || undefined,
        storeEmail: general?.storeEmail || undefined,
        storePhone: general?.storePhone || undefined,
        storeAddress: general?.storeAddress || undefined,
        defaultCurrency: general?.defaultCurrency || DEFAULT_CURRENCY,
        defaultLanguage: general?.defaultLanguage || undefined,
        supportedLanguages: Array.isArray(general?.supportedLanguages)
          ? general.supportedLanguages
          : [],
        countryAvailability: normalizeCountryAvailability(
          general?.countryAvailability,
        ),
        logoUrl: general?.logoUrl || undefined,
        darkModeLogoUrl: general?.darkModeLogoUrl || undefined,
        faviconUrl: resolveFaviconUrl(general?.faviconUrl),
        socialLinks: {
          facebookUrl: settings.social?.facebookUrl || undefined,
          twitterUrl: settings.social?.twitterUrl || undefined,
          instagramUrl: settings.social?.instagramUrl || undefined,
          youtubeUrl: settings.social?.youtubeUrl || undefined,
          linkedinUrl: settings.social?.linkedinUrl || undefined,
          tiktokUrl: settings.social?.tiktokUrl || undefined,
        },
        shareSettings: resolveShareSettings(settings.social?.share),
        appearance: {
          themeMode: normalizeThemeMode(appearance?.theme),
          contrast: Boolean(appearance?.contrast),
          rtl: Boolean(appearance?.rtl),
          collapsedSidebar: Boolean(appearance?.collapsedSidebar),
          navLayout: appearance?.navLayout || "mini",
          navColor: appearance?.navColor || "integrate",
          presetColor: appearance?.presetColor || "default",
          primaryColor: appearance?.primaryColor || DEFAULT_PRIMARY_COLOR,
          secondaryColor: appearance?.secondaryColor || DEFAULT_SECONDARY_COLOR,
          accentColor: appearance?.accentColor || DEFAULT_ACCENT_COLOR,
        },
      };
    } catch (error) {
      if (process.env.NODE_ENV !== "production") {
        console.error("Failed to preload app settings:", error);
      }

      return {
        isLoading: false,
        storeName: DEFAULT_STORE_NAME,
        defaultCurrency: DEFAULT_CURRENCY,
      };
    }
  },
  ["initial-app-settings"],
  {
    revalidate: 60,
    tags: [CACHE_TAGS.settings],
  },
);

export default async function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  const initialSettings = await getInitialAppSettings();

  // Inline the configured brand colors on <html> so the very first paint uses
  // them — without this the stylesheet defaults flash until the client-side
  // settings applier runs after hydration. The applier keeps runtime edits live.
  const appearance = initialSettings.appearance;
  const customColorVars = buildCustomColorVars({
    primary: appearance?.primaryColor ?? DEFAULT_PRIMARY_COLOR,
    secondary: appearance?.secondaryColor ?? DEFAULT_SECONDARY_COLOR,
    accent: appearance?.accentColor ?? DEFAULT_ACCENT_COLOR,
  });

  return (
    <html
      lang="en"
      suppressHydrationWarning
      style={customColorVars as React.CSSProperties}
    >
      <body
        className={`${inter.className} ${inter.variable} ${geistMono.variable} antialiased`}
      >
        <AppProviders initialSettings={initialSettings}>
          {children}
        </AppProviders>
      </body>
    </html>
  );
}
