'use client';

import {AppShell} from '@astryxdesign/core/AppShell';
import {
  Layout,
  LayoutContent,
  LayoutHeader,
  LayoutPanel,
  VStack,
} from '@astryxdesign/core/Layout';
import {AppSideNav} from '@/components/shell/AppSideNav';
import {AppTopNav} from '@/components/shell/AppTopNav';
import {MobileMenu} from '@/components/shell/MobileMenu';
import {SidebarProvider, useSidebar} from '@/components/shell/SidebarProvider';
import {CopilotPanel} from '@/features/copilot/CopilotPanel';
import {CopilotSlideOver} from '@/features/copilot/CopilotSlideOver';
import {useCopilot} from '@/features/copilot/CopilotProvider';
import {
  useBreakpoint,
  isPanelInline,
  isSideNavInline,
  copilotWidth,
  contentMaxWidth,
} from '@/lib/breakpoints';

/**
 * The three-column shell, and the reason the Copilot 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, the Copilot — is untouched.
 *
 * Structure, and why AppShell alone is not enough: AppShell renders
 * Layout{header, start, content} internally and exposes no end/panel slot. The
 * Copilot column therefore comes from a NESTED Layout inside its children.
 *
 *   AppShell(sideNav)
 *     └─ Layout(header = TopNav, content = page, end = LayoutPanel > Copilot)
 *
 * 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 default function WorkspaceLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <SidebarProvider>
      <WorkspaceShell>{children}</WorkspaceShell>
    </SidebarProvider>
  );
}

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

  // 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();

  // 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"
      // Layout/LayoutContent own the padding so the Copilot panel can sit
      // flush against the workspace edge.
      contentPadding={0}
      topNav={sideNavInline ? undefined : <AppTopNav />}
      sideNav={<AppSideNav />}
      // 'sm' (640px) matches the mobile/tablet line in lib/breakpoints —
      // AppShell's own threshold and ours have to agree or the hamburger and
      // the rail are both visible, or neither is.
      mobileNav={{
        breakpoint: 'sm',
        isOpen: isDrawerOpen,
        onOpenChange: setDrawerOpen,
        // hasToggle: false — AppShell would otherwise inject a SECOND
        // hamburger into TopNav's mobile bar, next to the one in `heading`
        // that works at every width. See AppTopNav.
        hasToggle: false,
        content: <MobileMenu />,
      }}
    >
      <Layout
        height="fill"
        header={
          sideNavInline ? (
            <LayoutHeader padding={0}>
              <AppTopNav />
            </LayoutHeader>
          ) : undefined
        }
        content={
          <LayoutContent padding={5}>
            {/*
              Above 1920 the workspace is capped and centred. Uncapped, a
              30-day line chart stretches across ~1900px, which flattens every
              trend it exists to show, and body copy runs well past the ~90ch
              where reading breaks down. Below the cap this is a no-op —
              maxWidth only binds once there is more width than content wants.
            */}
            <VStack
              width="100%"
              maxWidth={contentMaxWidth(bp)}
              className={bp === 'ultrawide' ? 'mx-auto' : undefined}
            >
              {children}
            </VStack>
          </LayoutContent>
        }
        end={
          showInlinePanel ? (
            <LayoutPanel
              hasDivider
              width={copilotWidth(bp)}
              // CopilotPanel supplies its own header and tab chrome.
              padding={0}
              role="complementary"
              label="Loyaly AI"
            >
              <CopilotPanel />
            </LayoutPanel>
          ) : undefined
        }
      />
      {/* Mounted only below the inline breakpoint. Because every piece of
          Copilot state lives in CopilotProvider (above the route tree), moving
          between these two presentations is lossless. */}
      {!inline ? <CopilotSlideOver /> : null}
    </AppShell>
  );
}
