/**
 * 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, useMatch, useResolvedPath } from 'react-router-dom';
import { useIdentity } from '@/lib/identity';
import { NAV_GROUP_HEADING, NAV_GROUPS, visibleNav, type NavItem } from '@/lib/nav';
import { AccountSwitcher } from './AccountSwitcher';
import { Button } 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();

  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">
            <span className="text-[10px] font-semibold uppercase tracking-[0.16em] text-muted">
              Navigate
            </span>
            <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} />
                    ))}
                  </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>
  );
}

function NavItemRow({ item }: { item: NavItem }) {
  const { setOpenMobile, isMobile } = useSidebar();
  // `asChild` renders the row *as* the link rather than wrapping one, so there
  // is a single focusable element per row. Active state is asked of the router
  // instead of compared against a pathname, so `/demand/abc` still lights
  // Demand and `/` does not light everything.
  const resolved = useResolvedPath(item.to);
  const isActive = useMatch({ path: resolved.pathname, end: item.to === '/' }) !== null;

  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>
  );
}
