import type { CSSProperties, ExtractPropTypes, PropType } from 'vue';
import { ref, computed, watchEffect, defineComponent } from 'vue';
import PropTypes from '../_util/vue-types';
import warning from '../_util/warning';
import classNames from '../_util/classNames';
import SlickCarousel from '../vc-slick';
import { withInstall } from '../_util/type';
import useConfigInject from '../config-provider/hooks/useConfigInject';

export type SwipeDirection = 'left' | 'down' | 'right' | 'up' | string;

export type LazyLoadTypes = 'ondemand' | 'progressive';

export type CarouselEffect = 'scrollx' | 'fade';
export type DotPosition = 'top' | 'bottom' | 'left' | 'right';

export interface CarouselRef {
  goTo: (slide: number, dontAnimate?: boolean) => void;
  next: () => void;
  prev: () => void;
  autoplay: (palyType?: 'update' | 'leave' | 'blur') => void;
  innerSlider: any;
}

// Carousel
export const carouselProps = () => ({
  effect: String as PropType<CarouselEffect>,
  dots: { type: Boolean, default: true },
  vertical: { type: Boolean, default: undefined },
  autoplay: { type: Boolean, default: undefined },
  easing: String,
  beforeChange: Function as PropType<(currentSlide: number, nextSlide: number) => void>,
  afterChange: Function as PropType<(currentSlide: number) => void>,
  // style: PropTypes.React.CSSProperties,
  prefixCls: String,
  accessibility: { type: Boolean, default: undefined },
  nextArrow: PropTypes.any,
  prevArrow: PropTypes.any,
  pauseOnHover: { type: Boolean, default: undefined },
  // className: String,
  adaptiveHeight: { type: Boolean, default: undefined },
  arrows: { type: Boolean, default: false },
  autoplaySpeed: Number,
  centerMode: { type: Boolean, default: undefined },
  centerPadding: String,
  cssEase: String,
  dotsClass: String,
  draggable: { type: Boolean, default: false },
  fade: { type: Boolean, default: undefined },
  focusOnSelect: { type: Boolean, default: undefined },
  infinite: { type: Boolean, default: undefined },
  initialSlide: Number,
  lazyLoad: String as PropType<LazyLoadTypes>,
  rtl: { type: Boolean, default: undefined },
  slide: String,
  slidesToShow: Number,
  slidesToScroll: Number,
  speed: Number,
  swipe: { type: Boolean, default: undefined },
  swipeToSlide: { type: Boolean, default: undefined },
  swipeEvent: Function as PropType<(swipeDirection: SwipeDirection) => void>,
  touchMove: { type: Boolean, default: undefined },
  touchThreshold: Number,
  variableWidth: { type: Boolean, default: undefined },
  useCSS: { type: Boolean, default: undefined },
  slickGoTo: Number,
  responsive: Array,
  dotPosition: { type: String as PropType<DotPosition>, default: undefined },
  verticalSwiping: { type: Boolean, default: false },
});
export type CarouselProps = Partial<ExtractPropTypes<ReturnType<typeof carouselProps>>>;
const Carousel = defineComponent({
  compatConfig: { MODE: 3 },
  name: 'ACarousel',
  inheritAttrs: false,
  props: carouselProps(),
  setup(props, { slots, attrs, expose }) {
    const slickRef = ref();

    const goTo = (slide: number, dontAnimate = false) => {
      slickRef.value?.slickGoTo(slide, dontAnimate);
    };

    expose({
      goTo,
      autoplay: palyType => {
        slickRef.value?.innerSlider?.handleAutoPlay(palyType);
      },
      prev: () => {
        slickRef.value?.slickPrev();
      },
      next: () => {
        slickRef.value?.slickNext();
      },
      innerSlider: computed(() => {
        return slickRef.value?.innerSlider;
      }),
    } as CarouselRef);
    watchEffect(() => {
      warning(
        props.vertical === undefined,
        'Carousel',
        '`vertical` is deprecated, please use `dotPosition` instead.',
      );
    });
    const { prefixCls, direction } = useConfigInject('carousel', props);
    const dotPosition = computed(() => {
      if (props.dotPosition) return props.dotPosition;
      if (props.vertical !== undefined) return props.vertical ? 'right' : 'bottom';
      return 'bottom';
    });
    const vertical = computed(() => dotPosition.value === 'left' || dotPosition.value === 'right');
    const dsClass = computed(() => {
      const dotsClass = 'slick-dots';
      return classNames({
        [dotsClass]: true,
        [`${dotsClass}-${dotPosition.value}`]: true,
        [`${props.dotsClass}`]: !!props.dotsClass,
      });
    });
    return () => {
      const { dots, arrows, draggable, effect } = props;
      const { class: cls, style, ...restAttrs } = attrs;
      const fade = effect === 'fade' ? true : props.fade;
      const className = classNames(prefixCls.value, {
        [`${prefixCls.value}-rtl`]: direction.value === 'rtl',
        [`${prefixCls.value}-vertical`]: vertical.value,
        [`${cls}`]: !!cls,
      });
      return (
        <div class={className} style={style as CSSProperties}>
          <SlickCarousel
            ref={slickRef}
            {...props}
            {...restAttrs}
            dots={!!dots}
            dotsClass={dsClass.value}
            arrows={arrows}
            draggable={draggable}
            fade={fade}
            vertical={vertical.value}
            v-slots={slots}
          />
        </div>
      );
    };
  },
});

export default withInstall(Carousel);
