import ExpandMore from "@mui/icons-material/ExpandMore";
import { useEffect, useRef, useState } from "react";

export function Sidebar({ activePath, isCollapsed, items, onNavigate }) {
  const [expandedGroupId, setExpandedGroupId] = useState(() => findParentGroupId(activePath, items));
  const [flyoutGroupId, setFlyoutGroupId] = useState(null);
  const sidebarRef = useRef(null);

  useEffect(() => {
    setExpandedGroupId(findParentGroupId(activePath, items));
    setFlyoutGroupId(null);
  }, [activePath, items]);

  useEffect(() => {
    setFlyoutGroupId(null);
  }, [isCollapsed]);

  useEffect(() => {
    if (!flyoutGroupId) {
      return undefined;
    }

    const closeFlyout = (event) => {
      if (sidebarRef.current?.contains(event.target)) {
        return;
      }

      setFlyoutGroupId(null);
    };

    const closeOnEscape = (event) => {
      if (event.key === "Escape") {
        setFlyoutGroupId(null);
      }
    };

    document.addEventListener("pointerdown", closeFlyout);
    document.addEventListener("keydown", closeOnEscape);

    return () => {
      document.removeEventListener("pointerdown", closeFlyout);
      document.removeEventListener("keydown", closeOnEscape);
    };
  }, [flyoutGroupId]);

  const handleNavigate = (path) => {
    if (path) {
      onNavigate(path);
    }
    setFlyoutGroupId(null);
  };

  return (
    <aside className={`sidebar ${isCollapsed ? "sidebar--collapsed" : ""}`} ref={sidebarRef}>
      <nav className="nav-list" aria-label="主导航">
        {items.map((item) => (
          <NavGroup
            activePath={activePath}
            isExpanded={expandedGroupId === item.id}
            isCollapsed={isCollapsed}
            isFlyoutOpen={flyoutGroupId === item.id}
            item={item}
            key={item.id}
            onNavigate={handleNavigate}
            onToggleExpanded={() => setExpandedGroupId((current) => (current === item.id ? null : item.id))}
            onToggleFlyout={() => setFlyoutGroupId((current) => (current === item.id ? null : item.id))}
          />
        ))}
      </nav>
    </aside>
  );
}

function NavGroup({
  activePath,
  isCollapsed,
  isExpanded,
  isFlyoutOpen,
  item,
  onNavigate,
  onToggleExpanded,
  onToggleFlyout
}) {
  const Icon = item.icon;
  const children = item.children || [];
  const hasChildren = children.length > 0;
  const activeChildPath = findBestActiveChildPath(activePath, children);
  const isActive = isActiveRoute(activePath, item.path);
  const isChildActive = Boolean(activeChildPath);
  const isOpen = isCollapsed ? isFlyoutOpen : isExpanded;

  const handleParentClick = () => {
    if (hasChildren && isCollapsed) {
      onToggleFlyout();
      return;
    }

    if (hasChildren && children.length === 1 && children[0].path && !isChildActive) {
      onNavigate(children[0].path);
      return;
    }

    if (hasChildren) {
      onToggleExpanded();
      return;
    }

    onNavigate(item.path);
  };

  return (
    <div className="nav-group">
      <button
        aria-expanded={hasChildren ? isOpen : undefined}
        aria-haspopup={hasChildren && isCollapsed ? "menu" : undefined}
        className={[
          "nav-item",
          hasChildren ? "nav-item--parent" : "",
          isOpen ? "nav-item--expanded" : "",
          isActive ? "nav-item--active" : "",
          isChildActive || isOpen ? "nav-item--open" : "",
          isFlyoutOpen ? "nav-item--flyout-open" : ""
        ]
          .filter(Boolean)
          .join(" ")}
        type="button"
        onClick={handleParentClick}
      >
        <Icon fontSize="small" />
        <span className="nav-label">{item.label}</span>
        {hasChildren ? <ExpandMore className="nav-expand" fontSize="small" /> : null}
      </button>

      {hasChildren && !isCollapsed ? (
        <div className={`nav-children ${isOpen ? "nav-children--open" : ""}`}>
          <div className="nav-children__inner">
            {children.map((child) => {
              const ChildIcon = child.icon;

              return (
                <button
                  className={`nav-item nav-item--child ${child.path === activeChildPath ? "nav-item--active" : ""}`}
                  key={child.id}
                  type="button"
                  onClick={() => onNavigate(child.path)}
                >
                  <ChildIcon fontSize="small" />
                  <span className="nav-label">{child.label}</span>
                </button>
              );
            })}
          </div>
        </div>
      ) : null}

      {hasChildren && isCollapsed && isFlyoutOpen ? (
        <div className="nav-flyout" role="menu">
          <div className="nav-flyout__title">{item.label}</div>
          {children.map((child) => {
            const ChildIcon = child.icon;

            return (
              <button
                className={`nav-flyout__item ${child.path === activeChildPath ? "nav-flyout__item--active" : ""}`}
                key={child.id}
                role="menuitem"
                type="button"
                onClick={() => onNavigate(child.path)}
              >
                <ChildIcon fontSize="small" />
                <span>{child.label}</span>
              </button>
            );
          })}
        </div>
      ) : null}
    </div>
  );
}

function findParentGroupId(activePath, items) {
  for (const item of items) {
    if (findBestActiveChildPath(activePath, item.children || [])) {
      return item.id;
    }
  }

  return null;
}

function findBestActiveChildPath(activePath, children) {
  return children
    .map((child) => child.path)
    .filter((path) => isActiveRoute(activePath, path))
    .sort((left, right) => right.length - left.length)[0];
}

function isActiveRoute(activePath, itemPath) {
  if (!itemPath) {
    return false;
  }
  if (activePath === itemPath) {
    return true;
  }
  return activePath.startsWith(`${itemPath}/`);
}
