import React, { useRef } from 'react';
import PropTypes from 'prop-types';
import { createNamespace } from '../../../tools/namingTools';
import { useRowHeights } from '../../../tools/heights/useRowHeights';
import { generateUniqueID } from '../../../tools/helpers';
import { useItemsGrid } from '../../../tools/widths/useItemsGrid';
import { ANCHOR_PRODUCT_EDITIONS, ANCHOR_PRODUCT_ADDONS } from '../../../constants/anchors';
import { RenderInclusions, RenderPrice } from '../../product-edition/card/ProductEditionCard';
import SlickSlider from '../../slick-slider/SlickSlider';
import Button from '../../button/Button';
import Badge from '../../badge/Badge';
import Grid from '../../grid/Grid';
import Icon from '../../icon/Icon';
import './styles/ProductAddonList.scss';

const n = createNamespace('ProductAddonList');

export const ProductAddonListComponent = ({
    data,
    selectable,
    selectedAddons,
    onAddonSelect,
    onAddonRemove,
    onViewEditions,
    carouselEnabled
}) => {
    const { addonProducts = {} } = data;
    const { items = [] } = addonProducts;
    const i18n = {
        ...ProductAddonListComponent.defaultProps.data.addonProducts.i18n,
        ...addonProducts.i18n
    };
    const sliderRef = useRef(null);
    const [nameRefs, , heights] = useRowHeights(items.length);
    const [containerRef, gridColumns] = useItemsGrid();

    const pickPriceElements = ({ unit, price, formattedFullPriceElements }) => ({
        unit,
        price: {
            original: price.formatted.originalFullPriceElements,
            formatted: formattedFullPriceElements
        }
    });

    const reducePrices = (flatPrice, unitPrice) => {
        const prices = [];

        // if there is a flat price, that needs to be shown first
        if (flatPrice.price) {
            prices.push(pickPriceElements(flatPrice));
        }

        // if there are unit prices, they need to be added
        if (unitPrice) {
            unitPrice.forEach(price => {
                price.usageRange.forEach(usageRange => {
                    prices.push(pickPriceElements(usageRange));
                });
            });
        }

        return prices;
    };

    const renderButton = (currentAddon = {}) => {
        const isSelectedAddon = !!selectedAddons.filter(addon => (addon.id === currentAddon.id)).length;
        const buttonProps = Object.assign({
            'data-testid': `button:select:addon-${currentAddon.id}`,
            type: isSelectedAddon ? 'emphasis-outline' : 'primary',
            wide: true
        },
        isSelectedAddon && {
            selected: true
        },
        !isSelectedAddon && {
            onClick: () => onAddonSelect(currentAddon)
        });

        return (
            <div {...n('actionsWrapper').props}>
                <div {...n('buttonWrapper').withTestId('actions-container').props}>
                    <Button {...buttonProps}>
                        {isSelectedAddon ? i18n.added : i18n.addToOrder}
                        {isSelectedAddon && <Icon type="check" {...n('selectedCheck').props} />}
                    </Button>
                </div>
                {isSelectedAddon && (
                    <div {...n('actionLink').withTestId().props}>
                        <Button
                            buttonType="link"
                            type="primary"
                            {...n('actionLinkLabel').props}
                            data-testid={`button:remove:addon-${currentAddon.id}`}
                            onClick={() => onAddonRemove(currentAddon)}
                            onKeyDown={() => onAddonRemove(currentAddon)}
                        >
                            {i18n.remove}
                        </Button>
                    </div>
                )}
            </div>
        );
    };

    const renderPlan = (plans, key) => {
        const { costs, setupFees, contractFees, formattedContract, uuid } = plans;
        const { unitPrice, flatPrice = {} } = costs;

        // unify the prices into one array
        const prices = reducePrices(flatPrice, unitPrice);

        // split off the first element
        const [mainPrice, ...otherPrices] = prices;
        const shouldRenderPrices = !!otherPrices.length || !!setupFees.length || !!contractFees.length;

        return (
            <div {...n('addonPriceSection').props} key={`plans-${unitPrice}-${key}`}>
                {mainPrice && (
                    <div {...n('mainPrice').props}>
                        <RenderPrice unit={mainPrice.unit} formattedPrice={mainPrice.price.formatted} />
                        {formattedContract && <Badge label={formattedContract} {...n('contract').withTestId().props} />}
                    </div>
                )}
                {shouldRenderPrices && (
                    <div {...n('otherPrice').props}>
                        {otherPrices && !!otherPrices.length && otherPrices.map(otherPrice => (
                            <RenderPrice
                                unit={otherPrice.unit}
                                formattedPrice={otherPrice.price.formatted}
                                key={generateUniqueID([...otherPrice.price.formatted, uuid])}
                                isSecondary
                            />
                        ))}
                        {setupFees && !!setupFees.length && setupFees.map(setupFee => (
                            <RenderPrice
                                formattedPrice={setupFee.formattedFullPriceElements}
                                key={generateUniqueID([...setupFee.formattedFullPriceElements, uuid])}
                                isSecondary
                                textSeparator={' '}
                            />
                        ))}
                        {contractFees && !!contractFees.length && contractFees.map(contractFee => (
                            <RenderPrice
                                formattedPrice={contractFee.formattedFullPriceElements}
                                key={generateUniqueID([...contractFee.formattedFullPriceElements, uuid])}
                                isSecondary
                                textSeparator={' '}
                            />
                        ))}
                    </div>
                )}
            </div>
        );
    };

    const renderEdition = (edition, key) => {
        const { plans } = edition;
        if (!plans) {
            return null;
        }
        return (
            <React.Fragment key={`edition-${plans}-${key}`}>
                {plans.map(renderPlan)}
            </React.Fragment>
        );
    };

    const renderAddon = (addon, index) => {
        const { edition, name } = addon;
        const {
            bullets
        } = edition[0];
        const inclusions = addon.edition[0].items; // Can't deconstruct object, "items" is already declared
        // height of title comes from hook, as all title within the same row must be as high as the highest one
        const addonVariables = { 'addon-name-height': heights.main[index] && `${heights.main[index]}px` };

        return (
            <div {...n('container').withVariables(addonVariables).withTestId().props} key={`addon-${name}-${index}`}>
                <div {...n('main', selectable && 'with-buttons').props}>
                    <div {...n('text').props}>
                        {name && <div {...n('name').withTestId().props} ref={nameRefs[index]}>{name}</div>}
                        {edition.map(renderEdition)}
                        {inclusions && <RenderInclusions items={inclusions} i18n={i18n} />}
                        <p {...n('description').withTestId().props}>
                            {bullets[0] && bullets[0].content}
                        </p>
                    </div>
                    {selectable ? renderButton(addon) : null}
                </div>
            </div>
        );
    };

    return !!items.length && (
        <section {...n('wrapper').props} id={ANCHOR_PRODUCT_ADDONS} ref={containerRef}>
            <div {...n('header').props}>
                {i18n.addons && <h1 {...n('title', i18n.viewEditions && 'with-link').props}>{i18n.addons}</h1>}
                {i18n.viewEditions && (
                    <a href={`#${ANCHOR_PRODUCT_EDITIONS}`} {...n('headerLink').props} onClick={() => onViewEditions()}>{i18n.viewEditions}</a>
                )}
            </div>
            <div {...n('content').props}>
                {carouselEnabled ? (
                    <SlickSlider
                        settings={{
                            ref: sliderRef,
                            dots: false,
                            infinite: false,
                            draggable: false
                        }}
                    >
                        {items.map(renderAddon)}
                    </SlickSlider>
                ) : (
                    <Grid maxCol={gridColumns}>
                        {items.map(renderAddon)}
                    </Grid>
                )}
            </div>
        </section>
    );
};

