import React, { useRef, useEffect, useCallback, useState } from 'react';
import PropTypes from 'prop-types';
import Slick from 'react-slick';
import withListener from '../withListener';
import { i18nMock } from '../../../mocks/i18n';
import { TOGGLE_CHECK, GROUP_HEADER, DROPDOWN, RADIO, COLORPICKER, IMAGE_UPLOADER, SLIDES } from '../../constants/schemaComponentTypes';
import { NAMESPACES } from '../../constants/namespaces';
import { CARETS_COLOR_OPTIONS, COLOR_DARK, defaultSlide, DELAY_OPTIONS, HEIGHT_OPTIONS, MOBILE_HEIGHT_RATIO } from '../../constants/components/carousel';
import { BACKGROUND_COLOR, BACKGROUND_IMAGE } from '../../constants/backgroundSettings';
import { createNamespace } from '../../tools/namingTools';
import { parseProductSlide } from '../../tools/parsers/slidesParser';
import { generateUniqueID } from '../../tools/helpers';
import Icon from '../../atoms/icon/Icon';
import CarouselSlideText, { TEXT_SLIDE_TYPE, PRODUCT_TYPE } from '../../atoms/carousel-slide-text/CarouselSlideText';
import CarouselSlideImage, { IMAGE_SLIDE_TYPE } from '../../atoms/carousel-slide-image/CarouselSlideImage';
import CarouselSlideVideo, { VIDEO_SLIDE_TYPE } from '../../atoms/carousel-slide-video/CarouselSlideVideo';
import Button from '../../atoms/button-icon/ButtonIcon';

import './styles/Carousel.scss';
import { i18nShape } from '../../constants/prop-types/i18n';

const n = createNamespace('Carousel');

const SLIDE_COMPONENT = {
    [IMAGE_SLIDE_TYPE]: CarouselSlideImage,
    [VIDEO_SLIDE_TYPE]: CarouselSlideVideo,
    [TEXT_SLIDE_TYPE]: CarouselSlideText,
    [PRODUCT_TYPE]: CarouselSlideText
};

