import React, { useState, useEffect, useCallback, useMemo } from 'react';
import useResizeObserver from 'use-resize-observer';
import PropTypes from 'prop-types';
import withListener from '../withListener';
import { createNamespace } from '../../tools/namingTools';
import { bSmall, bMedium, bLarge } from '../../constants/breakpoints';
import { useContainerWidth } from '../../tools/widths/useContainerWidth';
import { ANCHOR_PRODUCT_CONFIGURATOR } from '../../constants/anchors';

import ProductAddonList, { defaultAddonsProps } from '../../atoms/product-addon/list/ProductAddonList';
import ProductEditionList, { defaultEditionsProps } from '../../atoms/product-edition/list/ProductEditionList';
import { SelectionPreviewComponent, defaultSelectionPreviewProps } from '../../atoms/selection-preview/SelectionPreview';
import { getUrlParams } from '../../tools/urlTools';
import GridContainer from '../../atoms/grid-container/GridContainer';
import Button from '../../atoms/button/Button';
import ButtonPageScroll from '../../atoms/button-page-scroll/ButtonPageScroll';
import Icon from '../../atoms/icon/Icon';

import './styles/ProductConfigurator.scss';

const n = createNamespace('ProductConfigurator');

const getUrlParamValue = key => {
    const urlParams = getUrlParams();

    if (key && urlParams[key] && urlParams[key].length) {
        return urlParams[key][0] !== 'false';
    }

    return true;
};

const getFilteredAddonsForEdition = (addons = [], selectedEditionId) => addons
    .filter(({ parentEditionId }) => !parentEditionId || parentEditionId === selectedEditionId);

const addSelectedAddons = (newAddon, addons) => [].concat(addons, [newAddon]);

const removeSelectedAddon = (removedAddon, addons) => addons.filter(addon => (addon.id !== removedAddon.id));

const getDefaultSelected = (selectedEdition, allEditions) => (
    selectedEdition || allEditions.find(edition => edition.primary) || allEditions[0]
);

