"use client";

import cx from "classnames";
import MiniSearch from "minisearch";
import Link from "next/link";
import { usePathname, useRouter } from "next/navigation";
import React from "react";
import packageJson from "../../../package.json";
import { TextInput } from "../Forms/TextInput";
import { Icon } from "../Icon";
import { Image } from "../Image";

export interface DocumentationSidebarItem {
  label: string;
  href?: string;
  target?: React.HTMLAttributeAnchorTarget;
  items?: DocumentationSidebarItem[];
  content?: string; // Optional: MDX content for searching
}

export interface DocumentationSidebarProps {
  items: DocumentationSidebarItem[];
  className?: string;
  /** Called when user selects a different item */
  onChangeHref?: (href: string) => void;
  /** Content to render in the main area */
  children?: React.ReactNode;
}

const CLASS_ROOT = "documentation-sidebar";

interface SearchResult {
  id: string;
  label: string;
  href: string;
}

interface IndexedItem {
  id: string;
  label: string;
  href: string;
  content: string;
}

interface MiniSearchResult {
  id: string;
  label?: string;
  href?: string;
  score?: number;
}

// Flatten and index all menu items for search
const flattenMenuItems = (
  items: DocumentationSidebarItem[],
  indexedItems: IndexedItem[] = [],
): IndexedItem[] => {
  items.forEach((item) => {
    if (item.href) {
      indexedItems.push({
        id: item.href,
        label: item.label,
        href: item.href,
        content: `${item.label} ${item.content || ""}`,
      });
    }
    if (item.items && item.items.length > 0) {
      flattenMenuItems(item.items, indexedItems);
    }
  });
  return indexedItems;
};

// Initialize MiniSearch with indexed items
const initializeSearch = (items: DocumentationSidebarItem[]) => {
  const indexedItems = flattenMenuItems(items);
  const miniSearch = new MiniSearch({
    fields: ["label", "content"],
    storeFields: ["id", "label", "href"],
  });
  miniSearch.addAll(indexedItems);
  return miniSearch;
};

// Merge search index from MDX files with menu items
const mergeSearchIndex = (
  items: DocumentationSidebarItem[],
  indexData: Array<{ href: string; content: string }>,
): DocumentationSidebarItem[] => {
  const contentMap = new Map(
    indexData.map((item) => [item.href, item.content]),
  );

  const merge = (
    items: DocumentationSidebarItem[],
  ): DocumentationSidebarItem[] => {
    return items.map((item) => {
      const mergedItem = { ...item };

      // Add content from search index if available and not already set
      if (item.href && !item.content && contentMap.has(item.href)) {
        mergedItem.content = contentMap.get(item.href);
      }

      // Recursively merge child items
      if (item.items) {
        mergedItem.items = merge(item.items);
      }

      return mergedItem;
    });
  };

  return merge(items);
};

