'use client';

import {useCallback, useState} from 'react';
import {Avatar} from '@astryxdesign/core/Avatar';
import {Dialog} from '@astryxdesign/core/Dialog';
import {Icon} from '@astryxdesign/core/Icon';
import {IconButton} from '@astryxdesign/core/IconButton';
import {Item} from '@astryxdesign/core/Item';
import {HStack, VStack} from '@astryxdesign/core/Layout';
import {Text} from '@astryxdesign/core/Text';
import {useSession} from '@/features/auth/providers/SessionProvider';
import {ICONS} from '@/shared/utils/icons';
import {ACCOUNT_PANELS, type AccountPanelId} from './account-menu';
import {useAccountMenu} from './AccountMenuProvider';
import {
  AccountRootPanel,
  AccountSubPanel,
  PANEL_TWO_MOTION,
} from './AccountPanels';
import {useSidebar} from './SidebarProvider';
import {useAccountActions} from './useAccountActions';

/**
 * The same menu, on a screen too narrow to put two panels side by side.
 *
 * Below the rail breakpoint the cascade becomes a drill-down: panel two slides
 * in over panel one with a back button, which is the phone form of the same
 * idea — a branch still opens a panel instead of navigating, and nothing routes
 * until a leaf is chosen. The rows are literally the same components; only the
 * container differs.
 *
 * ── Why it is mounted at the shell ────────────────────────────────────────
 * Its trigger lives inside MobileNav's `<dialog>`. A second `<dialog>` nested
 * in the first cannot show once the first closes, and the drawer MUST close —
 * a sheet stacked on the drawer it came from is two overlays deep. So the
 * trigger only asks (AccountMenuProvider) and the sheet is mounted beside the
 * shell, out of the drawer's subtree entirely.
 */

/** Bottom sheet: full width, pinned to the bottom, top corners only. */
const SHEET_SURFACE = [
  'rounded-t-xl rounded-b-none border-t border-border bg-popover',
  // max-w-none because a native <dialog> ships a UA max-width of
  // `calc(100% - 6px - 2em)` — measured, that left a 39px gutter down the
  // right-hand side of a sheet that is supposed to span the viewport.
  'max-w-none',
  // 48px rows on touch. Item's balanced density lands at 36px, which is a
  // pointer target. min-, not fixed height, so rows carrying a subtitle keep
  // the 56px they already need.
  '[&_.astryx-item]:min-h-12',
  'transition-[opacity,translate] duration-200 ease-out',
  'opacity-100 translate-y-0 starting:opacity-0 starting:translate-y-full',
].join(' ');

const CHIP = 'rounded-lg cursor-pointer';

/**
 * The drawer's account chip. It closes the drawer and asks for the sheet —
 * see the mounting note above for why it cannot open one itself.
 */
export function AccountSheetTrigger() {
  const {user} = useSession();
  const {closeDrawer} = useSidebar();
  const {openSheet} = useAccountMenu();

  return (
    <Item
      label={user?.name ?? 'Account'}
      description={user?.email ?? 'Not signed in'}
      labelLines={1}
      descriptionLines={1}
      density="balanced"
      className={CHIP}
      startContent={
        <Avatar name={user?.name || undefined} size="sm" tooltip={false} />
      }
      endContent={<Icon icon={ICONS.switcher} size="sm" color="secondary" />}
      onClick={() => {
        closeDrawer();
        openSheet();
      }}
      data-testid="account-sheet-trigger"
    />
  );
}

export function AccountSheet() {
  const {isSheetOpen, closeSheet, locale, setLocale} = useAccountMenu();
  const [activePanel, setActivePanel] = useState<AccountPanelId | null>(null);

  // Every way out of the sheet goes through here, so a re-open always starts
  // at panel one. Written as one handler rather than an effect watching
  // `isSheetOpen`: the reset belongs to the act of closing, not to a later
  // render that notices the sheet has closed.
  const dismiss = useCallback(() => {
    setActivePanel(null);
    closeSheet();
  }, [closeSheet]);

  const handleLeaf = useAccountActions(dismiss);
  const back = useCallback(() => setActivePanel(null), []);

  return (
    <Dialog
      isOpen={isSheetOpen}
      onOpenChange={(open) => (open ? undefined : dismiss())}
      // info: backdrop click and Escape both dismiss, and both close the whole
      // menu rather than stepping back a level — the same contract the popover
      // has on the desktop.
      purpose="info"
      width="100%"
      maxHeight="85vh"
      position={{bottom: 0, left: 0, right: 0}}
      padding={4}
      className={SHEET_SURFACE}
      aria-label="Account menu"
    >
      {/* overflow-y-auto matters here, where eleven rows plus a header can
          outgrow an 85vh cap on a short phone. overscroll-contain stops a
          flick at the end of the list from scrolling the page underneath. */}
      <VStack
        gap={2}
        width="100%"
        className="overflow-y-auto overscroll-contain"
      >
        {activePanel ? (
          <VStack gap={2} width="100%" className={PANEL_TWO_MOTION}>
            <HStack gap={2} vAlign="center">
              <IconButton
                variant="ghost"
                label="Back"
                icon={<Icon icon="chevronLeft" />}
                onClick={back}
              />
              <Text weight="medium">{ACCOUNT_PANELS[activePanel].title}</Text>
            </HStack>
            <AccountSubPanel
              panel={activePanel}
              locale={locale}
              onLocale={setLocale}
              onLeaf={handleLeaf}
            />
          </VStack>
        ) : (
          <AccountRootPanel
            activePanel={null}
            onBranch={(panel) => setActivePanel(panel)}
            onLeaf={handleLeaf}
          />
        )}
      </VStack>
    </Dialog>
  );
}
