import {
  AriaAttributes,
  Children,
  KeyboardEventHandler,
  ReactElement,
  useCallback,
  useLayoutEffect,
  useRef,
  useState,
} from 'react';

import { useId } from '../../hooks/useId';
import { useTestIdAttribute } from '../../hooks/useTestIdAttribute';
import { CommonProps } from '../../types';
import { assertEmptyObject } from '../../utils/assertEmptyObject';
import { makeTestId } from '../../utils/makeTestId';

import { TabItemProps } from './components/TabItem/TabItem';
import { StyledPointer, StyledTab, StyledTabList, StyledTabPanel } from './styled';

/** Props for {@link Tabs} */
export interface TabsProps extends CommonProps {
  /** Array of {@link TabItem}   */
  children: ReactElement<TabItemProps> | ReactElement<TabItemProps>[];
  /** aria-label for the root tabs element */
  ariaLabel?: AriaAttributes['aria-label'];
  /**
   * Index of the active tab. Are counted only enabled tabs.
   *
   * @default 0
   */
  defaultActiveIndex?: number;
}

/**
 * Tabs make it easy to explore and switch between different views.
 *
 * ```tsx
 * <Tabs>
 *   <TabItem label="Apple">apple description</TabItem>
 *   <TabItem label="Orange" disabled>orange description</TabItem>
 *   <TabItem label="Carrot">carrot description</TabItem>
 * </Tabs>
 * ```
 *
 * Tabs organize and allow navigation between groups of content that are related and at the same level of hierarchy.
 *
 * <Story id="components-tabs--default" />
 * <Story id="components-tabs-examples--login-form" />
 */
export function Tabs(props: TabsProps) {
  const { children, ariaLabel, ariaDescribedBy, testId, className, defaultActiveIndex, ...restProps } = props;
  assertEmptyObject(restProps);

  const testIdAttribute = useTestIdAttribute();

  const [pointerWidth, setPointerWidth] = useState(0);
  const [pointerLeft, setPointerLeft] = useState(0);

  const id = useId();
  const tabId = (index: number) => `${id}-tab-${index}`;
  const tabPanelId = (index: number) => `${id}-tabpanel-${index}`;

  const activeTabRef = useRef<HTMLButtonElement | null>(null);
  const [activeIndex, setActiveIndex] = useState(defaultActiveIndex ?? 0);
  const childrenArray = Children.toArray(children) as ReactElement<TabItemProps | undefined>[];
  const enabledChildrenArray = childrenArray.filter((child) => !child.props?.disabled);
  const maxEnabledIndex = enabledChildrenArray.length - 1;

  useLayoutEffect(() => {
    const activeTabElement = activeTabRef.current;
    /* istanbul ignore next */
    if (!activeTabElement) {
      return;
    }
    if (
      document.activeElement &&
      Array.from(activeTabElement.parentElement!.children).includes(document.activeElement)
    ) {
      activeTabElement.focus();
    }
    const textWrapper = activeTabElement.firstElementChild as HTMLDivElement;
    setPointerWidth(textWrapper.offsetWidth);
    setPointerLeft(textWrapper.offsetLeft);
  }, [activeIndex]);

  const handleKeyDown = useCallback<KeyboardEventHandler<HTMLDivElement>>(
    (event) => {
      switch (event.key) {
        case 'ArrowRight':
          setActiveIndex(activeIndex < maxEnabledIndex ? activeIndex + 1 : 0);
          break;
        case 'ArrowLeft':
          setActiveIndex(activeIndex > 0 ? activeIndex - 1 : maxEnabledIndex);
          break;
        /* istanbul ignore next */
        default:
      }
    },
    [activeIndex, maxEnabledIndex],
  );

  return (
    <>
      <StyledTabList
        aria-describedby={ariaDescribedBy}
        aria-label={ariaLabel}
        className={className}
        onKeyDown={handleKeyDown}
        role="tablist"
        {...{ [testIdAttribute]: testId }}
      >
        {Children.map(children, (child, index) => {
          const active = childrenArray[index] === enabledChildrenArray[activeIndex];
          return (
            <StyledTab
              ref={active ? activeTabRef : undefined}
              aria-controls={tabPanelId(index)}
              aria-describedby={child.props.ariaDescribedBy}
              aria-selected={active}
              className={child.props.className}
              disabled={child.props.disabled}
              id={tabId(index)}
              role="tab"
              tabIndex={active ? undefined : -1}
              onClick={() => {
                setActiveIndex(enabledChildrenArray.indexOf(childrenArray[index]));
                child.props.onClick?.();
              }}
              {...{ [testIdAttribute]: child.props.testId }}
            >
              <div>{child.props.label}</div>
            </StyledTab>
          );
        })}
        <StyledPointer style={{ left: pointerLeft, width: pointerWidth }} />
      </StyledTabList>
      {Children.map(children, (child, index) => {
        const active = childrenArray[index] === enabledChildrenArray[activeIndex];
        return (
          <StyledTabPanel
            aria-labelledby={tabId(index)}
            hidden={!active}
            id={tabPanelId(index)}
            role="tabpanel"
            tabIndex={0}
            {...{ [testIdAttribute]: makeTestId(child.props.testId, 'panel') }}
          >
            {active ? child.props.children : null}
          </StyledTabPanel>
        );
      })}
    </>
  );
}
