import React, { useCallback, useMemo, useRef, useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import Slick from 'react-slick';
import { SMALL, MEDIUM, LARGE } from '../../constants/sizes';
import { productQuickviewSchemaForm } from '../../constants/components/schemas/product-quickview';
import { productQuickviewSettingsPropTypes } from '../../constants/components/prop-types/product-quickview';
import { bMedium } from '../../constants/breakpoints';
import { TILE_TYPES, SQUARE, CLASSIC, TALL } from '../../constants/components/tile';
import { INPUT, RADIO, COLORPICKER, CHECKBOX, SLIDER, GROUP_HEADER } from '../../constants/schemaComponentTypes';
import { TILE_BACKGROUND } from '../../constants/backgroundSettings';
import { NUMBER_OF_DEFAULT_SLIDES, MANUALLY_CURATED_CONTENT_TYPE, DYNAMICALLY_CURATED_CONTENT_TYPE } from '../../constants/components/slider';
import { getReponsiveSettings } from '../../tools/responsive/slider';
import { generateUniqueID } from '../../tools/helpers';
import { createNamespace } from '../../tools/namingTools';
import { useSliderHeights } from '../../tools/heights/useSliderHeights';
import { useContainerWidth } from '../../tools/widths/useContainerWidth';
import { getAbsoluteCoordinates } from '../../tools/coordinates';
import { defaultSlider } from '../../tools/defaultSlider';
import ProductQuickView from '../product-quick-view/ProductQuickView';
import Tile, { defaultTileProps } from '../tile/Tile';
import Button from '../button/Button';
import Icon from '../icon/Icon';
import Modal from '../modal/Modal';
import './styles/Slider.scss';

const n = createNamespace('Slider');

const renderSeeMoreButton = (contentType, buttonLabel, url) => (
    contentType !== MANUALLY_CURATED_CONTENT_TYPE && buttonLabel && url
);

export const SliderComponent = ({
    name,
    url,
    items: propItems,
    staticTitle,
    // although component's main purpose is being a slider, it can also show a static list of elements
    isSlider: isSliderSetting,
    maxRows,
    buttonLabel,
    contentType,
    i18n,
    settings
}) => {
    const [currentQuickview, setCurrentQuickview] = useState(null);
    const i18nWithDefaults = useMemo(
        () => {
            if (!i18n) {
                return SliderComponent.defaultProps.i18n;
            }
            return { ...SliderComponent.defaultProps.i18n, ...i18n };
        },
        [i18n]
    );

    const componentSettings = {
        ...SliderComponent.defaultProps.settings,
        ...settings
    };

    const {
        sliderTitle,
        layout,
        borderRadius,
        size,
        tileBackgroundColor,
        tileCtaLabel,
        displayOptions,
        quickviewOptions,
        quickviewDisplayOptions,
        quickviewViewMode
    } = componentSettings;

    // if props items are empty, fill with placeholder slides
    const items = useMemo(() => {
        if (!propItems || !propItems.length) {
            const defaultSlideTexts = {
                title: i18n.defaultTitlePlaceholder,
                description: i18n.defaultDescriptionPlaceholder,
                price: i18n.defaultPricePlaceholder
            };
            return Array.from(
                Array(NUMBER_OF_DEFAULT_SLIDES).keys()
            ).map(
                index => defaultSlider(index, defaultSlideTexts)
            );
        }
        return propItems;
    }, [propItems, i18n]);

    const nItems = items && items.length;

    const sliderRef = useRef(null);
    const sliderWrapperRef = useRef(null);

    const hasQuickview = quickviewOptions ? quickviewOptions.includes('isQuickviewVisible') : false;
    const isDescriptionVisible = displayOptions ? displayOptions.includes('isDescriptionVisible') : false;
    const isRatingsVisible = displayOptions ? displayOptions.includes('isRatingsVisible') : false;

    const [containerRef, containerWidth] = useContainerWidth();
    const isMobile = Boolean(containerWidth && containerWidth < bMedium);

    const sliderResponsiveSettings = useMemo(() => getReponsiveSettings(layout, size, nItems, containerWidth), [
        layout,
        size,
        nItems,
        containerWidth
    ]);

    // no slider on mobile, slides are displayed one over the other
    const isSlider = isSliderSetting && !isMobile && nItems > sliderResponsiveSettings.columns;
    const staticModifier = isSlider ? 'slider' : 'static';
    const listModifiers = [staticModifier, isMobile && 'mobile'];

    useEffect(() => {
        const updateQuickview = e => {
            const { targetSlider, id, left, top } = e.detail;
            const isTargetedSlider = sliderRef === targetSlider;

            if (isTargetedSlider) {
                setCurrentQuickview({ id, left, top });
            } else {
                setCurrentQuickview(null);
            }
        };

        window.addEventListener('updateQuickview', updateQuickview);
        return () => window.removeEventListener('updateQuickview', updateQuickview);
    }, []);

    const [height, reportHeight] = useSliderHeights(size, layout);

    const { columns = 1 } = sliderResponsiveSettings || {};

    const componentVariables = useMemo(() => {
        const variables = { 'slider-column': columns };

        if (borderRadius) {
            variables['slide-border-radius'] = `${borderRadius}px`;
        }

        return variables;
    }, [columns, borderRadius]);

    const previousSlide = () => {
        setCurrentQuickview(null);
        sliderRef.current.slickPrev();
    };

    const nextSlide = () => {
        setCurrentQuickview(null);
        sliderRef.current.slickNext();
    };

    // will horizontally center on clicked button, and adjust top to slider's top
    const getQuickviewTargetPosition = event => {
        const { left } = getAbsoluteCoordinates(event.currentTarget);
        const { top } = getAbsoluteCoordinates(sliderWrapperRef.current);
        return {
            top,
            left
        };
    };

    // dispatch an event, so that if there are more than one slider in the page, only one
    // can have a quickview open at any given time
    const openQuickView = (event, id) => {
        const { left, top } = getQuickviewTargetPosition(event);

        const quickViewPayload = {
            targetSlider: sliderRef,
            id,
            left,
            top
        };

        const updateQueriesEvent = new CustomEvent('updateQuickview', {
            bubbles: true,
            detail: quickViewPayload
        });
        window.dispatchEvent(updateQueriesEvent);
    };

    const renderHeaderTitle = useCallback(() => {
        const title = sliderTitle || staticTitle || name;

        return title && (<h2 {...n('title').withTestId().withE2EId().props}>{title}</h2>);
    }, [sliderTitle, name, staticTitle]);

    const renderSlide = (item, index) => {
        if (!item || !item.id) {
            return null;
        }
        const modifiers = [layout, staticModifier, isMobile && 'mobile'];
        const slideModifiers = [
            ...modifiers,
            `${sliderResponsiveSettings.columns}cols`
        ];
        const tileSettings = {
            layout,
            size,
            borderRadius,
            tileBackgroundColor,
            isDescriptionVisible,
            isRatingsVisible
        };

        return (
            <div {...n('slide', slideModifiers).withTestId().withE2EId().props} key={generateUniqueID([item.id, item.name, index])}>
                <div {...n('content', modifiers).withE2EId().props}>
                    <Tile
                        onDragStart={e => e.preventDefault()}
                        data={item}
                        settings={tileSettings}
                        tileIndex={index}
                        reportHeight={reportHeight}
                        height={height}
                        isMobile={!!isMobile}
                        ctaLabel={tileCtaLabel}
                    />
                    {hasQuickview && (
                        <div {...n('quickviewButton').withE2EId().props}>
                            <Button type="primary" onClick={e => openQuickView(e, item.id)} size="small">{i18nWithDefaults.quickview}</Button>
                        </div>
                    )}
                </div>
            </div>
        );
    };

    const renderSliderArrows = () => {
        const buttonProps = {
            type: 'white',
            size: 'small',
            condensed: true
        };

        return (
            <div {...n('arrows').withE2EId().props}>
                <div {...n('arrow', 'previous').withTestId('goPrev').withE2EId('prev').props}>
                    <Button
                        {...buttonProps}
                        onClick={previousSlide}
                    >
                        <Icon type="caretLeft" {...n('arrowIcon').withE2EId('arrowIconPrev').props} />
                        <span {...n('arrowText').withE2EId('arrowTextPrev').props}>Previous</span>
                    </Button>
                </div>
                <div {...n('arrow', 'next').withTestId('goNext').withE2EId('goNext').props}>
                    <Button
                        {...buttonProps}
                        {...n('arrows').withTestId('goNext').withE2EId('goNext').props}
                        onClick={nextSlide}
                    >
                        <Icon type="caretRight" {...n('arrowIcon').withE2EId('arrowIconNext').props} />
                        <span {...n('arrowText').withE2EId('arrowTextNext').props}>Next</span>
                    </Button>
                </div>
            </div>
        );
    };

    const renderQuickview = () => {
        let quickview = <Modal currentKey="none" />;

        const quickviewItem = currentQuickview ? items.find(app => app.id === currentQuickview.id) : null;

        if (quickviewItem) {
            quickview = (
                <Modal
                    currentKey={currentQuickview.id}
                    left={currentQuickview.left}
                    top={currentQuickview.top}
                    onClose={() => setCurrentQuickview(null)}
                    currentItem={(
                        <ProductQuickView
                            data={quickviewItem}
                            settings={{ quickviewDisplayOptions, quickviewViewMode }}
                        />
                    )}
                />
            );
        }

        return quickview;
    };

    const renderViewMore = (isLink = false) => renderSeeMoreButton(contentType, buttonLabel, url) && (
        <div {...n('viewMore').withTestId().withE2EId().props}>
            <Button
                type="primary"
                buttonType={isLink ? 'link' : 'button'}
                size={!isLink && 'small'}
                extraClass="btn-view-more"
                onClick={() => { window.location = url; }}
            >
                <span {...n('viewMoreText').withClass('button_text').withTestId().withE2EId().props}>{buttonLabel}</span>
                {isLink && <Icon {...n('viewMoreIcon').withTestId().withE2EId().props} type="caretRight" />}
            </Button>
        </div>
    );

    const headerButtons = (url || sliderResponsiveSettings.infinite) && (
        <div {...n('headerButtons').props}>
            {!isMobile && renderViewMore()}
            {sliderResponsiveSettings.infinite && renderSliderArrows()}
        </div>
    );

    const renderableItems = useMemo(() => {
        // on mobile, there is no slider but we display an amount of slides depending on the default
        if (isMobile) {
            return items.slice(0, sliderResponsiveSettings.maxShown);
        }
        // when there is no slider, we display a given number of static rows
        if (!isSliderSetting) {
            return items.slice(0, sliderResponsiveSettings.columns * maxRows);
        }
        return items;
    }, [items, isMobile, isSliderSetting, maxRows, sliderResponsiveSettings]);

    const slides = renderableItems.map(renderSlide);

    const slideContents = isSlider ? (
        <Slick
            ref={sliderRef}
            draggable={false}
            arrows={false}
            speed={300}
            mobileFirst
            slidesToShow={sliderResponsiveSettings.columns}
            infinite={sliderResponsiveSettings.infinite}
        >
            {slides}
        </Slick>
    ) : slides;

    return (
        <div {...n('container').withTestId().withVariables(componentVariables).withE2EId().props} ref={containerRef}>
            <div {...n('header').withE2EId().props}>
                {renderHeaderTitle()}
                {isSlider && !!nItems && headerButtons}
            </div>
            <div>
                <div {...n('list', listModifiers).withE2EId().props} ref={sliderWrapperRef}>
                    {slideContents}
                </div>
                {renderQuickview()}
                {isMobile && (
                    <div {...n('footer').withE2EId().props}>{renderViewMore(true)}</div>
                )}
            </div>
        </div>
    );
};

const SLIDE_SIZES = [SMALL, MEDIUM, LARGE];
const DISPLAY_OPTIONS = [
    { value: 'isDescriptionVisible', label: 'uieditor.sfbComponent.slider.displayOptions.option.description' },
    { value: 'isRatingsVisible', label: 'uieditor.sfbComponent.slider.displayOptions.option.ratings' }
];
const DISPLAY_QUICKVIEW_OPTIONS = [
    { value: 'isQuickviewVisible', label: 'uieditor.sfbComponent.slider.quickviewOptions.option.display' }
];

export const sliderSettingShape = {
    sliderTitle: PropTypes.string,
    layout: PropTypes.string,
    borderRadius: PropTypes.number,
    size: PropTypes.string,
    tileBackgroundColor: PropTypes.string,
    tileCtaLabel: PropTypes.string,
    displayOptions: PropTypes.array,
    quickviewOptions: PropTypes.array,
    ...productQuickviewSettingsPropTypes
};

export const sliderSchemaForm = {
    sliderTitle: {
        title: 'uieditor.sfbComponent.slider.sliderTitle.title',
        placeholder: 'uieditor.sfbComponent.slider.sliderTitle.placeholder',
        defaultValue: 'uieditor.sfbComponent.slider.sliderTitle.defaultValue',
        type: INPUT,
        required: true,
        validation: {
            pattern: /^[a-zA-Z0-9 ]+$/gm,
            message: 'uieditor.sfbComponent.input.validation.message'
        }
    },
    layout: {
        title: 'uieditor.sfbComponent.slider.layout.title',
        type: RADIO,
        required: false,
        defaultValue: TILE_TYPES[0].value,
        options: TILE_TYPES,
        validation: {
            availableOptions: TILE_TYPES.map(({ value }) => value),
            message: 'uieditor.sfbComponent.radio.validation.message',
            default: 'uieditor.sfbComponent.dropdown.defaultValue'
        }
    },
    size: {
        title: 'uieditor.sfbComponent.sliderComponent.size.title',
        type: RADIO,
        required: true,
        defaultValue: SLIDE_SIZES[0].value,
        options: SLIDE_SIZES,
        validation: {
            availableOptions: SLIDE_SIZES.map(({ value }) => value),
            message: 'uieditor.sfbComponent.radio.validation.message',
            default: 'uieditor.sfbComponent.dropdown.defaultValue'
        }
    },
    tileBackgroundColor: {
        title: 'uieditor.sfbComponent.slider.tileBackgroundColor.title',
        defaultValue: TILE_BACKGROUND,
        conditionalAttributes: {
            isVisible: {
                when: 'layout',
                connector: 'equals',
                value: SQUARE
            }
        },
        type: COLORPICKER
    },
    tileCtaLabel: {
        title: 'uieditor.sfbComponent.slider.tileCtaLabel.title',
        type: INPUT,
        defaultValue: 'Buy Now',
        conditionalAttributes: {
            isVisible: {
                when: 'layout',
                connector: 'equals',
                value: TALL
            }
        }
    },
    sliderSettings: {
        type: GROUP_HEADER,
        title: 'uieditor.sfbComponent.slider.settings.title',
        symbol: 'settings'
    },
    displayOptions: {
        type: CHECKBOX,
        required: false,
        defaultValue: DISPLAY_OPTIONS.map(({ value }) => value),
        options: DISPLAY_OPTIONS,
        conditionalAttributes: {
            isVisible: {
                when: 'layout',
                connector: 'equals',
                value: SQUARE
            }
        },
        validation: {
            availableOptions: DISPLAY_OPTIONS.map(({ value }) => value),
            message: 'uieditor.sfbComponent.checkbox.validation.message'
        }
    },
    borderRadius: {
        title: 'uieditor.sfbComponent.sliderComponent.borderRadius.title',
        type: SLIDER,
        defaultValue: 8,
        options: {
            range: true,
            min: 0,
            max: 35,
            step: 1
        }
    },
    quickviewSettings: {
        type: GROUP_HEADER,
        title: 'uieditor.sfbComponent.sliderComponent.quickview.settings.title',
        symbol: 'quickview'
    },
    quickviewOptions: {
        type: CHECKBOX,
        required: false,
        defaultValue: DISPLAY_QUICKVIEW_OPTIONS.map(({ value }) => value),
        options: DISPLAY_QUICKVIEW_OPTIONS,
        validation: {
            availableOptions: DISPLAY_QUICKVIEW_OPTIONS.map(({ value }) => value),
            message: 'uieditor.sfbComponent.checkbox.validation.message'
        },
        removeMarginBottom: true
    },
    ...productQuickviewSchemaForm
};

SliderComponent.schema = () => ({
    name: 'Slider',
    form: {
        ...sliderSchemaForm
    }
});

SliderComponent.propTypes = {
    name: PropTypes.string,
    staticTitle: PropTypes.string,
    url: PropTypes.string,
    isSlider: PropTypes.bool,
    maxRows: PropTypes.number,
    buttonLabel: PropTypes.string,
    contentType: PropTypes.oneOf([MANUALLY_CURATED_CONTENT_TYPE, DYNAMICALLY_CURATED_CONTENT_TYPE]),
    items: PropTypes.arrayOf(defaultTileProps),
    i18n: PropTypes.shape({
        quickview: PropTypes.string,
        defaultTitlePlaceholder: PropTypes.string,
        defaultDescriptionPlaceholder: PropTypes.string,
        defaultPricePlaceholder: PropTypes.string
    }),
    settings: PropTypes.shape(sliderSettingShape)
};

const SETTINGS = SliderComponent.schema().form;

export const sliderDefaultSettings = {
    sliderTitle: 'Slider title',
    layout: CLASSIC,
    borderRadius: 8,
    size: MEDIUM.value,
    tileBackgroundColor: TILE_BACKGROUND,
    tileCtaLabel: 'Buy now',
    displayOptions: SETTINGS.displayOptions.defaultValue,
    quickviewOptions: SETTINGS.quickviewOptions.defaultValue,
    quickviewDisplayOptions: productQuickviewSchemaForm.quickviewDisplayOptions.defaultValue,
    quickviewViewMode: productQuickviewSchemaForm.quickviewViewMode.defaultValue
};

SliderComponent.defaultProps = {
    items: [],
    name: '',
    url: '',
    staticTitle: undefined,
    isSlider: true,
    maxRows: 1,
    buttonLabel: null,
    contentType: DYNAMICALLY_CURATED_CONTENT_TYPE,
    i18n: {
        quickview: 'Quickview',
        defaultTitlePlaceholder: 'Product Name',
        defaultDescriptionPlaceholder: 'Product description goes here in this space provided.',
        defaultPricePlaceholder: 'From'
    },
    settings: sliderDefaultSettings
};

export default SliderComponent;
