import {
  AppShell,
  Box,
  Group,
  NavLink,
  ScrollArea,
  Stack,
  Text,
  UnstyledButton,
} from "@mantine/core";
import { ChevronDown, X } from "lucide-react";
import {
  type ReactNode,
  useCallback,
  useEffect,
  useMemo,
  useState,
} from "react";

import type { SidebarItem, SidebarSection } from "./types";
import { Link } from "react-router-dom";

export interface FreightSidebarProps {
  sections: SidebarSection[];
  activeHref?: string;
  onNavigate?: (href: string) => void;
  /** Close handler for the mobile drawer (X button, hidden on desktop). */
  onClose?: () => void;
}

const BRAND_LOGO = "/assets/logo.svg";

// Active / inactive NavLink styling, expressed through the shared edr-* theme
// tokens (bridged into Tailwind in index.css). Items are pills floating on the
// page background — the navbar itself has no surface of its own.
const navClassNames = (active: boolean) =>
  active
    ? {
      root: "rounded-md transition-all py-1.5! duration-150 bg-edr-soft! ring-1 ring-inset ring-edr-primary/40 [&_svg]:size-[16px]",
      label: "text-edr-primary-dark! font-medium! text-sm!",
      section: "text-edr-primary-dark!",
    }
    : {
      root: "rounded-md transition-all py-1.5! duration-150 hover:bg-[#EEF2F6]! [&_svg]:size-4",
      label: "text-edr-text! font-medium! text-sm! hover:text-edr-ink!",
      section: "text-edr-text!",
    };

const itemKey = (parentKey: string, item: SidebarItem, index: number) =>
  `${parentKey}/${item.href ?? item.label}/${index}`;

const collectHrefs = (items: SidebarItem[]): string[] =>
  items.flatMap((item) => [
    ...(item.href ? [item.href.toLowerCase()] : []),
    ...(item.children?.length ? collectHrefs(item.children) : []),
  ]);

const FreightSidebar = ({
  sections,
  activeHref,
  onNavigate,
  onClose,
}: FreightSidebarProps) => {
  const activePath = activeHref?.toLowerCase() ?? "";

  const isHrefActive = useCallback(
    (href: string) => {
      const normalized = href.toLowerCase();
      return (
        activePath === normalized || activePath.startsWith(`${normalized}/`)
      );
    },
    [activePath],
  );

  const branchActive = useCallback(
    (items: SidebarItem[]) => collectHrefs(items).some(isHrefActive),
    [isHrefActive],
  );

  // Branches containing the active route start expanded; manual toggles win
  // afterwards (merge keeps user intent while still opening newly-active paths).
  const defaultOpen = useMemo(() => {
    const acc: Record<string, boolean> = {};
    const walk = (items: SidebarItem[], parentKey: string) => {
      items.forEach((item, i) => {
        if (!item.children?.length) return;
        const key = itemKey(parentKey, item, i);
        acc[key] =
          (item.href ? isHrefActive(item.href) : false) ||
          branchActive(item.children);
        walk(item.children, key);
      });
    };
    sections.forEach((section) => walk(section.items, section.title));
    return acc;
  }, [sections, isHrefActive, branchActive]);

  const [openMap, setOpenMap] = useState(defaultOpen);
  useEffect(() => {
    setOpenMap((current) => ({ ...defaultOpen, ...current }));
  }, [defaultOpen]);

  const toggle = useCallback(
    (key: string) => setOpenMap((m) => ({ ...m, [key]: !m[key] })),
    [],
  );

  const renderItem = useCallback(
    (item: SidebarItem, key: string): ReactNode => {
      const hasChildren = !!item.children?.length;

      if (hasChildren) {
        const isLink = !!item.href;
        const active = isLink ? isHrefActive(item.href!) : false;
        const isOpen = openMap[key] ?? false;

        return (
          // `opened` is controlled so a link-parent navigates on row click
          // without collapsing; the chevron is the only toggle affordance.
          <NavLink
            key={key}
            label={item.label}
            leftSection={item.icon}
            active={active}
            opened={isOpen}
            classNames={navClassNames(active)}
            onClick={() => toggle(key)}
            rightSection={
              <Box
                component="span"
                role="button"
                aria-label={isOpen ? "Collapse section" : "Expand section"}
                onClick={(e) => {
                  e.preventDefault();
                  e.stopPropagation();
                  toggle(key);
                }}
                className="flex cursor-pointer items-center"
              >
                <ChevronDown
                  size={16}
                  className="text-edr-muted transition-transform duration-200"
                  style={{
                    transform: isOpen ? "rotate(-180deg)" : "rotate(180deg)",
                  }}
                />
              </Box>
            }
          >
            {item.children!.map((child, i) =>
              renderItem(child, itemKey(key, child, i)),
            )}
          </NavLink>
        );
      }

      if (!item.href) return null;
      const active = isHrefActive(item.href);

      return (
        <NavLink
          key={key}
          label={item.label}
          leftSection={item.icon}
          active={active}
          component={Link}
          classNames={navClassNames(active)}
          to={item.href!}
        />
      );
    },
    [branchActive, isHrefActive, onNavigate, openMap, toggle],
  );

  const renderedSections = useMemo(
    () =>
      sections.map((section) => (
        <Box key={section.title}>
          <Text
            size="xs"
            tt="uppercase"
            px="sm"
            mb={6}
            className={"text-edr-muted!"}
            style={{ fontWeight: 500, fontSize: 10, letterSpacing: "0.05em" }}
          >
            {section.title}
          </Text>
          <Stack gap={2}>
            {section.items.map((item, i) =>
              renderItem(item, itemKey(section.title, item, i)),
            )}
          </Stack>
        </Box>
      )),
    [renderItem, sections],
  );

  return (
    <AppShell.Navbar
      withBorder={false}
      // White surface + hairline right border, matching the portal AppLayout.
      // Inline styles beat Mantine's cascade layer (where a Tailwind bg-* would
      // lose to the navbar's default --mantine-color-body).
      style={{
        backgroundColor: "var(--mantine-color-edr-card-6)",
        borderRight: "1px solid var(--mantine-color-edr-border-6)",
      }}
    >
      {/* Brand — aligns with the 64px header for a continuous top edge */}
      <Box className="flex h-16 shrink-0 items-center justify-between px-5">
        <Group gap={10} wrap="nowrap">
          <img
            src={BRAND_LOGO}
            alt="EDR Freight"
            className="size-8 shrink-0 object-contain"
          />
          <Box>
            <Text
              className="text-edr-primary! leading-tight"
              fw={700}
              fz={15}
              style={{ letterSpacing: "-0.01em" }}
            >
              EDR Freight
            </Text>
            <Text
              className="text-edr-muted!"
              fz={10}
              fw={500}
              style={{ letterSpacing: "0.02em" }}
            >
              Backoffice Console
            </Text>
          </Box>
        </Group>
        {onClose && (
          <UnstyledButton
            onClick={onClose}
            hiddenFrom="sm"
            aria-label="Close sidebar"
          >
            <X size={18} className="text-edr-muted" strokeWidth={1.8} />
          </UnstyledButton>
        )}
      </Box>

      {/* Nav */}
      <AppShell.Section
        grow
        component={ScrollArea}
        type="hover"
        scrollbars="y"
        scrollbarSize={6}
        scrollHideDelay={500}
        px="sm"
        pb="md"
      >
        <Stack gap="lg">{renderedSections}</Stack>
      </AppShell.Section>
    </AppShell.Navbar>
  );
};

export default FreightSidebar;
