import * as React from "react"
import { Outlet, useLocation } from "react-router"

import {
  AppSidebar,
  SidebarShell,
  SecondaryPanelProvider,
  SecondaryPanel,
} from "@/components/sidebar"
import { isExamLockPath } from "@/lib/exam-lock-shell"
import {
  SIDEBAR_STATE_COOKIE_NAME,
  sidebarDefaultOpenFromCookie,
} from "@/lib/sidebar-state-cookie"
import { ThemeProvider } from "@/components/theme-provider"
import { TooltipProvider } from "@/components/ui/tooltip"
import { ProductProviderRoot } from "@/components/product-provider-root"
import {
  ProductRouteSync,
  useProductDashboardHref,
  useProductOrganizationSettingsHref,
} from "@/contexts/product-route-sync"
import { ProductSwitchOverlay } from "@/components/product-switch-overlay"
import { ThemeColorSync } from "@/components/theme-color-sync"
import { DashboardViewProvider } from "@/contexts/dashboard-view-context"
import { ChartVariantProvider } from "@/contexts/chart-variant-context"
import { AskLeoProvider } from "@/components/ask-leo-context"
import { LeoAmbienceProvider } from "@/components/leo-ambience-context"
import { KeyMetricsAskLeoBridge } from "@/components/key-metrics-ask-leo-bridge"
import { SystemBannerProvider } from "@/contexts/system-banner-context"
import { SystemBannerSlot } from "@/components/system-banner-slot"
import { UtilityBarSlot } from "@/components/utility-bar-slot"
import {
  isFullWidthUtilityBar,
  ShellLayoutProvider,
  showsUtilityBar,
  useShellLayout,
} from "@/contexts/shell-layout-context"
import { CommandMenu } from "@/components/command-menu"
import { CommandMenuProvider } from "@/contexts/command-menu-context"
import { buildCommandMenuConfig, type CommandMenuConfig } from "@/lib/command-menu-config"
import { COMMAND_MENU_SEARCH_DATA_GROUPS } from "@/lib/command-menu-search-data"
import {
  shouldShowOnboardingSidebarPreview,
  useBuilderOnboardingChrome,
} from "@/lib/builder-onboarding-chrome"
import {
  AskLeoShell,
  useAskLeoDockedPanelOpen,
} from "@/components/ask-leo-shell"
import { isProductsHomePath } from "@/lib/product-home"
import { cn } from "@/lib/utils"

/**
 * Vite-side root layout — replaces both `app/layout.tsx` (Next root) and
 * `app/(app)/layout.tsx` (Next signed-in app shell).
 *
 * Cookie reading: Next reads `cookies()` server-side. In Vite/SPA we read
 * `document.cookie` synchronously on first render — the API is identical
 * (default `true` if absent, otherwise the persisted state). No
 * hydration mismatch because there is no server render to mismatch
 * against.
 *
 * Provider order is preserved verbatim from the Next layout — these
 * contexts are tightly coupled to DS components (KeyMetrics ↔ Ask Leo,
 * SecondaryPanel ↔ Sidebar) and reorder would break runtime behaviour.
 *
 * The `<Outlet />` in the centre is where each route's element renders.
 */

function readCookie(name: string): string | undefined {
  if (typeof document === "undefined") return undefined
  const match = document.cookie
    .split("; ")
    .find(c => c.startsWith(`${name}=`))
  return match ? decodeURIComponent(match.split("=")[1]) : undefined
}

function patchOrganizationSettingsHref(
  config: CommandMenuConfig,
  organizationSettingsHref: string,
): CommandMenuConfig {
  return {
    ...config,
    groups: config.groups.map(group => ({
      ...group,
      items: group.items?.map(item =>
        item.id === "nav-settings-organization"
          ? { ...item, href: organizationSettingsHref }
          : item,
      ),
    })),
  }
}

