'use client';

import {Avatar} from '@astryxdesign/core/Avatar';
import {Divider} from '@astryxdesign/core/Divider';
import {Icon} from '@astryxdesign/core/Icon';
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 {
  ACCOUNT_PANELS,
  ACCOUNT_ROOT_GROUPS,
  LOCALE_OPTIONS,
  isBranch,
  type AccountBranch,
  type AccountLeaf,
  type AccountPanelId,
  type LocaleOption,
} from './account-menu';

/**
 * The two panels, with no opinion about how they float.
 *
 * Everything here is content: the cascade — anchoring, dismissal, which panel
 * is open — belongs to AccountMenu, and the mobile sheet reuses these same two
 * components without a second code path.
 */

/**
 * The panel skin, shared by both panels so they read as one system.
 *
 * The popover layer underneath is deliberately surface-less (see AccountMenu),
 * so this is the ONLY thing painting a background: two separate cards with a
 * gap of nothing between them, which is what makes the second panel read as a
 * second panel rather than a wider first one.
 *
 * Every value is a token: bg-popover IS --color-background-popover,
 * border-border IS --color-border, rounded-xl IS --radius-page (16px, the
 * largest radius the theme defines), shadow-lg IS --shadow-high.
 */
export const PANEL_SURFACE =
  'bg-popover border border-border rounded-xl shadow-lg';

/** Panel one. Wide enough for an email address at 14px without truncating. */
export const PANEL_ONE_WIDTH = 288;
/** Panel two. Narrower — its longest row is "Roles & Permissions". */
export const PANEL_TWO_WIDTH = 264;

const ROW = 'rounded-lg transition-colors duration-150 cursor-pointer';

/**
 * The one row allowed semantic colour, and only on hover — the monochrome rule
 * keeps the resting state gray like every other row.
 *
 * `hover:text-error` alone reaches the glyph as well as the label: Item's label
 * span carries no colour of its own and the icon is passed `color="inherit"`,
 * so both inherit the row's.
 */
const ROW_DANGER = `${ROW} hover:text-error`;

/**
 * Panel two's entrance: 8px of travel from the right plus a fade, over 180ms.
 *
 * `starting:` is what makes this animate at all. The panel is newly INSERTED
 * into an already-open popover — there is no display change to transition
 * from, so without an @starting-style rule the browser has no "before" value
 * and the panel would simply appear. Keying the element on the panel id (see
 * AccountMenu) re-runs it when the merchant moves between branches.
 */
export const PANEL_TWO_MOTION = [
  'transition-[opacity,translate] duration-200 ease-out',
  'opacity-100 translate-x-0 starting:opacity-0 starting:translate-x-2',
].join(' ');

// =============================================================================
// Identity
// =============================================================================

/** Avatar, name, email. The only part of the menu that is not a list. */
export function AccountIdentity() {
  const {user} = useSession();

  return (
    <HStack gap={3} vAlign="center" paddingInline={2} paddingBlock={2}>
      {/* The avatar draws its own initials from the name; no data-URI portrait
          to keep in sync, and a signed-out visit falls back to Avatar's own
          person glyph rather than someone else's letter. */}
      <Avatar name={user?.name || undefined} size="lg" tooltip={false} />
      {/* min-w-0 so a long address truncates instead of widening the panel and
          pushing the chevrons out of view. */}
      <VStack gap={0.5} className="min-w-0">
        <Text weight="medium" maxLines={1}>
          {user?.name ?? 'Not signed in'}
        </Text>
        <Text type="supporting" size="sm" maxLines={1}>
          {user?.email ?? 'Sign in to sync your workspace'}
        </Text>
      </VStack>
    </HStack>
  );
}

// =============================================================================
// Panel one
// =============================================================================

interface RootPanelProps {
  /** Which branch is showing panel two, so its row can stay lit. */
  activePanel: AccountPanelId | null;
  /**
   * `viaKeyboard` is how the cascade knows whether to move focus into panel
   * two. See AccountMenu — a mouse user is already looking at it.
   */
  onBranch: (panel: AccountPanelId, viaKeyboard: boolean) => void;
  onLeaf: (leaf: AccountLeaf) => void;
}

/**
 * Identity, four branches, sign-out.
 *
 * A branch row NEVER navigates — it only asks for panel two. That is the whole
 * behavioural difference from the dropdown this replaces, and it is enforced
 * by the config's types rather than by remembering to check here.
 */
