/**
 * The left navigation pane.
 *
 * One component for both treatments the sidebar primitive provides: the
 * collapsible desktop rail and the phone Sheet. Deliberately not two, because
 * the previous shell had the desktop list and the tab bar as separate JSX and
 * they had already drifted — the tab bar's active pill and the sidebar's
 * active row used different tokens.
 */
import { Fragment } from 'react';
import { X } from 'lucide-react';
import { Link, useLocation } from 'react-router-dom';
import { useIdentity } from '@/lib/identity';
import { activeNavItem, NAV_GROUP_HEADING, NAV_GROUPS, visibleNav, type NavItem } from '@/lib/nav';
import { AccountSwitcher } from './AccountSwitcher';
import { Button, Label } from './ui';
import {
  Sidebar,
  SidebarContent,
  SidebarGroup,
  SidebarGroupContent,
  SidebarGroupLabel,
  SidebarHeader,
  SidebarMenu,
  SidebarMenuButton,
  SidebarMenuItem,
  SidebarRail,
  SidebarSeparator,
  useSidebar,
} from './ui/sidebar';

export function AppSidebar() {
  const identity = useIdentity();
  const items = visibleNav(identity);
  const { isMobile, setOpenMobile } = useSidebar();
  const { pathname } = useLocation();
  /*
   * One winner for the whole rail, decided here rather than by each row asking
   * the router about itself. Motion is the first group whose destinations nest
   * — `/motion` is a prefix of `/motion/library` — and a per-row match lit both
   * of those at once, so the sidebar and the header disagreed about which page
   * you were on. `activeNavItem` is the same longest-match helper the header
   * titles with, which is what keeps them from ever disagreeing again.
   */
  const current = activeNavItem(items, pathname);

  return (
    <Sidebar collapsible="icon">
      <SidebarHeader>
        {/* The Sheet's own close button is suppressed because it lands on top
            of the account switcher. This one replaces it — Escape and the
            overlay work, but a visible close is not optional on a touch
            device where neither is discoverable. */}
        {isMobile ? (
          <div className="flex items-center justify-between pl-2">
            {/* Same spec as every other group heading in the rail — this one
                had its own copy of the retired 10px/600/0.16em values. */}
            <Label>Navigate</Label>
            <Button
              type="button"
              variant="ghost"
              size="icon"
              className="text-muted"
              aria-label="Close navigation"
              onClick={() => setOpenMobile(false)}
            >
              <X className="size-5" aria-hidden />
            </Button>
          </div>
        ) : null}
        <AccountSwitcher />
      </SidebarHeader>
      <SidebarSeparator />
      <SidebarContent>
        {NAV_GROUPS.map((group) => {
          const groupItems = items.filter((item) => item.group === group);
          // A heading over nothing is worse than a missing section: it reads
          // as a section that failed to load rather than one you cannot use.
          if (!groupItems.length) return null;
          const heading = NAV_GROUP_HEADING[group];
          return (
            <Fragment key={group}>
              <SidebarGroup>
                {heading ? <SidebarGroupLabel>{heading}</SidebarGroupLabel> : null}
                <SidebarGroupContent>
                  <SidebarMenu>
                    {groupItems.map((item) => (
                      <NavItemRow key={item.to} item={item} isActive={current?.to === item.to} />
                    ))}
                  </SidebarMenu>
                </SidebarGroupContent>
              </SidebarGroup>
              {/*
                An unlabelled group has no heading to separate it from the next
                one, so it gets a rule instead. This is also the only separation
                that survives collapse: at icon width every heading is pulled up
                and faded out, so without the rule the front door would be just
                one more glyph in an undifferentiated stack of them.
              */}
              {heading === null ? <SidebarSeparator /> : null}
            </Fragment>
          );
        })}
      </SidebarContent>
      {/*
        No footer. The old shell ended with "Prime Intellect Growth / Compute
        revenue system", which the account switcher at the top now says
        better — and at 900px the fourteen nav rows do not all fit, so a
        restatement of the workspace name was costing two of them.
      */}
      <SidebarRail />
    </Sidebar>
  );
}

// `asChild` renders the row *as* the link rather than wrapping one, so there is
// a single focusable element per row. Active state arrives as a prop because it
// is a question about the whole table — see the caller.
function NavItemRow({ item, isActive }: { item: NavItem; isActive: boolean }) {
  const { setOpenMobile, isMobile } = useSidebar();

  return (
    <SidebarMenuItem>
      <SidebarMenuButton asChild isActive={isActive} tooltip={item.label}>
        <Link
          to={item.to}
          aria-current={isActive ? 'page' : undefined}
          onClick={() => {
            if (isMobile) setOpenMobile(false);
          }}
        >
          <item.icon aria-hidden />
          <span>{item.label}</span>
        </Link>
      </SidebarMenuButton>
    </SidebarMenuItem>
  );
}