const SidebarItem: React.FC<{
  item: DocumentationSidebarItem;
  pathname: string;
  level?: number;
  onSelect: (href: string) => void;
}> = ({ item, pathname, level = 0, onSelect }) => {
  const hasChildren = item.items && item.items.length > 0;
  const isActive = item.href === pathname;

  // Check if this item or any of its children is active
  const hasActiveChild = React.useMemo(() => {
    const checkActiveInChildren = (
      items: DocumentationSidebarItem[],
    ): boolean => {
      return items.some((child) => {
        if (child.href === pathname) return true;
        if (child.items) return checkActiveInChildren(child.items);
        return false;
      });
    };
    return hasChildren ? checkActiveInChildren(item.items!) : false;
  }, [item.items, pathname, hasChildren]);

  // Auto-expand if this is a top level item, or if it has an active child
  const [isExpanded, setIsExpanded] = React.useState(
    level === 0 || hasActiveChild,
  );

  // Update expansion state when pathname changes and this item has active children
  React.useEffect(() => {
    if (hasActiveChild) {
      setIsExpanded(true);
    }
  }, [hasActiveChild]);

  const handleToggle = () => {
    if (hasChildren) {
      setIsExpanded(!isExpanded);
    }
  };

  // Create ref for active items to enable scrolling
  const itemRef = React.useRef<HTMLLIElement>(null);

  // Scroll to active item on mount and when it becomes active
  React.useEffect(() => {
    if (isActive && itemRef.current) {
      itemRef.current.scrollIntoView({
        behavior: "smooth",
        block: "center",
      });
    }
  }, [isActive]);

  return (
    <li className={`${CLASS_ROOT}__list-item`} ref={itemRef}>
      <div
        className={cx(`${CLASS_ROOT}__item`, {
          [`${CLASS_ROOT}__item--has-children`]: hasChildren,
          [`${CLASS_ROOT}__item--expanded`]: isExpanded,
        })}
        style={{ paddingLeft: `${level * 16}px` }}
      >
        {hasChildren ? (
          <button
            type="button"
            className={cx(`${CLASS_ROOT}__dropdown-toggle`, {
              [`${CLASS_ROOT}__dropdown-toggle--active`]: isActive,
            })}
            onClick={handleToggle}
            aria-expanded={isExpanded}
          >
            {item.label}
            <span className={`${CLASS_ROOT}__dropdown-icon`}>
              {isExpanded ? "−" : "+"}
            </span>
          </button>
        ) : (
          <Link
            prefetch={false}
            href={item.href || "#"}
            target={item.target}
            rel={item.target === "_blank" ? "noopener noreferrer" : undefined}
            className={cx(`${CLASS_ROOT}__link`, {
              [`${CLASS_ROOT}__link--active`]: isActive,
            })}
            onClick={() => item.href && onSelect(item.href)}
          >
            {item.label}
          </Link>
        )}
      </div>

      {hasChildren && isExpanded && (
        <ul className={`${CLASS_ROOT}__sub-list`}>
          {item.items!.map((subItem, index) => (
            <SidebarItem
              key={`${subItem.label}-${index}`}
              item={subItem}
              pathname={pathname}
              level={level + 1}
              onSelect={onSelect}
            />
          ))}
        </ul>
      )}
    </li>
  );
};

