import React, {
  ReactElement,
  ReactNode,
  useRef,
  useState,
  useEffect,
  KeyboardEvent,
  MouseEvent,
} from 'react';
import css from '../../utils/css';

import { TabWrapper, TabList, Tab, TabTitleIcon, TabPanel } from './StyledTabs';
import Icon, { IconName } from '../Icon';
import { focusTab, getTabVariant } from './utils';
import { fromUndefinedable, getOrElse, map } from '../../fp/Option';
import { pipe } from '../../fp/function';

import SubTabs from './SubTabs';

import { CommonProps } from '../common';

interface TabsProps extends Omit<CommonProps, 'id'> {
  /**
   * Unique identifier for Tabs container. This will be combined with the id of each Tab child to generate ARIA accessibility attributes.
   */
  id: string;
  /*
   * onChange event handler receiving id of upcoming active Tab.
   */
  onChange: (tabId: string | number) => void;
  /**
   * Whether inactive tab panels should be removed from the DOM and unmounted in React.
   */
  renderActiveTabPanelOnly?: boolean;
  /**
   * Current selected tab id.
   */
  selectedTabId: string | number;
  /**
   * List of Tab to be rendered. Each Tab must have an unquie id.
   */
  tabs: {
    disabled?: boolean;
    icon?: IconName | ReactElement;
    id: string | number;
    panel: ReactNode;
    title: string;
  }[];
}

const tabPrefix = 'tabs';

const Tabs = ({
  renderActiveTabPanelOnly = false,
  tabs,
  selectedTabId,
  onChange,
  id,
  className,
  style,
  sx = {},
  'data-test-id': dataTestId,
}: TabsProps): ReactElement => {
  const tabRefs = useRef<Array<HTMLButtonElement | null>>([]);

  const [focusTabIndex, setFocusTabIndex] = useState<number>(0);

  useEffect(() => {
    setFocusTabIndex(tabs.findIndex(tab => tab.id === selectedTabId));
  }, [selectedTabId, tabs]);

  useEffect(() => {
    tabRefs.current = tabRefs.current.slice(0, tabs.length);
  }, [tabs]);

  const onTabListKeyDown = (e: KeyboardEvent): void => {
    // Move right
    if (e.keyCode === 39) {
      const nextIndex = focusTabIndex + 1;
      const newFocusIndex = nextIndex >= tabs.length ? 0 : nextIndex;
      setFocusTabIndex(newFocusIndex);
      focusTab(tabRefs.current[newFocusIndex]);
    }

    // Move left
    if (e.keyCode === 37) {
      const nextIndex = focusTabIndex - 1;
      const newFocusIndex = nextIndex < 0 ? tabs.length - 1 : nextIndex;
      setFocusTabIndex(newFocusIndex);
      focusTab(tabRefs.current[newFocusIndex]);
    }
  };

  return (
    <TabWrapper
      id={id}
      className={className}
      style={{ ...style, ...css(sx) }}
      data-test-id={dataTestId}
    >
      <TabList role="tablist" onKeyDown={onTabListKeyDown} tabIndex={-1}>
        {tabs.map((tab, index) => {
          const active = selectedTabId === tab.id;
          const variant = getTabVariant({
            active,
            disabled: tab.disabled !== undefined ? tab.disabled : false,
          });
          return (
            <Tab
              key={tab.id}
              type="button"
              role="tab"
              id={`${tabPrefix}-${id}-tab-${tab.id}`}
              disabled={tab.disabled}
              aria-selected={active}
              aria-controls={`${tabPrefix}-${id}-panel-${tab.id}`}
              tabIndex={pipe(
                tabs[focusTabIndex],
                fromUndefinedable,
                map(focusedTab => (focusedTab.id === tab.id ? 0 : -1)),
                getOrElse(() => -1)
              )}
              ref={(el: HTMLButtonElement): void => {
                tabRefs.current[index] = el;
              }}
              onClick={(e: MouseEvent): void => {
                onChange(tab.id);
                e.preventDefault();
              }}
              themeVariant={variant}
            >
              {tab.icon !== undefined && (
                <TabTitleIcon>
                  <Icon icon={tab.icon} />
                </TabTitleIcon>
              )}
              {tab.title}
            </Tab>
          );
        })}
      </TabList>
      {tabs.map(tab => {
        const hidden = selectedTabId !== tab.id;
        return hidden === false || renderActiveTabPanelOnly === false ? (
          <TabPanel
            key={tab.id}
            role="tabpanel"
            id={`${tabPrefix}-${id}-panel-${tab.id}`}
            aria-labelledby={`${tabPrefix}-${id}-tab-${tab.id}`}
            hidden={selectedTabId !== tab.id}
          >
            {tab.panel}
          </TabPanel>
        ) : null;
      })}
    </TabWrapper>
  );
};

Tabs.Sub = SubTabs;

export default Tabs;