export function AccountRootPanel({
  activePanel,
  onBranch,
  onLeaf,
}: RootPanelProps) {
  const {isAuthenticated} = useSession();

  return (
    <VStack gap={2} width="100%">
      <AccountIdentity />

      {ACCOUNT_ROOT_GROUPS.map((group, index) => (
        <VStack gap={0.5} key={group[0]?.label ?? index} width="100%">
          <Divider />
          {group.map((row) =>
            isBranch(row) ? (
              <BranchRow
                key={row.label}
                branch={row}
                isActive={activePanel === row.panel}
                onSelect={onBranch}
              />
            ) : (
              <LeafRow
                key={row.label}
                // Signed out — nothing to end, so the row offers the way in
                // instead. The handler is already right: it clears an absent
                // session and routes to /login either way. Only the wording
                // and the danger colour would have lied.
                leaf={
                  row.action === 'signOut' && !isAuthenticated
                    ? {...row, label: 'Log in', tone: undefined}
                    : row
                }
                onSelect={onLeaf}
              />
            ),
          )}
        </VStack>
      ))}
    </VStack>
  );
}

// =============================================================================
// Panel two
// =============================================================================

interface SubPanelProps {
  panel: AccountPanelId;
  /** Display-only: there is no locale provider yet. See account-menu.ts. */
  locale: LocaleOption['code'];
  onLocale: (code: LocaleOption['code']) => void;
  onLeaf: (leaf: AccountLeaf) => void;
}

/**
 * The panel that opens beside panel one. Every row in it is a destination —
 * this is the level at which clicking finally does something.
 */
export function AccountSubPanel({
  panel,
  locale,
  onLocale,
  onLeaf,
}: SubPanelProps) {
  const {title, rows} = ACCOUNT_PANELS[panel];
  const isLanguage = panel === 'language';

  return (
    <VStack gap={0.5} width="100%">
      <HStack paddingInline={2} paddingBlock={1.5}>
        <Text type="supporting" size="sm" weight="medium">
          {title}
        </Text>
      </HStack>

      {isLanguage ? (
        <>
          {LOCALE_OPTIONS.map((option) => (
            <Item
              key={option.code}
              label={option.label}
              density="balanced"
              className={ROW}
              isSelected={option.code === locale}
              // The check is the only end content: a locale row goes nowhere,
              // so a chevron would promise a panel three that does not exist.
              endContent={
                option.code === locale ? (
                  <Icon icon="check" size="sm" color="inherit" />
                ) : undefined
              }
              onClick={() => onLocale(option.code)}
            />
          ))}
          <Divider />
        </>
      ) : null}

      {rows.map((leaf) => (
        <LeafRow key={leaf.label} leaf={leaf} onSelect={onLeaf} />
      ))}
    </VStack>
  );
}

// =============================================================================
// Rows
// =============================================================================

function BranchRow({
  branch,
  isActive,
  onSelect,
}: {
  branch: AccountBranch;
  isActive: boolean;
  onSelect: (panel: AccountPanelId, viaKeyboard: boolean) => void;
}) {
  return (
    <Item
      label={branch.label}
      description={branch.description}
      density="balanced"
      className={ROW}
      // Lit while its panel is open, so the pair reads as one selection rather
      // than two unrelated surfaces that happen to be adjacent.
      isSelected={isActive}
      startContent={<Icon icon={branch.icon} color="inherit" />}
      endContent={<Icon icon="chevronRight" size="sm" color="secondary" />}
      // detail === 0 is a click the browser synthesised from Enter or Space —
      // the one reliable signal, on a plain click handler, that no pointer was
      // involved.
      onClick={(event) => onSelect(branch.panel, event.detail === 0)}
    />
  );
}

function LeafRow({
  leaf,
  onSelect,
}: {
  leaf: AccountLeaf;
  onSelect: (leaf: AccountLeaf) => void;
}) {
  const isExternal = !!leaf.externalHref;

  return (
    <Item
      label={leaf.label}
      description={leaf.description}
      density="balanced"
      className={leaf.tone === 'danger' ? ROW_DANGER : ROW}
      startContent={
        leaf.icon ? <Icon icon={leaf.icon} color="inherit" /> : undefined
      }
      // The outbound glyph so a new tab is not a surprise. Internal rows get
      // nothing: inside panel two every row already navigates, so a chevron
      // on each of them would be decoration.
      endContent={
        isExternal ? (
          <Icon icon="externalLink" size="sm" color="secondary" />
        ) : undefined
      }
      // External rows stay real links so middle-click and "open in new tab"
      // behave; everything else routes through the handler so the menu closes
      // before the destination lands.
      href={leaf.externalHref}
      target={isExternal ? '_blank' : undefined}
      onClick={isExternal ? undefined : () => onSelect(leaf)}
    />
  );
}
