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

import {
  TabWrapper,
  TabList,
  Tab,
  TabPanel,
  ActiveTabIndicator,
} from './StyledSubTabs';
import { focusTab, getTabVariant, getTabWidth } from './utils';
import { fromUndefinedable, getOrElse, map } from '../../fp/Option';
import { pipe } from '../../fp/function';

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

export interface SubTabsProps extends Omit<CommonProps, 'id'> {
  /**
   * Unique identifier for SubTabs container. This will be combined with the id of each SubTab child to generate ARIA accessibility attributes.
   */
  id: string;
  /*
   * onChange event handler receiving id of upcoming active SubTab.
   */
  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;
    id: string | number;
    panel: ReactNode;
    title: string;
  }[];
}

const subTabPrefix = 'subTabs';

const SubTabs = ({
  onChange,
  renderActiveTabPanelOnly = false,
  selectedTabId,
  tabs,
  id,
  className,
  style,
  sx = {},
  'data-test-id': dataTestId,
}: SubTabsProps): ReactElement => {
  const tabRefs = useRef<Array<HTMLButtonElement | null>>([]);
  const [tabWidths, setTabWidths] = useState<string[]>();
  const [indicatorWidth, setIndiCatorWidth] = useState<string>();
  const [indicatorLeft, setIndiCatorLeft] = useState<string>();
  const [focusTabIndex, setFocusTabIndex] = useState<number>(0);

  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]);
    }
  };

  useEffect(() => {
    const selectedTabIndex = tabs.findIndex(tab => tab.id === selectedTabId);
    setFocusTabIndex(selectedTabIndex);
    if (tabWidths === undefined) return;
    setIndiCatorWidth(tabWidths[selectedTabIndex]);
    setIndiCatorLeft(
      selectedTabIndex === 0
        ? '0'
        : `calc(${tabWidths.slice(0, selectedTabIndex).join(' + ')})`
    );
  }, [
    selectedTabId,
    tabs,
    setFocusTabIndex,
    tabWidths,
    setIndiCatorWidth,
    setIndiCatorLeft,
  ]);

  useEffect(() => {
    setTimeout(() => {
      setTabWidths(tabRefs.current.map(tabElement => getTabWidth(tabElement)));
    }, 200);
  }, [tabs]);

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

  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={`${subTabPrefix}-${id}-tab-${tab.id}`}
              aria-selected={active}
              aria-controls={`${subTabPrefix}-${id}-panel-${tab.id}`}
              disabled={tab.disabled}
              themeVariant={variant}
              tabIndex={pipe(
                tabs[focusTabIndex],
                fromUndefinedable,
                map(focusedTab => (focusedTab.id === tab.id ? 0 : -1)),
                getOrElse(() => -1)
              )}
              onClick={(e: MouseEvent): void => {
                onChange(tab.id);
                e.preventDefault();
              }}
              ref={(el: HTMLButtonElement): void => {
                tabRefs.current[index] = el;
              }}
            >
              {tab.title}
            </Tab>
          );
        })}
        <ActiveTabIndicator
          data-test-id="active-tab-indicator"
          style={{ width: indicatorWidth, left: indicatorLeft }}
        />
      </TabList>
      {tabs.map(tab => {
        const hidden = selectedTabId !== tab.id;
        return hidden === false || renderActiveTabPanelOnly === false ? (
          <TabPanel
            key={tab.id}
            role="tabpanel"
            id={`${subTabPrefix}-${id}-panel-${tab.id}`}
            aria-labelledby={`${subTabPrefix}-${id}-tab-${tab.id}`}
            hidden={selectedTabId !== tab.id}
          >
            {tab.panel}
          </TabPanel>
        ) : null;
      })}
    </TabWrapper>
  );
};

export default SubTabs;
