import React, { useState, forwardRef, useRef } from 'react';
import PropTypes from 'prop-types';
import { createNamespace } from '../../../tools/namingTools';
import { stripSlash, replacePlaceholders } from '../../../tools/stringTools';
import { generateUniqueID } from '../../../tools/helpers';
import { parseDiscountOff } from '../../../tools/components/productEditionCard';
import { useCarouselA11yVisible } from '../../../tools/a11y/useCarouselA11yVisible';
import Badge from '../../badge/Badge';
import Bullet from '../../bullet/Bullet';
import Button from '../../button/Button';
import Icon from '../../icon/Icon';
import Pricing from '../../pricing/Pricing';
import './styles/ProductEditionCardV2.scss';
import { marketplaceProductDataStore } from '../../../store/marketplaceProductData';

const n = createNamespace('ProductEditionCardV2');

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

const getPriceLimitLabel = priceUnit => {
    if (!priceUnit) {
        return null;
    }

    const hasOnlyMin = priceUnit.min === 1 && !priceUnit.max;
    const shouldRenderLimitLabel = priceUnit.limitLabel && !hasOnlyMin;

    return shouldRenderLimitLabel ? priceUnit.limitLabel : null;
};

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

    // if there is a flat price, that needs to be shown first
    if (flatPrice) {
        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 RenderPlan = ({ plan }) => (
    <li {...n('section').props} key={plan.uuid}>
        {Object.keys(plan.costs).length ? <RenderCosts plan={plan} /> : null}
    </li>
);

const RenderCosts = ({ plan: {
    costs: { flatPrice, unitPrice },
    setupFees,
    contractFees,
    formattedContract,
    uuid
} }) => {
    // 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 (
        <React.Fragment>
            {mainPrice && (
                <div {...n('mainPrice').props}>
                    <RenderPrice unit={mainPrice.unit} formattedPrice={mainPrice.price.formatted} />
                    {formattedContract && <Badge label={formattedContract} {...n('contract').props} />}
                    {mainPrice.discount.discountOff && (
                        <div {...n('mainPriceDiscount').props}>
                            <RenderDiscount
                                discountOff={mainPrice.discount.discountOff}
                                original={mainPrice.price.original}
                            />
                        </div>
                    )}
                </div>
            )}
            {shouldRenderPrices && (
                <ul {...n('additionalPrice').props}>
                    {Boolean(otherPrices && otherPrices.length) && otherPrices.map(otherPrice => (
                        <li {...n('pricingItem').props} key={generateUniqueID([...otherPrice.price.formatted, uuid])}>
                            <RenderPrice
                                unit={otherPrice.unit}
                                formattedPrice={otherPrice.price.formatted}
                                isSecondary
                                discount={(
                                    <RenderDiscount
                                        discountOff={otherPrice.discount.discountOff}
                                        original={otherPrice.price.original}
                                        isUnitPrice
                                    />
                                )}
                            />
                        </li>
                    ))}
                    {Boolean(setupFees && setupFees.length) && setupFees.map(setupFee => (
                        <li>
                            <RenderPrice
                                formattedPrice={setupFee.formattedFullPriceElements}
                                key={generateUniqueID([...setupFee.formattedFullPriceElements, uuid])}
                                isSecondary
                                textSeparator={' '}
                                discount={(
                                    <RenderDiscount
                                        discountOff={setupFee.price.formatted.discountOff}
                                        original={setupFee.price.formatted.originalFullPriceElements}
                                        isUnitPrice
                                    />
                                )}
                            />
                        </li>
                    ))}
                    {Boolean(contractFees && contractFees.length) && contractFees.map(contractFee => (
                        <li>
                            <RenderPrice
                                formattedPrice={contractFee.formattedFullPriceElements}
                                key={generateUniqueID([...contractFee.formattedFullPriceElements, uuid])}
                                isSecondary
                                textSeparator={' '}
                                discount={(
                                    <RenderDiscount
                                        discountOff={contractFee.price.formatted.discountOff}
                                        original={contractFee.price.formatted.originalFullPriceElements}
                                        isUnitPrice
                                    />
                                )}
                            />
                        </li>
                    ))}
                </ul>
            )}
        </React.Fragment>
    );
};

