'use client';

import {AppShell} from '@astryxdesign/core/AppShell';
import {
  Layout,
  LayoutContent,
  LayoutHeader,
  LayoutPanel,
  VStack,
} from '@astryxdesign/core/Layout';
import {AccountDialogs} from '@/shared/layouts/workspace/AccountDialogs';
import {AccountMenuProvider} from '@/shared/layouts/workspace/AccountMenuProvider';
import {AccountSheet} from '@/shared/layouts/workspace/AccountSheet';
import {AppSideNav} from '@/shared/layouts/workspace/AppSideNav';
import {AppTopNav} from '@/shared/layouts/workspace/AppTopNav';
import {MobileMenu} from '@/shared/layouts/workspace/MobileMenu';
import {SidebarProvider, useSidebar} from '@/shared/layouts/workspace/SidebarProvider';
import {useWorkspaceShortcuts} from '@/shared/layouts/workspace/useWorkspaceShortcuts';
import {LoyalyAiPanel} from '@/features/loyaly-ai/components/LoyalyAiPanel';
import {LoyalyAiSlideOver} from '@/features/loyaly-ai/components/LoyalyAiSlideOver';
import {useLoyalyAi} from '@/features/loyaly-ai/providers/LoyalyAiProvider';
import {
  useBreakpoint,
  isPanelInline,
  isSideNavInline,
  assistantWidth,
  contentMaxWidth,
} from '@/shared/hooks/useBreakpoint';

/**
 * The three-column shell, and the reason Loyaly AI survives navigation.
 *
 * Next's App Router preserves a layout's element identity across sibling route
 * changes, so /dashboard → /staff swaps only {children}. Everything else here —
 * nav, scroll position, Loyaly AI — is untouched.
 *
 * Structure, and why AppShell alone is not enough: AppShell renders
 * Layout{header, start, content} internally and exposes no end/panel slot. The
 * Loyaly AI column therefore comes from a NESTED Layout inside its children.
 *
 *   AppShell(sideNav)
 *     └─ Layout(header = TopNav, content = page, end = LayoutPanel > Loyaly AI)
 *
 * The top nav is deliberately NOT passed to AppShell's `topNav` slot. That slot
 * renders Layout{header} at the shell root, which spans the full viewport width
 * and pushes the sidebar — and therefore the branding — 48px down the screen.
 * Measured in the browser, the logo sat at y=64 with the top-left 260x48 region
 * empty. Moving the bar into the content column's own header starts the sidebar
 * at y=0, so the brand anchors in the window's top-left corner and the bar
 * begins where the content does. This is the arrangement Linear, Cursor and
 * Stripe use, and no amount of padding inside the sidebar could produce it.
 *
 * The one thing given up: with no `topNav`, AppShell drops the --radius-page
 * corner it draws where the top bar meets the sidebar. The content area still
 * paints elevated --color-background-surface (#0F0F10) against the #000 rails,
 * and with a full-height sidebar that corner had nothing left to round.
 */
export function WorkspaceShell({children}: {children: React.ReactNode}) {
  return (
    <SidebarProvider>
      {/* Above ShellFrame because the account menu's sheet and its two help
          dialogs are mounted at the shell, out of the nav drawer's subtree —
          see AccountMenuProvider. */}
      <AccountMenuProvider>
        <ShellFrame>{children}</ShellFrame>
      </AccountMenuProvider>
    </SidebarProvider>
  );
}

/**
 * Split from WorkspaceShell only so it can READ the sidebar context that
 * WorkspaceShell mounts — a provider's own component sits above its value.
 */
function ShellFrame({children}: {children: React.ReactNode}) {
  const bp = useBreakpoint();
  const {isOpen, setIsOpen, panelMode, panelWidth} = useLoyalyAi();
  const inline = isPanelInline(bp);
  const showInlinePanel = inline && isOpen;

  const isFullscreen = showInlinePanel && panelMode === 'fullscreen';
  const isExpanded = showInlinePanel && panelMode === 'expanded';

  let currentWidth: number | string = assistantWidth(bp);
  if (showInlinePanel) {
    if (isFullscreen) {
      currentWidth = '100%';
    } else if (isExpanded) {
      currentWidth = Math.max(panelWidth, 800);
    } else {
      currentWidth = panelWidth;
    }
  }

  // Both flags come from SidebarProvider now. AppShell keeps a copy of the
  // open state for its own context (MobileNav reads it for aria wiring), but
  // ours is the one that decides — uncontrolled, there would be no way to
  // dismiss the drawer when a nav item is selected.
  const {isDrawerOpen, setDrawerOpen} = useSidebar();

  // Mounted once, here, so the bindings exist on every route and there is
  // nowhere for two copies of them to disagree. See shortcuts.ts.
  useWorkspaceShortcuts();

  // Below the breakpoint the rail is gone — it lives in the drawer — so there
  // is no top-left corner to anchor the brand to and nothing for the bar to
  // start after. Hand the top nav back to AppShell there. Keeping it in the
  // content column instead pushed the SideNav into AppShell's
  // `autoMobileTopBar` fallback, which crops the lockup into a 48px bar.
  const sideNavInline = isSideNavInline(bp);

  return (
    <AppShell
      variant="elevated"
      height="fill"
      contentPadding={0}
      topNav={sideNavInline ? undefined : <AppTopNav />}
      sideNav={<AppSideNav />}
      mobileNav={{
        breakpoint: 'sm',
        isOpen: isDrawerOpen,
        onOpenChange: setDrawerOpen,
        hasToggle: false,
        content: <MobileMenu />,
      }}
    >
      <Layout
        height="fill"
        header={
          sideNavInline ? (
            <LayoutHeader padding={0}>
              <AppTopNav />
            </LayoutHeader>
          ) : undefined
        }
        content={
          !isFullscreen ? (
            <LayoutContent padding={5}>
              <VStack
                width="100%"
                maxWidth={contentMaxWidth(bp)}
                className={bp === 'ultrawide' ? 'mx-auto' : undefined}
              >
                {children}
              </VStack>
            </LayoutContent>
          ) : undefined
        }
        end={
          showInlinePanel ? (
            <LayoutPanel
              hasDivider
              width={currentWidth as any}
              padding={0}
              role="complementary"
              label="Loyaly AI"
              className="transition-[width] duration-300 ease-out"
            >
              <LoyalyAiPanel onClose={() => setIsOpen(false)} />
            </LayoutPanel>
          ) : undefined
        }
      />
      {!inline ? <LoyalyAiSlideOver /> : null}

      <AccountSheet />
      <AccountDialogs />
    </AppShell>
  );
}