export const CarouselComponent = ({
    data: {
        i18n
    },
    settings,
    isPreviewMode = false
}) => {
    const settingsWithDefaults = { ...CarouselComponent.defaultProps.settings, ...settings };
    const { caretsColorTheme, isAutorotate, slides } = settingsWithDefaults;
    const caretsColor = caretsColorTheme === COLOR_DARK ? 'black' : 'white';
    const i18nWithDefaults = { ...CarouselComponent.defaultProps.data.i18n, ...i18n };
    const sliderRef = useRef(null);
    const [isPaused, setIsPaused] = useState(false);

    const pauseSlider = () => {
        sliderRef.current.slickPause();
        setIsPaused(true);
    };

    const goToPrev = useCallback(() => {
        if (isPreviewMode) {
            return;
        }
        sliderRef.current.slickPrev();
        pauseSlider();
    }, [isPreviewMode]);

    const goToNext = useCallback(() => {
        if (isPreviewMode) {
            return;
        }
        sliderRef.current.slickNext();
        pauseSlider();
    }, [isPreviewMode]);

    useEffect(() => {
        if (Number.isInteger(isPreviewMode) && isPreviewMode >= 0) {
            sliderRef.current && sliderRef.current.slickGoTo(isPreviewMode);
            pauseSlider();
        } else if (isAutorotate) {
            sliderRef.current.slickPlay();
        }
    }, [isAutorotate, isPreviewMode]);

    // Pause the slider when clicking on dot, for accessibility
    // Not a feature of React-Slick so need to do this manually
    const rootRef = useRef(null);
    useEffect(() => {
        const slickDots = rootRef.current.querySelectorAll('.slick-dots button');
        slickDots.forEach(dot => dot.addEventListener('click', pauseSlider));
        return () => {
            slickDots.forEach(dot => dot.removeEventListener('click', pauseSlider));
        };
    });

    const mobileHeight = Math.ceil(MOBILE_HEIGHT_RATIO * settingsWithDefaults.height);
    const slideSettings = {
        ...settingsWithDefaults,
        mobileHeight
    };

    const renderItem = item => {
        const uid = generateUniqueID([item.slideData.title, item.slideData.text]);
        const ComponentConstructor = SLIDE_COMPONENT[item.contentType || TEXT_SLIDE_TYPE];
        const itemSettings = item.contentType === PRODUCT_TYPE ? parseProductSlide(item.slideData) : item.slideData;

        return (
            <div {...n('slide').withTestId().withE2EId().props} key={uid}>
                <ComponentConstructor data={{ ...itemSettings, i18n: i18nWithDefaults }} settings={slideSettings} />
            </div>
        );
    };

    const renderSlides = () => {
        if (!slides.length) {
            return defaultSlide.map(renderItem);
        }
        return slides.map(renderItem);
    };

    const { backgroundType, backgroundColor, backgroundImg } = settingsWithDefaults;
    const componentVariables = {
        'background-color': backgroundType === BACKGROUND_COLOR.value ? backgroundColor : null,
        'background-image': backgroundType === BACKGROUND_IMAGE.value ? backgroundImg && `url(${backgroundImg.url})` : null,
        'carets-color': caretsColor,
        height: `${settingsWithDefaults.height}px`,
        'mobile-height': `${mobileHeight}px`
    };

    const wrapperModifiers = [settingsWithDefaults.isDisplayDots && 'with-dots'];

    const arrowModifiers = [
        !settingsWithDefaults.isDisplayCarets && 'hidden',
        settingsWithDefaults.isDisplayCaretsOnHover && 'showOnHover'
    ];

    // change of state is necessary on each change,
    // to trigger the layout effect that will check for slide's a11y tabindex
    const [currentSlide, setCurrentSlide] = useState(0);
    const onChange = e => {
        setCurrentSlide(e);
    };
    const togglePause = () => {
        if (isPaused) {
            sliderRef.current.slickPlay();
        } else {
            sliderRef.current.slickPause();
        }
        setIsPaused(!isPaused);
    };

    return (
        <div
            {...n('wrapper', wrapperModifiers).withTestId().withVariables(componentVariables).withE2EId().props}
            ref={rootRef}
        >
            <div {...n('content').withE2EId().props}>
                {isAutorotate && (
                    <Button
                        {...n('btnPause').props}
                        onClick={togglePause}
                        theme="secondary"
                        aria-label={isPaused ? i18nWithDefaults.carouselPlay : i18nWithDefaults.carouselPause}
                    >
                        {isPaused ? i18nWithDefaults.play : i18nWithDefaults.pause}
                    </Button>
                )}
                <Slick
                    ref={sliderRef}
                    slidesToShow={1}
                    dots={settingsWithDefaults.isDisplayDots}
                    arrows={false}
                    autoplay={isAutorotate}
                    autoplaySpeed={settingsWithDefaults.delay * 1000}
                    slickPause={isPreviewMode}
                    speed={450}
                    draggable={false}
                    infinite
                    pauseOnDotsHover
                    afterChange={onChange}
                >
                    {renderSlides(currentSlide)}
                </Slick>
                <>
                    <button {...n('arrow', [...arrowModifiers, 'prev']).withTestId().withE2EId('prev').props} type="button" onClick={goToPrev}>
                        <Icon type="caretLeft" {...n('arrowIcon').withE2EId('prev').props} />
                        <span {...n('arrowText').withE2EId('prev').props}>{i18nWithDefaults.previous}</span>
                    </button>
                    <button {...n('arrow', [...arrowModifiers, 'next']).withTestId('next').props} type="button" onClick={goToNext}>
                        <Icon type="caretRight" {...n('arrowIcon').withE2EId('next').props} />
                        <span {...n('arrowText').withE2EId('next').props}>{i18nWithDefaults.next}</span>
                    </button>
                </>
            </div>
        </div>
    );
};

const BACKGROUND_TYPE_OPTIONS = [BACKGROUND_IMAGE, BACKGROUND_COLOR];