export const RenderPrice = ({
    formattedPrice: [price, priceUnit, frequency],
    isSecondary,
    textSeparator,
    unit,
    discount,
    planType
}) => {
    const priceLimit = getPriceLimitLabel(unit);
    const modifiers = [
        isSecondary ? 'secondary' : 'primary',
        isSecondary && 'with-icon',
        planType
    ];

    return (
        <div {...n('pricing', modifiers).withTestId().withE2EId().props}>
            {isSecondary && (
                <Icon type="plus" {...n('pricingPlusIcon').props} aria-hidden="true" />
            )}
            <Pricing
                price={price}
                frequency={frequency ? stripSlash(frequency) : ''}
                unit={priceUnit ? stripSlash(priceUnit) : ''}
                isEmphasis={!isSecondary}
                textSeparator={textSeparator}
                priceExtraClass={n('pricingValue').props.className}
                isMultipleLine
                isConfigurator
            />
            {priceLimit && (
                <div {...n('pricingLimit').withTestId().props}>{priceLimit}</div>
            )}
            {discount && (
                <div {...n('pricingDiscount').props}>{discount}</div>
            )}
        </div>
    );
};

export const RenderDiscount = ({
    original,
    discountOff,
    isUnitPrice
}) => {
    if (!discountOff) {
        return null;
    }

    const [price, unit, frequency] = original;

    return (
        <div {...n('discountSection', isUnitPrice ? 'unit' : null).withTestId().props}>
            <div {...n('discountLabel').withTestId().props}>
                <Pricing price={price} unit={isUnitPrice ? unit : ''} frequency={isUnitPrice ? frequency : ''} textSeparator="" />
            </div>
            {discountOff && (<div {...n('discountOff').withTestId().props}>{parseDiscountOff(discountOff)}</div>)}
        </div>
    );
};

export const RenderInclusions = ({ items, i18n }) => {
    if (!items || !items.length) {
        return null;
    }

    return (
        <div {...n('inclusions').props}>
            <span {...n('sectionTitle').withTestId('inclusionsTitle').withE2EId('inclusionsTitle').props}>{i18n.inclusions}</span>
            {items.map(({ content }) => (
                <div key={`includes-${content}`} {...n().withTestId('inclusionsItem').withE2EId('inclusionsItem').props}>
                    {content}
                </div>
            ))}
        </div>
    );
};

export const RenderAudience = ({ audience, i18n }) => {
    if (!audience || !audience.length) {
        return null;
    }

    return (
        <div {...n('audience').withTestId().props}>
            <span {...n('sectionTitle').withTestId('audienceTitle').withE2EId('audienceTitle').props}>{i18n.audience}</span>
            {audience.map(({ content }) => (
                <div key={`audience-${content}`} {...n('section').withTestId('audienceItem').withE2EId('audienceItem').props}>
                    {content}
                </div>
            ))}
        </div>
    );
};

const RenderBullet = ({ bullet }) => {
    const { content, tooltip } = bullet;
    return (
        <li {...n('bullet').withTestId().withE2EId('bullet').props}>
            <Bullet content={content} tooltip={tooltip} iconType="check" />
        </li>
    );
};

const RenderBullets = ({ bullets }) => {
    if (!bullets || !bullets.length) {
        return null;
    }
    return (
        <ul {...n('bullets').props}>
            {bullets.map((bullet, index) => (
                <RenderBullet bullet={bullet} key={generateUniqueID([bullet.content, index])} />
            ))}
        </ul>
    );
};