ProductAddonListComponent.schema = () => ({
    name: 'ProductAddons',
    title: 'uieditor.sfbComponent.addonsList.title',
    form: {},
    marketplaceData: {
        addonProducts: 'application.addonProducts'
    },
    requiredDataFunc: ({ addonProducts }) => (addonProducts && addonProducts.length)
});

export const defaultAddonsProps = PropTypes.shape({
    items: PropTypes.arrayOf(PropTypes.shape({
        name: PropTypes.string,
        edition: PropTypes.arrayOf(PropTypes.shape({
            plans: PropTypes.arrayOf(PropTypes.shape({
                costs: PropTypes.shape({
                    unitPrice: PropTypes.arrayOf(PropTypes.shape({
                        usageRange: PropTypes.arrayOf(PropTypes.shape({
                            formattedFullPriceElements: PropTypes.arrayOf(PropTypes.string)
                        }))
                    }))
                }),
                contractLength: PropTypes.number,
                formattedContract: PropTypes.oneOfType([
                    PropTypes.string,
                    PropTypes.bool
                ])
            })),
            bullets: PropTypes.arrayOf(PropTypes.shape({
                content: PropTypes.string
            }))
        }))
    })),
    i18n: PropTypes.shape({
        addons: PropTypes.string,
        startingFrom: PropTypes.string,
        seePricingPlans: PropTypes.string,
        availableAddons: PropTypes.string,
        inclusions: PropTypes.string
    })
});

ProductAddonListComponent.propTypes = {
    data: PropTypes.shape({
        addonProducts: defaultAddonsProps
    }),
    selectedAddons: PropTypes.arrayOf(
        defaultAddonsProps
    ),
    selectable: PropTypes.bool,
    onAddonSelect: PropTypes.func,
    onAddonRemove: PropTypes.func,
    onViewEditions: PropTypes.func,
    carouselEnabled: PropTypes.bool
};

ProductAddonListComponent.defaultProps = {
    selectable: false,
    onAddonSelect: () => {},
    onAddonRemove: () => {},
    onViewEditions: () => {},
    data: {
        addonProducts: {
            items: [],
            i18n: {
                addons: 'Add-ons',
                startingFrom: 'Starting From',
                seePricingPlans: 'See Pricing Plans',
                availableAddons: 'Available Add-ons',
                selectAddonsTitle: 'Add-ons available for this plan',
                added: 'Added',
                addToOrder: 'Add to Order',
                inclusions: 'Included'
            }
        }
    },
    selectedAddons: [],
    carouselEnabled: false
};

export default ProductAddonListComponent;
