import clsx from "clsx";
import {
  FC,
  HTMLAttributes,
  ReactNode,
  useEffect,
  useRef,
  useState,
} from "react";

import ChevronDown from "@dndbeyond/fontawesome-cache/svgs/solid/chevron-down.svg";

import { Popover } from "../Popover";
import styles from "./styles.module.css";

/**
 * This component renders a set of tabs with associated content panels.
 * It supports different variants for tab behavior, including default,
 * collapse, and toggle modes. Tabs can be limited in number of visible
 * items, with overflow handled via a "More" dropdown menu.
 *
 ** Variants:
 * - default: Standard tab behavior where clicking a tab shows its content.
 * - collapse: Always has a selected tab, but clicking it again collapses the content.
 * - fullwidth: Tabs stretch to fill the container width.
 */

interface TabItem {
  label: ReactNode;
  content: ReactNode;
  className?: string;
  id: string;
}

interface TabListProps extends HTMLAttributes<HTMLElement> {
  tabs: Array<TabItem | null>;
  variant?: "default" | "collapse" | "fullwidth";
  defaultActiveId?: string;
  maxNavItemsShow?: number;
  onChangeTab?: (id: string) => void;
  themed?: boolean;
  forceShow?: boolean;
}

export const TabList: FC<TabListProps> = ({
  className,
  tabs,
  defaultActiveId,
  maxNavItemsShow = 7,
  variant = "default",
  onChangeTab,
  themed,
  forceShow,
  ...props
}) => {
  const tabListRef = useRef<HTMLDivElement>(null!);
  const [activeTab, setActiveTab] = useState(defaultActiveId || tabs[0]?.id);
  const [isTabVisible, setIsTabVisible] = useState(
    forceShow ?? activeTab !== ""
  );

  const tabsWithContent = tabs.filter((tab) => tab);
  const visibleTabs = tabsWithContent.slice(0, maxNavItemsShow);
  const hiddenTabs = tabsWithContent.slice(maxNavItemsShow);
  const selectedTab = tabs.find((tab) => tab?.id === activeTab);
  const isToggleable = variant === "collapse";

  const handleTabClick = (id: string) => {
    if (id === activeTab && variant === "collapse") {
      setIsTabVisible(!isTabVisible);
    }

    if (id !== activeTab) {
      setActiveTab(id);
      setIsTabVisible(true);

      if (onChangeTab) {
        onChangeTab(id);
      }
    }
  };

  useEffect(
    () => {
      // Update CSS variable for number of tabs
      const tabListStyles = tabListRef.current?.style;
      if (tabListStyles) {
        tabListStyles.setProperty("--tab-count", tabs.length.toString());
        tabListStyles.setProperty(
          "--tab-active-position",
          tabs.indexOf(selectedTab as TabItem).toString()
        );
      }
    },
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [tabs.length, selectedTab]
  );

  return (
    <div
      className={clsx([
        styles.tabList,
        styles[variant],
        themed && styles.themed,
        className,
      ])}
      ref={tabListRef}
      {...props}
    >
      <menu className={styles.tabs}>
        {/* List of visible tabs */}
        {visibleTabs.map((tab: TabItem) => (
          <li key={tab.id}>
            <button
              className={styles.tabButton}
              role="radio"
              aria-checked={activeTab === tab.id}
              onClick={() => handleTabClick(tab.id)}
              data-testid={tab.id}
            >
              {tab.label}
              {isToggleable && (
                <ChevronDown
                  className={clsx([!isTabVisible && styles.closed])}
                />
              )}
            </button>
          </li>
        ))}

        {/* Toggle and menu for hidden tabs */}
        {hiddenTabs.length > 0 && (
          <li>
            <Popover
              className={styles.morePopover}
              variant="menu"
              trigger={<button className={styles.tabButton}>More</button>}
            >
              <menu className={styles.moreMenu}>
                {hiddenTabs.map((tab: TabItem) => (
                  <li key={tab.id}>
                    <button
                      className={styles.tabButton}
                      role="radio"
                      aria-checked={activeTab === tab.id}
                      onClick={() => handleTabClick(tab.id)}
                    >
                      {tab.label}
                    </button>
                  </li>
                ))}
              </menu>
            </Popover>
          </li>
        )}
      </menu>

      {/* Content of the active tab */}
      <div className={clsx([styles.tabContent, selectedTab?.className])}>
        {isTabVisible && activeTab !== "" && selectedTab?.content}
      </div>
    </div>
  );
};