export const ProductConfiguratorComponent = ({ data }) => {
    const {
        editions = {},
        addonProducts = {},
        configurator = { selectedEdition: null, backAction: {}, i18n: {} },
        callToActions = {},
        summary = {}
    } = data;

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

    const i18n = Object.assign({}, ProductConfiguratorComponent.defaultProps.data.i18n, data.i18n);
    const componentVariables = {};
    const containerModifiers = [isMobileXL && 'with-preview-sticky-bottom'];
    const { logoSrc } = summary;
    const [isEditionsVisible, setEditionsVisibleState] = useState(getUrlParamValue('showEditions'));
    const [selectedEdition, setSelectedEdition] = useState(
        getDefaultSelected(configurator.selectedEdition, editions.items)
    );
    const [selectedAddons = [], setSelectedAddons] = useState(configurator.selectedAddons);
    const [showBackTopButton, setBackTopButtonState] = useState(false);
    const [isPreviewCondensed, setPreviewCondensedState] = useState(false);
    const [isPreviewFullscreen, setPreviewFullscreenState] = useState(false);
    const [filteredAddons, setFilteredAddons] = useState(
        getFilteredAddonsForEdition(addonProducts.items, selectedEdition.id)
    );

    const [uncondensedHeight, setUncondensedHeight] = useState(0);
    const [previewHeight, setPreviewHeight] = useState(0);

    const { ref: selectionPreviewRef } = useResizeObserver({
        onResize: ({ height }) => {
            if (!isPreviewCondensed && height > uncondensedHeight) {
                setUncondensedHeight(height);
            }
            if (isMobileXL) {
                setUncondensedHeight(0);
                setPreviewHeight(height);
            }
        }
    });

    const getCondensedState = useCallback(() => () => {
        if (isMobile) {
            return true;
        }
        return !isMobileXL ? window.scrollY > 0 : false;
    }, [isMobileXL, isMobile]);

    const onScroll = useCallback(() => {
        const isCondensed = getCondensedState();
        setPreviewCondensedState(isCondensed);
        setBackTopButtonState(window.scrollY > window.innerHeight / 2);
    }, [getCondensedState]);

    useEffect(() => {
        onScroll();
        window.addEventListener('scroll', onScroll);

        return () => {
            window.removeEventListener('scroll', onScroll);
        };
    }, [onScroll]);

    const changeEdition = newEdition => {
        setSelectedEdition(newEdition);
        setSelectedAddons([]);
        setUncondensedHeight(0);
        setFilteredAddons(getFilteredAddonsForEdition(addonProducts.items, newEdition.id));
    };

    const changeAddons = newAddons => {
        setUncondensedHeight(0);
        setSelectedAddons(newAddons);
    };

    const showEditions = () => {
        setEditionsVisibleState(true);
    };

    const renderBackButton = useMemo(() => (
        <div {...n('backAction').props}>
            <a {...n('backActionLink').withTestId().props} href={configurator.backAction.url}>
                <Icon type="caretLeft" {...n('backActionIcon').props} />
                <span {...n('backActionLabel').withTestId().withE2EId().props}>{configurator.backAction.label}</span>
            </a>
        </div>
    ), [configurator.backAction]);

    const renderBackTopButton = (
        <div {...n('btnBackTop', showBackTopButton && 'visible').props}>
            <ButtonPageScroll text={i18n.backTop} />
        </div>
    );

    const renderSelectionPreview = () => {
        const modifiers = [isMobileXL ? 'sticky-bottom' : 'sticky-top'];

        if (isPreviewCondensed) {
            modifiers.push('is-condensed');
            modifiers.push('with-boxshadow');
        }

        if (isPreviewFullscreen) {
            modifiers.push('fullscreen');
        }

        const style = uncondensedHeight ? { stickyheight: `${uncondensedHeight}px` } : null;

        return (
            <div {...n('preview-sticky', modifiers).withVariables(style).props}>
                <div {...n('preview', modifiers).props} ref={selectionPreviewRef}>
                    {!isMobileXL && renderBackButton}
                    <SelectionPreviewComponent
                        data={{
                            configurator,
                            selectedEdition,
                            selectedAddons,
                            callToActions,
                            productImgSrc: logoSrc
                        }}
                        isCondensed={isPreviewCondensed}
                        isFullscreen={isTablet && isPreviewFullscreen}
                        onCollapseAddons={isAddonsCollapsed => {
                            if (isMobileXL) {
                                setPreviewFullscreenState(isAddonsCollapsed);
                            }
                        }}
                    />
                </div>
            </div>
        );
    };

    if (previewHeight) {
        componentVariables.previewHeight = `${previewHeight}px`;
    }

    return (
        <div {...n('container', containerModifiers).withVariables(componentVariables).props} id={ANCHOR_PRODUCT_CONFIGURATOR} ref={containerRef}>
            {!isMobileXL && renderSelectionPreview()}
            {isMobileXL && renderBackButton}
            <div {...n('configurator').props}>
                {isEditionsVisible ? (
                    <div {...n('editions').props}>
                        <ProductEditionList
                            data={{
                                editions: editions.items,
                                i18n: {
                                    ...editions.i18n,
                                    viewAddons: i18n.viewAddons
                                }
                            }}
                            hasAddons={!!addonProducts.items.length}
                            configuratorMode
                            carouselEnabled={isMobileXL}
                            carouselSlidesToShow={isMobile ? 1 : 2}
                            selectedEditionId={selectedEdition.id}
                            onEditionSelect={(newEdition = {}) => changeEdition(newEdition)}
                        />
                    </div>
                ) : (
                    <GridContainer>
                        <div {...n('btnShowEditions').props}>
                            <Button
                                buttonType="link"
                                data-testid="button:showEditions"
                                type="primary"
                                onClick={() => showEditions()}
                            >
                                {i18n.changeEdition}
                                <Icon type={!isEditionsVisible ? 'caretUp' : 'caretDown'} {...n('btnShowEditionsIcon').props} />
                            </Button>
                        </div>
                    </GridContainer>
                )}
                {!!addonProducts.items.length && (
                    <div {...n('addons').props}>
                        <ProductAddonList
                            data={{
                                addonProducts: {
                                    i18n: {
                                        ...addonProducts.i18n,
                                        addons: i18n.availableAddons,
                                        viewEditions: i18n.viewEditions
                                    },
                                    items: filteredAddons
                                }
                            }}
                            selectedAddons={selectedAddons}
                            selectable
                            onAddonSelect={(currentAddon = {}) => {
                                changeAddons(addSelectedAddons(currentAddon, selectedAddons));
                            }}
                            onViewEditions={() => showEditions()}
                            onAddonRemove={(currentAddon = {}) => {
                                changeAddons(removeSelectedAddon(currentAddon, selectedAddons));
                            }}
                        />
                    </div>
                )}
            </div>
            {isMobileXL && renderSelectionPreview()}
            {isMobileXL && renderBackTopButton}
        </div>
    );
};

ProductConfiguratorComponent.schema = () => ({
    name: 'ProductConfigurator',
    title: 'uieditor.sfbComponent.productConfigurator.title',
    form: {},
    marketplaceData: {
        editions: 'application.editions',
        addonProducts: 'application.addonProducts',
        configurator: 'application.configurator',
        callToActions: 'application.callToActions',
        summary: 'application.summary'
    }
});

ProductConfiguratorComponent.propTypes = {
    data: PropTypes.shape({
        addonProducts: defaultAddonsProps,
        editions: defaultEditionsProps,
        configurator: defaultSelectionPreviewProps.configurator,
        callToActions: defaultSelectionPreviewProps.callToActions,
        i18n: PropTypes.shape({
            selectEditionTitle: PropTypes.string,
            availableAddons: PropTypes.string,
            changeEdition: PropTypes.string,
            viewEditions: PropTypes.string,
            viewAddons: PropTypes.string,
            backTop: PropTypes.string
        }),
        summary: PropTypes.shape({
            logoSrc: PropTypes.string
        })
    })
};

ProductConfiguratorComponent.defaultProps = {
    data: {
        editions: {
            items: []
        },
        addonProducts: {
            items: []
        },
        callToActions: {
            items: []
        },
        configurator: {},
        i18n: {
            selectEditionTitle: 'Select an edition for this product',
            availableAddons: 'Available add-ons',
            changeEdition: 'Change Edition',
            viewEditions: 'View Editions',
            viewAddons: 'View Add-ons',
            backTop: 'Back to top'
        },
        summary: {
            logoSrc: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mO8ff12PQAIMAMONuPA0AAAAABJRU5ErkJggg=='
        }
    }
};

export default withListener(ProductConfiguratorComponent);