const RenderAdditionalProducts = ({ additionalProducts, i18n, isCollapse }) => {
    const additionalProductsNotInCollapsedState = additionalProducts.length > 1 ? additionalProducts.length - 2 : 0;
    const productString = additionalProductsNotInCollapsedState === 1 ? i18n.extraProduct : i18n.extraProducts;
    const formattedProductString = replacePlaceholders(
        productString, { productCount: additionalProductsNotInCollapsedState }
    );
    return (
        <div {...n('additionalProducts').props}>
            {additionalProducts.length > 1 ? (
                <div {...n('additionalProductsTitle').props}>{i18n.additionalProductsRequired}</div>
            ) : (
                <div {...n('additionalProductsTitle').props}>{i18n.additionalProductRequired}</div>
            )}
            <ul {...n('additionalProducts').props} aria-label={i18n.additionalProductsRequired}>
                <li {...n('additionalProductsList').props}>
                    {!isCollapse && additionalProducts.map((product, index) => (
                        <div {...n(`additonalProduct-${index}`).props} key={generateUniqueID(['association', index])}>
                            {`- ${product}`}
                        </div>
                    ))}
                    {isCollapse && additionalProducts.slice(0, 2).map((product, index) => (
                        <div {...n(`additonalProduct-${index}`).props} key={generateUniqueID(['association', index])}>
                            {`- ${product}`}
                        </div>
                    ))}
                    {isCollapse && additionalProducts.length > 2
                        && additionalProductsNotInCollapsedState > 0 && (
                        <div {...n('additonalProductOverflowString').withTestId().props}>
                            {`+ ${formattedProductString}`}
                        </div>
                    )}
                </li>
            </ul>
        </div>
    );
};

export const ProductEditionCardComponent = forwardRef((
    {
        data: {
            id,
            productEditionRefId,
            name,
            description,
            primary,
            plans,
            audience,
            items,
            bullets,
            freeTrialDuration,
            freeTrialType
        },
        configuratorMode,
        isDetailCollapsed,
        isSelected,
        onSelect,
        carouselEnabled,
        onExpand,
        titleHeight,
        height,
        i18n,
        isIncludedHidden
    },
    forwardedRefs
) => {
    const containerRef = useRef();
    const [isCarouselA11yVisible] = useCarouselA11yVisible(containerRef);
    const isA11yVisible = isCarouselA11yVisible || !carouselEnabled;
    const buttonsTabIndex = isA11yVisible ? 0 : -1;
    const [isCollapse, setCollapseState] = useState(true);
    const { marketplaceProductData } = marketplaceProductDataStore();
    const editions = marketplaceProductData.marketplaceProduct?.editions || [];
    const currentEdition = editions?.filter(edition => edition.id === productEditionRefId);
    const additionalProducts = [];
    const upstreamAssociations = currentEdition[0]?.upstreamAssociations || [];
    upstreamAssociations.map(
        association => additionalProducts.push(association.upstreamAssociationProductName)
    );
    const {
        titleRef,
        mainRef
    } = forwardedRefs || {};

    if (isDetailCollapsed !== null && isDetailCollapsed !== isCollapse) {
        setCollapseState(isDetailCollapsed);
    }

    const toggleDetails = () => {
        if (onExpand) {
            onExpand();
        } else {
            setCollapseState(!isCollapse);
        }
    };

    const containerModifiers = [
        primary && 'with-ribbon',
        isSelected && !isCollapse && 'is-selected-with-details',
        !isSelected && 'not-selected'
    ];
    const containerVariables = {
        infoHeight: (height && `${height}px`) || 'auto',
        titleHeight: (titleHeight && `${titleHeight}px`) || 'auto'
    };

    const mainModifiers = [
        configuratorMode && 'configurator-mode',
        primary && 'with-ribbon',
        configuratorMode && 'with-buttons',
        !isCollapse && 'with-details',
        isSelected && 'is-selected'
    ];

    // Remove empty costs from the plans array
    const productPlans = plans && plans.length ? plans.reduce((previous, current) => {
        if (current.costs && Object.keys(current.costs).length !== 0) {
            previous.push(current);
        }

        return previous;
    }, []) : null;

    const detailsSectionId = generateUniqueID([id, name]);

    return (
        <div {...n('container', containerModifiers).withVariables(containerVariables).withTestId().withE2EId().props} ref={containerRef} onClick={onSelect} onKeyPress={onSelect}>
            <article {...n('main', mainModifiers).props} ref={mainRef}>
                {primary && <div {...n('ribbon').withTestId().withE2EId().props}>{i18n.mostPopular}</div>}
                <div {...n('header').props} ref={titleRef}>
                    <h3 {...n('title').withTestId().withE2EId().props}>{name}</h3>
                    {description && <p {...n('description').withTestId().withE2EId().props}>{description}</p>}
                    {freeTrialDuration && freeTrialType && (
                        <div {...n('trialContainer').props}>
                            <Badge {...n('trialBadge').props} label={`${freeTrialDuration} ${freeTrialType} ${i18n.freeTrial}`} type="success" />
                        </div>
                    )}
                </div>
                {productPlans && productPlans.length ? (
                    <ul {...n('plans').props} aria-label={i18n.availablePricing}>
                        {plans.map(plan => <RenderPlan plan={plan} key={plan.uuid} />)}
                    </ul>
                ) : ''}
                {additionalProducts && additionalProducts.length ? (
                    <RenderAdditionalProducts
                        additionalProducts={additionalProducts}
                        isCollapse={isCollapse}
                    />
                ) : ''}
                <div {...n('actionsWrapper').props}>
                    {configuratorMode && (
                        <div {...n('actionLink').withE2EId().props}>
                            <Button
                                buttonType="link"
                                type="primary"
                                onClick={toggleDetails}
                                onKeyDown={toggleDetails}
                                {...n('actionLinkLabel').withTestId().withClass('btn-toggle-details').props}
                                aria-expanded={!isCollapse ? 'true' : 'false'}
                                aria-controls={detailsSectionId}
                                aria-label={`${name}: ${isCollapse ? i18n.viewDetails : i18n.hideDetails}`}
                                tabIndex={buttonsTabIndex}
                            >
                                {isCollapse ? i18n.viewDetails : i18n.hideDetails}
                            </Button>
                        </div>
                    )}
                </div>
            </article>
            {!isCollapse && (
                <div {...n('details', [!isCollapse && isSelected && 'view']).withTestId().withE2EId().props} id={detailsSectionId}>
                    {items && !isIncludedHidden && <RenderInclusions items={items} i18n={i18n} />}
                    {audience && <RenderAudience audience={audience} i18n={i18n} />}
                    {bullets && <RenderBullets bullets={bullets} />}
                </div>
            )}
        </div>
    );
});


