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

import {
  StyledButton,
  StyledButtonWrapper,
  StyledButtonList,
  CarouselContainer,
  CarouselSingleSlide,
  CarouselSlideCollection,
  StyledButtonIconWrapper,
} from './StyledCarousel';
import Spinner from '../Spinner';
import Button from '../Button';

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

import { getDotDirection, getDotState, getButtonPlacement } from './utils';

export interface CarouselProps extends CommonProps {
  /**
   * Whether to scroll automatically.
   */
  autoPlay?: boolean;
  /**
   * List of items to be shown to the story board. Each item must have an unquie id.
   */
  collection: {
    content: ReactNode;
    id: number;
  }[];
  /**
   * Position of dots button.
   */
  dotPlacement?: 'top' | 'right' | 'bottom' | 'left';
  /**
   * Set interval of each slide.
   */
  interval?: number;
  /**
   * Set loading state of carousel.
   */
  loading?: boolean;
  /**
   * Color of slide background to have corresponding dots color.
   */
  variant?: 'light' | 'dark';
}

const Carousel = ({
  autoPlay = false,
  collection,
  dotPlacement = 'bottom',
  interval = 2000,
  loading = false,
  variant = 'light',
  className,
  style,
  sx = {},
  id,
  'data-test-id': dataTestId,
}: CarouselProps): ReactElement => {
  const [itemCollection, setItemCollection] = useState<ReactElement[]>([]);
  const [currentItemIndex, setCurrentItemIndex] = useState<number>(0);
  const timer = useRef<number>();

  const handleItemChange = useCallback(
    (index: number) => {
      if (index < itemCollection.length && index >= 0) {
        setCurrentItemIndex(index);
      }
      if (index >= itemCollection.length) {
        setCurrentItemIndex(0);
      }
      if (index < 0) {
        setCurrentItemIndex(itemCollection.length - 1);
      }
    },
    [itemCollection]
  );

  const createInterval = useCallback(() => {
    timer.current = window.setInterval(() => {
      handleItemChange(currentItemIndex + 1);
    }, interval);
  }, [interval, handleItemChange, currentItemIndex]);

  const destroyInterval = useCallback(() => {
    clearInterval(timer.current);
  }, []);

  useEffect(
    () =>
      setItemCollection(
        collection.map(item => {
          return (
            <CarouselSingleSlide
              key={item.id}
              data-test-id={`slide-${item.id}`}
            >
              {item.content}
            </CarouselSingleSlide>
          );
        })
      ),
    [collection]
  );

  useEffect(() => {
    if (autoPlay === true && loading === false) {
      createInterval();
      return (): void => destroyInterval();
    }
    return undefined;
  }, [autoPlay, createInterval, destroyInterval, loading]);

  return (
    <CarouselContainer
      id={id}
      className={className}
      style={{ ...style, ...css(sx) }}
      data-test-id={dataTestId}
      onMouseEnter={(): void => {
        if (autoPlay === true) {
          destroyInterval();
        }
      }}
      onMouseLeave={(): void => {
        if (autoPlay === true) {
          createInterval();
        }
      }}
    >
      {loading === true ? (
        <Spinner
          data-test-id="spinner"
          style={{ minHeight: '100%', justifyContent: 'center' }}
        />
      ) : (
        <>
          <CarouselSlideCollection
            data-test-id="slide-collection"
            currentSlide={currentItemIndex}
            themeDotDirection={getDotDirection({ dotPlacement })}
          >
            {itemCollection}
          </CarouselSlideCollection>

          <StyledButtonIconWrapper
            themeButtonPlacement={getButtonPlacement({ dotPlacement })}
            themeVariant={variant}
          >
            <Button.Icon
              data-test-id="previous-slide-button"
              icon={
                dotPlacement === 'top' || dotPlacement === 'bottom'
                  ? 'circle-left'
                  : 'circle-up'
              }
              onClick={(e): void => {
                e.preventDefault();
                handleItemChange(currentItemIndex - 1);
              }}
            />
            <Button.Icon
              data-test-id="next-slide-button"
              icon={
                dotPlacement === 'top' || dotPlacement === 'bottom'
                  ? 'circle-right'
                  : 'circle-down'
              }
              onClick={(e): void => {
                e.preventDefault();
                handleItemChange(currentItemIndex + 1);
              }}
            />
          </StyledButtonIconWrapper>
          <StyledButtonWrapper
            themeDotPlacement={dotPlacement}
            themeDotDirection={getDotDirection({ dotPlacement })}
          >
            {collection.map((item, index) => {
              const active = currentItemIndex === index;

              return (
                <StyledButtonList
                  key={item.id}
                  themeDotState={getDotState({
                    active,
                    dotPlacement,
                  })}
                >
                  <StyledButton
                    data-test-id={`dot-navigation-button-${item.id}`}
                    type="button"
                    onClick={(e): void => {
                      e.preventDefault();
                      handleItemChange(index);
                    }}
                    active={active}
                    themeVariant={variant}
                    themeDotDirection={getDotDirection({ dotPlacement })}
                  />
                </StyledButtonList>
              );
            })}
          </StyledButtonWrapper>
        </>
      )}
    </CarouselContainer>
  );
};

export default Carousel;