export const DocumentationSidebar: React.FC<DocumentationSidebarProps> = (
  props,
) => {
  const { items, className, onChangeHref, children } = props;

  const [isMenuOpenOnMobile, setIsMenuOpenOnMobile] = React.useState(false);
  const [searchQuery, setSearchQuery] = React.useState("");
  const [searchResults, setSearchResults] = React.useState<SearchResult[]>([]);
  const [activeSearchIndex, setActiveSearchIndex] = React.useState(0);
  const [miniSearch, setMiniSearch] = React.useState<MiniSearch | null>(null);
  const pathname = usePathname();
  const router = useRouter();
  const sidebarRef = React.useRef<HTMLElement>(null);
  const searchInputRef = React.useRef<HTMLInputElement>(null);

  // Initialize search on mount
  React.useEffect(() => {
    const search = initializeSearch(items);
    setMiniSearch(search);

    // Load additional content from search index if available
    const loadSearchIndex = async () => {
      try {
        const response = await fetch("/search-index.json");
        if (!response.ok) throw new Error("Failed to fetch search index");
        const indexData = await response.json();

        // Re-initialize search with both menu items and indexed MDX content
        const mergedItems = mergeSearchIndex(items, indexData);
        const updatedSearch = initializeSearch(mergedItems);
        setMiniSearch(updatedSearch);
      } catch (_err) {
        // Search index not found or fetch failed, continue with menu items only
        console.debug("Search index not available, using menu items only");
      }
    };

    loadSearchIndex();
  }, [items]);

  // Handle search input
  const handleSearch = (query: string) => {
    setSearchQuery(query);
    if (query.trim() && miniSearch) {
      const results = miniSearch.search(query, {
        boost: { label: 10 },
        fuzzy: (term) => {
          // Allow 1 character difference for short terms, more for longer terms
          return term.length > 3 ? 0.2 : 0.3;
        },
        prefix: true, // Enable prefix matching (search "text" matches "text-secondary")
        combineWith: "AND", // Require all terms to match
      }) as unknown as SearchResult[];
      setSearchResults(results.slice(0, 10)); // Limit to 10 results
      setActiveSearchIndex(0); // Reset active index when results change
    } else {
      setSearchResults([]);
      setActiveSearchIndex(0);
    }
  };

  React.useEffect(() => {
    const handleKeyDown = (event: KeyboardEvent) => {
      // Handle Cmd+K or Ctrl+K to focus search
      if ((event.metaKey || event.ctrlKey) && event.key === "k") {
        event.preventDefault();
        searchInputRef.current?.focus();
        return;
      }

      // Handle Escape key
      if (event.key === "Escape") {
        setIsMenuOpenOnMobile(false);
        setSearchQuery("");
        setSearchResults([]);
        setActiveSearchIndex(0);
      }
    };
    document.addEventListener("keydown", handleKeyDown);
    return () => document.removeEventListener("keydown", handleKeyDown);
  }, []);

  // Scroll to active item when pathname changes or component mounts
  React.useEffect(() => {
    if (sidebarRef.current) {
      const activeLink = sidebarRef.current.querySelector(
        `.${CLASS_ROOT}__link--active`,
      );
      if (activeLink) {
        activeLink.scrollIntoView({
          behavior: "smooth",
          block: "center",
        });
      }
    }
  }, [pathname]);

  const handleSelect = (href: string) => {
    onChangeHref?.(href);
    setIsMenuOpenOnMobile(false);
    setSearchQuery("");
    setSearchResults([]);
    setActiveSearchIndex(0);
  };

  // Handle keyboard navigation in search results
  const handleSearchKeyDown = (
    event: React.KeyboardEvent<HTMLInputElement>,
  ) => {
    if (searchResults.length === 0) return;

    switch (event.key) {
      case "ArrowDown":
        event.preventDefault();
        setActiveSearchIndex((prev) =>
          prev < searchResults.length - 1 ? prev + 1 : prev,
        );
        break;
      case "ArrowUp":
        event.preventDefault();
        setActiveSearchIndex((prev) => (prev > 0 ? prev - 1 : 0));
        break;
      case "Enter":
        event.preventDefault();
        if (searchResults[activeSearchIndex]) {
          const href = searchResults[activeSearchIndex].href;
          searchInputRef.current?.blur();
          router.push(href);
          handleSelect(href);
        }
        break;
    }
  };

  const classes = cx(CLASS_ROOT, className, {
    [`${CLASS_ROOT}--open`]: isMenuOpenOnMobile,
  });

  return (
    <div className={classes}>
      <button
        type="button"
        className={`${CLASS_ROOT}__toggle`}
        aria-expanded={isMenuOpenOnMobile}
        aria-controls={`${CLASS_ROOT}__sidebar`}
        onClick={() => setIsMenuOpenOnMobile((v) => !v)}
      >
        <Icon name="menu" />
      </button>

      <aside
        id={`${CLASS_ROOT}__sidebar`}
        className={`${CLASS_ROOT}__sidebar`}
        aria-label="Sidebar navigation"
        ref={sidebarRef}
      >
        <Image src="/logo.svg" alt="logo" />

        <div className="mb">
          <TextInput
            id="search-documentation"
            htmlType="search"
            placeholder="CTRL+K to search..."
            value={searchQuery}
            onChange={(e) => handleSearch(e.currentTarget.value)}
            onKeyDown={handleSearchKeyDown}
            width="fullwidth"
            aria-label="Search documentation"
            ref={searchInputRef}
          />

          {searchResults.length > 0 && (
            <div className={`${CLASS_ROOT}__search-results`}>
              <ul className={`${CLASS_ROOT}__search-list`}>
                {searchResults.map((result, index) => (
                  <li key={result.id}>
                    <Link
                      href={result.href}
                      className={cx(`${CLASS_ROOT}__search-result-link`, {
                        [`${CLASS_ROOT}__search-result-link--active`]:
                          index === activeSearchIndex,
                      })}
                      onClick={() => handleSelect(result.href)}
                    >
                      {result.label}
                    </Link>
                  </li>
                ))}
              </ul>
            </div>
          )}
        </div>

        <nav
          className={`${CLASS_ROOT}__nav`}
          style={{ display: searchResults.length > 0 ? "none" : "block" }}
        >
          <ul className={`${CLASS_ROOT}__list`}>
            {items.map((item, index) => (
              <SidebarItem
                key={`${item.label}-${index}`}
                item={item}
                pathname={pathname}
                onSelect={handleSelect}
              />
            ))}
          </ul>
        </nav>

        <div className={`${CLASS_ROOT}__version`}>
          <Link href="/changelog" className={`${CLASS_ROOT}__link`}>
            v{packageJson.version}
          </Link>
        </div>
      </aside>

      <main className={`${CLASS_ROOT}__content`}>{children}</main>

      {isMenuOpenOnMobile && (
        <button
          type="button"
          aria-label="Close navigation"
          className={`${CLASS_ROOT}__backdrop`}
          onClick={() => setIsMenuOpenOnMobile(false)}
        />
      )}
    </div>
  );
};

DocumentationSidebar.displayName = "DocumentationSidebar";