const i18nDefault = {
    audience: 'Ideal For',
    inclusions: 'Included',
    mostPopular: 'Most Popular',
    select: 'Select',
    selected: 'Selected',
    hideDetails: 'Hide Details',
    viewDetails: 'View Details',
    freeTrial: 'free trial',
    availablePricing: 'Available Pricing:',
    additionalProductsRequired: 'Additional products required:',
    additionalProductRequired: 'Additional product required:',
    extraProduct: '{productCount} other product',
    extraProducts: '{productCount} other products'
};

const planDefault = {
    costs: {},
    setupFees: [],
    constractFees: [],
    formattedContract: '',
    uuid: ''
};

const bulletSchema = PropTypes.shape({
    content: PropTypes.string,
    tooltip: PropTypes.string
});

const bulletsSchema = PropTypes.arrayOf(bulletSchema);

const i18nSchema = PropTypes.shape({
    audience: PropTypes.string,
    inclusions: PropTypes.string,
    mostPopular: PropTypes.string,
    select: PropTypes.string,
    selected: PropTypes.string,
    hideDetails: PropTypes.string,
    viewDetails: PropTypes.string,
    freeTrial: PropTypes.string,
    availablePricing: PropTypes.string,
    additionalProductsRequired: PropTypes.string,
    additionalProductRequired: PropTypes.string,
    extraProduct: PropTypes.string,
    extraProducts: PropTypes.string
});

const callToActionSchema = PropTypes.shape({
    label: PropTypes.string,
    tooltip: PropTypes.string,
    isPrimary: PropTypes.bool,
    enabled: PropTypes.bool,
    url: PropTypes.string,
    actionType: PropTypes.string,
    popup: PropTypes.shape(),
    isSecondary: PropTypes.bool
});

const priceTypeSchema = PropTypes.shape({
    formattedFullPriceElements: PropTypes.arrayOf(PropTypes.string),
    originalFullPriceElements: PropTypes.arrayOf(PropTypes.string),
    unit: PropTypes.shape({
        limitLabel: PropTypes.string
    })
});

const planSchema = PropTypes.shape({
    costs: PropTypes.shape({
        flatPrice: priceTypeSchema,
        unitPrice: PropTypes.arrayOf(priceTypeSchema)
    }),
    setupFees: PropTypes.arrayOf(
        PropTypes.shape({
            formattedFullPriceElements: PropTypes.arrayOf(PropTypes.string)
        })
    ),
    contractFees: PropTypes.arrayOf(
        PropTypes.shape({
            formattedFullPriceElements: PropTypes.arrayOf(PropTypes.string)
        })
    ),
    formattedContract: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
    uuid: PropTypes.string
});