CarouselComponent.schema = () => ({
    name: 'Carousel',
    title: 'uieditor.sfbComponent.carousel.title',
    iconName: 'carousel',
    orderableSection: [
        NAMESPACES.home, NAMESPACES.listing, NAMESPACES.profile, NAMESPACES.profileEdition,
        NAMESPACES.profileFeatures, NAMESPACES.bundle, NAMESPACES.help
    ],
    form: {
        slides: {
            type: SLIDES
        },
        headingSpacer: {
            type: GROUP_HEADER,
            title: 'uieditor.sfbComponent.carousel.settings.title',
            symbol: 'style'
        },
        height: {
            title: 'uieditor.sfbComponent.carousel.height.title',
            type: DROPDOWN,
            required: false,
            defaultValue: HEIGHT_OPTIONS[1].value,
            options: HEIGHT_OPTIONS
        },

        backgroundType: {
            title: 'uieditor.sfbComponent.carousel.backgroundType.title',
            type: RADIO,
            required: false,
            defaultValue: BACKGROUND_TYPE_OPTIONS[1].value,
            options: BACKGROUND_TYPE_OPTIONS
        },
        backgroundImg: {
            title: 'uieditor.sfbComponent.carousel.backgroundImg.title',
            caption: 'uieditor.sfbComponent.carousel.backgroundImg.caption',
            placeholder: 'uieditor.sfbComponent.carousel.backgroundImg.placeholder',
            defaultValue: {},
            type: IMAGE_UPLOADER,
            required: false,
            conditionalAttributes: {
                isDisabled: {
                    when: 'backgroundType',
                    connector: 'equals',
                    value: BACKGROUND_COLOR.value
                }
            }
        },
        backgroundColor: {
            title: 'uieditor.sfbComponent.carousel.backgroundColor.title',
            defaultValue: '#fff',
            type: COLORPICKER,
            conditionalAttributes: {
                isDisabled: {
                    when: 'backgroundType',
                    connector: 'equals',
                    value: BACKGROUND_IMAGE.value
                }
            }
        },
        isDisplayCarets: {
            label: 'uieditor.sfbComponent.carousel.isDisplayCarets.label',
            defaultValue: true,
            type: TOGGLE_CHECK,
            marginSize: 10
        },
        isDisplayCaretsOnHover: {
            label: 'uieditor.sfbComponent.carousel.isDisplayCaretsOnHover.label',
            defaultValue: false,
            type: TOGGLE_CHECK,
            conditionalAttributes: {
                isVisible: {
                    when: 'isDisplayCarets',
                    connector: 'equals',
                    value: true
                }
            },
            indentSize: 1,
            marginSize: 10
        },
        isDisplayDots: {
            label: 'uieditor.sfbComponent.carousel.isDisplayDots.label',
            defaultValue: true,
            type: TOGGLE_CHECK
        },
        caretsColorTheme: {
            title: 'uieditor.sfbComponent.carousel.caretsColorTheme.title',
            type: DROPDOWN,
            required: false,
            defaultValue: CARETS_COLOR_OPTIONS[0].value,
            options: CARETS_COLOR_OPTIONS,
            validation: {
                availableOptions: CARETS_COLOR_OPTIONS.map(({ value }) => value),
                message: 'uieditor.sfbComponent.dropdown.validation.message',
                default: 'uieditor.sfbComponent.dropdown.defaultValue'
            }
        },
        isAutorotate: {
            label: 'uieditor.sfbComponent.carousel.isAutorotate.label',
            defaultValue: true,
            type: TOGGLE_CHECK
        },
        // Default value here is 5 seconds for accessibility reasons,
        // minimum to give time to slow readers to check the active slide
        delay: {
            title: 'uieditor.sfbComponent.carousel.delay.title',
            type: DROPDOWN,
            required: false,
            defaultValue: DELAY_OPTIONS[2].value, // 5 seconds
            options: DELAY_OPTIONS,
            conditionalAttributes: {
                isVisible: {
                    when: 'isAutorotate',
                    connector: 'equals',
                    value: true
                }
            }
        }
    },
    marketplaceData: {
        i18n: 'i18n'
    }
});

CarouselComponent.propTypes = {
    data: PropTypes.shape({
        i18n: PropTypes.shape(i18nShape)
    }),
    settings: PropTypes.shape({
        slides: PropTypes.arrayOf(
            PropTypes.shape({
                name: PropTypes.string,
                blurb: PropTypes.string,
                url: PropTypes.string,
                overviewImageUrl: PropTypes.string
            })
        ),
        height: PropTypes.number,
        isDisplayCarets: PropTypes.bool,
        caretsColorTheme: PropTypes.oneOf(CARETS_COLOR_OPTIONS.map(({ value }) => value)),
        isDisplayCaretsOnHover: PropTypes.bool,
        isDisplayDots: PropTypes.bool,
        isAutorotate: PropTypes.bool,
        delay: PropTypes.number,
        backgroundColor: PropTypes.string,
        backgroundImg: PropTypes.shape({
            filename: PropTypes.string,
            type: PropTypes.oneOf([IMAGE_UPLOADER]),
            url: PropTypes.string
        })
    }),
    isPreviewMode: PropTypes.bool
};

const SETTINGS = CarouselComponent.schema().form;
CarouselComponent.defaultProps = {
    data: {
        i18n: i18nMock
    },
    settings: {
        slides: [],
        height: SETTINGS.height.defaultValue,
        isDisplayCarets: SETTINGS.isDisplayCarets.defaultValue,
        caretsColorTheme: SETTINGS.caretsColorTheme.defaultValue,
        isDisplayCaretsOnHover: SETTINGS.isDisplayCaretsOnHover.defaultValue,
        isDisplayDots: SETTINGS.isDisplayDots.defaultValue,
        isAutorotate: SETTINGS.isAutorotate.defaultValue,
        delay: SETTINGS.delay.defaultValue,
        backgroundColor: SETTINGS.backgroundColor.defaultValue,
        backgroundImg: null
    },
    isPreviewMode: false
};

export default withListener(CarouselComponent);
