import type { CSSProperties } from "react";
import { cookies, headers } from "next/headers";
import { apiFetch } from "@/lib/api";
import { auth } from "@/lib/auth";
import { formatDomain } from "@/lib/format-domain";
import { requireSession } from "@/lib/session";
import { siteListSchema } from "@/lib/sites";
import { AppSidebar } from "@/components/app/app-sidebar";
import type { SwitcherSite } from "@/components/app/site-switcher";
import { SiteHeader } from "@/components/app/site-header";
import { JobsToaster } from "@/components/jobs/jobs-toaster";
import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar";
import { Toaster } from "@/components/ui/sonner";

// Variables du block `dashboard-01` : largeur de la sidebar et hauteur de l'en-tête.
const SHELL_VARS = {
  "--sidebar-width": "calc(var(--spacing) * 68)",
  "--header-height": "calc(var(--spacing) * 12)",
} as CSSProperties;

// Première page de sites pour le switcher (limite api par défaut : 25). Le shell doit
// survivre à une api indisponible : toute erreur retombe sur une liste vide plutôt que
// de casser le layout de chaque page authentifiée.
async function loadSwitcherSites(): Promise<SwitcherSite[]> {
  try {
    const { items } = siteListSchema.parse(await apiFetch<unknown>("/sites"));
    // `formatDomain` s'appuie sur `node:url` : la conversion reste ici, côté serveur.
    return items.map((site) => ({
      id: site.id,
      name: site.name,
      displayDomain: formatDomain(site.domain),
    }));
  } catch (error) {
    // Sidebar en repli plutôt qu'app cassée, mais l'incident doit se voir dans les logs serveur.
    console.error("site-switcher: chargement des sites impossible", error);
    return [];
  }
}

export default async function AppLayout({ children }: { children: React.ReactNode }) {
  const { user, session } = await requireSession();
  // Sans organisation active (parcours d'onboarding) : ni organisation ni sites à charger,
  // l'api refuserait de toute façon un JWT sans `org_id`.
  const orgId = session.activeOrganizationId;
  const [org, sites] = await Promise.all([
    orgId ? auth.api.getFullOrganization({ headers: await headers() }) : null,
    orgId ? loadSwitcherSites() : [],
  ]);
  // La primitive écrit `sidebar_state` à chaque bascule : le layout le relit pour que
  // l'état replié survive au rechargement, sans flash de ré-ouverture (pattern shadcn).
  const sidebarState = (await cookies()).get("sidebar_state")?.value;
  return (
    <SidebarProvider style={SHELL_VARS} defaultOpen={sidebarState !== "false"}>
      <AppSidebar
        user={{ name: user.name, email: user.email }}
        orgName={org?.name ?? null}
        sites={sites}
      />
      {/* `min-w-0` — la ligne qui empêche la **fenêtre** de scroller horizontalement, et donc
          la seule qui vaille pour toutes les pages à la fois. `SidebarInset` est un item du
          flex de `SidebarProvider` : sans elle, son `min-width` vaut `auto`, c'est-à-dire la
          largeur minimale de son contenu, et une table large repousse l'inset au-delà du
          viewport — le bouton d'action et le sélecteur de cible se retrouvent hors écran
          (constaté en test réel sur l'écran Keywords). Avec `min-w-0`, l'inset se laisse
          comprimer et chaque zone gère son propre débordement : la table dans son conteneur
          `overflow-x-auto`, les barres de filtres en se repliant. */}
      <SidebarInset className="min-w-0">
        <SiteHeader />
        {/* `SidebarInset` rend déjà le <main> : un simple <div> ici, pas de landmark en double. */}
        <div className="flex min-w-0 flex-1 flex-col gap-4 p-4 lg:p-6">{children}</div>
      </SidebarInset>
      {/* Les notifications vivent dans le layout, jamais dans une page : c'est ce qui fait
          qu'un import lancé depuis le wizard reste visible après avoir navigué ailleurs.
          Sans organisation active (onboarding), il n'y a aucun travail à sonder. */}
      <Toaster position="top-right" />
      {orgId ? <JobsToaster /> : null}
    </SidebarProvider>
  );
}