const plansSchema = PropTypes.arrayOf(planSchema);

const itemsSchema = PropTypes.arrayOf(
    PropTypes.shape({
        content: PropTypes.string
    })
);

const dataAudienceSchema = PropTypes.arrayOf(
    PropTypes.shape({
        content: PropTypes.string
    })
);

RenderBullets.propTypes = {
    bullets: bulletsSchema
};

RenderBullets.defaultProps = {
    bullets: []
};

RenderBullet.propTypes = {
    bullet: bulletSchema
};

RenderBullet.defaultProps = {
    bullet: {}
};

RenderAudience.propTypes = {
    audience: dataAudienceSchema,
    i18n: i18nSchema
};

RenderAudience.defaultProps = {
    audience: [],
    i18n: i18nDefault
};

RenderInclusions.propTypes = {
    items: itemsSchema,
    i18n: i18nSchema
};

RenderInclusions.defaultProps = {
    items: [],
    i18n: i18nDefault
};

RenderAdditionalProducts.propTypes = {
    additionalProducts: PropTypes.array,
    i18n: i18nSchema,
    isCollapse: PropTypes.bool
};

RenderAdditionalProducts.defaultProps = {
    additionalProducts: [],
    i18n: i18nDefault,
    isCollapse: false
};
RenderDiscount.propTypes = {
    discountOff: PropTypes.string,
    original: PropTypes.arrayOf(PropTypes.string),
    isUnitPrice: PropTypes.bool
};

RenderDiscount.defaultProps = {
    discountOff: '',
    original: null,
    isUnitPrice: false
};

RenderPrice.propTypes = {
    formattedPrice: PropTypes.arrayOf(PropTypes.string),
    isSecondary: PropTypes.bool,
    textSeparator: PropTypes.string,
    unit: PropTypes.shape({
        min: PropTypes.number,
        max: PropTypes.number,
        limitLabel: PropTypes.string
    }),
    discount: PropTypes.element,
    planType: PropTypes.oneOf(['costs', 'setupFees', 'contractFees'])
};

RenderPrice.defaultProps = {
    formattedPrice: [],
    isSecondary: false,
    textSeparator: '/',
    unit: null,
    discount: null,
    planType: 'costs'
};

RenderPlan.propTypes = {
    plan: planSchema
};

RenderPlan.defaultProps = {
    plan: PropTypes.shape(planDefault)
};

RenderCosts.propTypes = {
    plan: planSchema
};

RenderCosts.defaultProps = {
    plan: PropTypes.shape(planDefault)
};

export const defaultEditionsProps = PropTypes.shape({
    id: PropTypes.number,
    productEditionRefId: PropTypes.string,
    name: PropTypes.string,
    description: PropTypes.string,
    primary: PropTypes.bool,
    plans: plansSchema,
    audience: dataAudienceSchema,
    items: itemsSchema,
    bullets: PropTypes.arrayOf(
        PropTypes.shape({
            content: PropTypes.string,
            tooltip: PropTypes.string
        })
    ),
    callToAction: callToActionSchema,
    freeTrialDuration: PropTypes.number,
    freeTrialType: PropTypes.string
});

ProductEditionCardComponent.schema = () => ({
    name: 'Product Editions',
    form: {}
});

ProductEditionCardComponent.propTypes = {
    data: defaultEditionsProps,
    i18n: i18nSchema,
    titleHeight: PropTypes.number,
    height: PropTypes.number,
    isSelected: PropTypes.bool,
    carouselEnabled: PropTypes.bool,
    configuratorMode: PropTypes.bool,
    isDetailCollapsed: PropTypes.bool,
    onSelect: PropTypes.func,
    onExpand: PropTypes.func,
    isIncludedHidden: PropTypes.bool
};

ProductEditionCardComponent.defaultProps = {
    data: {},
    i18n: i18nDefault,
    titleHeight: null,
    height: null,
    isSelected: false,
    carouselEnabled: false,
    configuratorMode: false,
    isDetailCollapsed: null,
    onSelect: () => { },
    onExpand: null,
    isIncludedHidden: false
};

export default ProductEditionCardComponent;