function AppShellLayout({ sidebarDefaultOpen }: { sidebarDefaultOpen: boolean }) {
  const { pathname } = useLocation()
  // Docked panel only — floating window must not reflow main (padding / flex).
  // Which shell mounts is still `AskLeoShell`'s call.
  const askLeoDockedPanelOpen = useAskLeoDockedPanelOpen()
  const { variant: shellLayoutVariant } = useShellLayout()
  const examLock = isExamLockPath(pathname)
  const onboardingSidebarPreview = useBuilderOnboardingChrome(shouldShowOnboardingSidebarPreview)
  // The products home has no active product, so a product sidebar would be
  // navigating somewhere the user has not chosen yet. Same reasoning as
  // onboarding: keep the utility bar (identity, search, sign-out) and drop the
  // product nav.
  const productsHome = isProductsHomePath(pathname)
  const hidePrimarySidebar =
    productsHome ||
    (pathname.startsWith("/builder/onboarding") && !onboardingSidebarPreview)
  // "utility-bar" layout variant: the bar moves out of the workspace column
  // and becomes a full-width row above the sidebar+workspace row (see
  // UtilityBarSlot's doc comment for how the sidebar accommodates this).
  const fullWidthUtilityBar = !examLock && isFullWidthUtilityBar(shellLayoutVariant)
  const mountUtilityBar = !examLock && showsUtilityBar(shellLayoutVariant)

  return (
    <SidebarShell
      defaultOpen={sidebarDefaultOpen}
      wrapperClassName="flex h-full min-h-0 w-full flex-1 flex-col overflow-hidden"
      data-shell-utility-bar-full={fullWidthUtilityBar ? "true" : undefined}
    >
      {!examLock ? <CommandMenu /> : null}
      {fullWidthUtilityBar ? <UtilityBarSlot /> : null}
      {examLock ? (
        <SecondaryPanelProvider>
          <div className="flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden bg-sidebar">
            <Outlet />
          </div>
        </SecondaryPanelProvider>
      ) : (
        <div
          className="relative z-40 flex min-h-0 w-full min-w-0 flex-1 items-stretch overflow-visible has-data-[variant=inset]:bg-sidebar"
          data-app-shell-row=""
          data-ask-leo-open={askLeoDockedPanelOpen ? "true" : undefined}
          suppressHydrationWarning
        >
          <SecondaryPanelProvider>
            {!hidePrimarySidebar ? <AppSidebar variant="inset" /> : null}
            <div
              data-app-shell-workspace=""
              className={cn(
                "relative z-40 flex min-h-0 min-w-0 flex-1 flex-col overflow-visible pb-1.5 md:pb-2",
                fullWidthUtilityBar ? "pt-0" : "pt-1.5 md:pt-2",
              )}
            >
              {/* The products home carries its own Featured banner, so the
                  workspace promo strip would stack a second pitch above it. */}
              {!examLock && !productsHome ? <SystemBannerSlot /> : null}
              {mountUtilityBar && !fullWidthUtilityBar ? <UtilityBarSlot /> : null}
              <div
                data-app-shell-workspace-body=""
                className="relative z-40 flex min-h-0 min-w-0 flex-1 items-stretch overflow-visible"
              >
                <SecondaryPanel />
                <div
                  data-app-shell-main=""
                  className="relative z-30 flex min-h-0 min-w-0 flex-1 flex-col overflow-visible"
                >
                  <div className="flex min-h-0 min-w-0 flex-1 flex-col overflow-visible">
                    <Outlet />
                  </div>
                </div>
                {/* Panel, window, or FAB — `AskLeoShell` owns that decision. */}
                <AskLeoShell />
              </div>
            </div>
          </SecondaryPanelProvider>
        </div>
      )}
    </SidebarShell>
  )
}

function AppShell({ sidebarDefaultOpen }: { sidebarDefaultOpen: boolean }) {
  const dashboardHref = useProductDashboardHref()
  const libraryHref = dashboardHref.replace(/\/dashboard$/, "/library")
  const organizationSettingsHref = useProductOrganizationSettingsHref()
  const commandMenuConfig = React.useMemo(
    () =>
      patchOrganizationSettingsHref(
        patchCommandMenuHrefs(
          buildCommandMenuConfig({ dataGroups: COMMAND_MENU_SEARCH_DATA_GROUPS }),
          {
            "nav-dashboard": dashboardHref,
            "nav-library": libraryHref,
            "nav-library-all": `${libraryHref}/all`,
          },
        ),
        organizationSettingsHref,
      ),
    [dashboardHref, libraryHref, organizationSettingsHref],
  )

  return (
    <>
      <ProductRouteSync />
      <ProductSwitchOverlay />
      <ThemeColorSync />
      <TooltipProvider delayDuration={300}>
        <DashboardViewProvider>
          <ChartVariantProvider>
            <AskLeoProvider>
              <LeoAmbienceProvider>
                <KeyMetricsAskLeoBridge>
                  <SystemBannerProvider>
                    <ShellLayoutProvider>
                      <CommandMenuProvider value={commandMenuConfig}>
                        <AppShellLayout sidebarDefaultOpen={sidebarDefaultOpen} />
                      </CommandMenuProvider>
                    </ShellLayoutProvider>
                  </SystemBannerProvider>
                </KeyMetricsAskLeoBridge>
              </LeoAmbienceProvider>
            </AskLeoProvider>
          </ChartVariantProvider>
        </DashboardViewProvider>
      </TooltipProvider>
    </>
  )
}

function patchCommandMenuHrefs(
  config: CommandMenuConfig,
  hrefs: Record<string, string>,
): CommandMenuConfig {
  return {
    ...config,
    groups: config.groups.map(group => ({
      ...group,
      items: group.items?.map(item =>
        item.id in hrefs ? { ...item, href: hrefs[item.id] } : item,
      ),
    })),
  }
}

export function App() {
  const sidebarDefaultOpen = sidebarDefaultOpenFromCookie(
    readCookie(SIDEBAR_STATE_COOKIE_NAME),
  )

  return (
    <ThemeProvider
      attribute="class"
      defaultTheme="system"
      enableSystem
      disableTransitionOnChange
    >
      <ProductProviderRoot>
        <div className="flex h-full min-h-0 w-full flex-1 flex-col overflow-hidden">
          <AppShell sidebarDefaultOpen={sidebarDefaultOpen} />
        </div>
      </ProductProviderRoot>
    </ThemeProvider>
  )
}
