import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import { createNamespace } from '../../tools/namingTools';
import { RADIO } from '../../constants/schemaComponentTypes';
import { parseToBool, generateUniqueID } from '../../tools/helpers';
import { HORIZONTAL, TALL, SQUARE, TILE_TYPES, TILE_MAX_MERCHANDISING_BADGES } from '../../constants/components/tile';
import { TILE_BACKGROUND, TILE_TEXT_BACKGROUND } from '../../constants/backgroundSettings';
import { PRODUCT_DEFAULT_IMG } from '../../constants/images';
import { TAG_TYPE_CATEGORY } from '../../constants/tags';
import { tileShape } from '../../constants/components/prop-types/tile';
import { i18nShape } from '../../constants/prop-types/productLists/i18n';
import { i18nMock } from '../../../mocks/productLists/i18n';
import { darken } from '../../tools/colorHelper';
import { renderMerchandisingBadges } from '../../tools/rendering/merchandisingBadges';
import { SMALL, LARGE } from '../../constants/sizes';
import Rating from '../rating/Rating';
import Badge from '../badge/Badge';
import './styles/Tile.scss';

const n = createNamespace('Tile');

const Tile = ({
    data: {
        id,
        name,
        vendorName,
        blurb,
        description,
        hidePricings,
        startingPrice = {},
        rating,
        ratingCaption,
        numRatings,
        iconUrl,
        iconSrcset = {},
        url,
        tags,
        productRibbon,
        badges = [],
        merchandisingBadges = []
    },
    settings,
    tileIndex,
    reportHeight,
    ctaLabel,
    isMobile,
    height,
    i18n
}) => {
    const { priceCaption, formattedFullPrice } = startingPrice;
    const settingsWithDefaults = { ...Tile.defaultProps.settings, ...settings };
    const {
        layout,
        size,
        tileBackgroundColor,
        textBackgroundColor,
        borderRadius,
        cardBorderRadius
    } = settingsWithDefaults;
    const [
        isDescriptionVisible,
        isRatingsVisible,
        isVendorVisible,
        isRibbonsVisible,
        isBadgesVisible,
        isShadowVisible
    ] = parseToBool(
        settingsWithDefaults.isDescriptionVisible,
        settingsWithDefaults.isRatingsVisible,
        settingsWithDefaults.isVendorVisible,
        settingsWithDefaults.isRibbonsVisible,
        settingsWithDefaults.isBadgesVisible,
        settingsWithDefaults.isShadowVisible
    );

    // ref definitions
    const imageRef = React.createRef();
    const contentRef = React.createRef();
    const tileRef = React.createRef();

    const imageUrl = iconSrcset['2x'] || iconUrl || Tile.defaultProps.data.iconUrl;
    const imageProps = imageUrl ? {
        style: { backgroundImage: `url(${imageUrl})` }
    } : {};

    const cssVariables = {
        'tile-background-color-hover': darken(tileBackgroundColor, 0.05),
        'tile-background-color': tileBackgroundColor,
        'text-background-color': textBackgroundColor,
        'border-radius': `${borderRadius}px`,
        'card-border-radius': `${cardBorderRadius}px`
    };
    if (height) {
        cssVariables['tile-height'] = `${height}px`;
    }

    const updateTileHeight = () => {
        if (reportHeight && contentRef && contentRef.current && contentRef.current.scrollHeight) {
            let newHeight = contentRef.current.scrollHeight;
            if (layout !== HORIZONTAL) {
                newHeight += imageRef.current.scrollHeight;
            }
            if (newHeight > height) {
                reportHeight(tileIndex, newHeight);
            }
        }
    };

    useEffect(() => {
        updateTileHeight();
    }, [layout, size]);

    // reset tile height on resize
    useEffect(() => {
        window.addEventListener('resize', updateTileHeight);

        return () => window.removeEventListener('resize', updateTileHeight);
    }, []);

    const image = (
        <div {...n('imageWrapper').withTestId().props} ref={imageRef}>
            <div {...n('image').withTestId().withE2EId().props} {...imageProps} />
        </div>
    );

    // do we display the longer version of number of ratings?
    const showLongNumRatings = !isMobile && (layout === TALL || layout === SQUARE);
    const ratingsBlock = isRatingsVisible && (
        <div {...n('rating').withTestId().props}>
            <Rating
                rating={rating}
                title={ratingCaption}
                numRatings={numRatings}
                displayRatingNumber={false}
                showLongNumRatings={showLongNumRatings}
            />
        </div>
    );

    const vendorBlock = isVendorVisible && (
        <div {...n('vendor').withTestId().props}>{vendorName}</div>
    );

    // isRibbonsVisible refers to both data badges and ribbon.
    const ribbonBlock = isRibbonsVisible && productRibbon && (
        <li {...n('ribbon').withTestId().props}>
            <Badge label={productRibbon.text} {...n('ribbonWrapper').withTestId().props} />
        </li>
    );

    const badgeBlock = isRibbonsVisible && badges && badges.length ? badges.map((badge, index) => (
        <li {...n('badge').withTestId().props} key={generateUniqueID([id, badge.id, index])}>
            <Badge label={badge.label} type={badge.type} />
        </li>
    )) : '';

    const merchandisingBadgesBlock = isBadgesVisible && merchandisingBadges && merchandisingBadges.length ? (
        <ul {...n('merchandisingBadges').withTestId().props} aria-label={i18n.merchandisingBadges}>
            {renderMerchandisingBadges(merchandisingBadges.slice(0, TILE_MAX_MERCHANDISING_BADGES), n)}
        </ul>
    ) : null;

    const tagsBlock = (
        <React.Fragment>
            {merchandisingBadgesBlock}
            <ul {...n('tags').withTestId().withE2EId().props} aria-label={i18n.tags}>
                {ribbonBlock}
                {badgeBlock}
            </ul>
        </React.Fragment>
    );

    let details;

    // large horizontal is the only layout in which elements are not in the same order
    if (layout === HORIZONTAL && settingsWithDefaults.size === LARGE.value) {
        const categories = tags && tags.map(({ type: tagType, name: tagName, id: tagId }) => {
            if (tagType === TAG_TYPE_CATEGORY) {
                return (
                    <span key={generateUniqueID([tagName, tagId])}>{tagName}</span>
                );
            }
            return null;
        });

        details = (
            <div {...n('contentWrapper').props} ref={contentRef}>
                <div {...n('detailsWrapper').withE2EId().props}>
                    <div {...n('name').withTestId().withE2EId().props}>{name}</div>

                    <div {...n('vendorWrapper').props}>
                        {ratingsBlock}
                        {vendorBlock}
                    </div>
                    {isDescriptionVisible && !!description && (
                        <div {...n('description').withTestId().props}>{description}</div>
                    )}
                    {tagsBlock}
                </div>
                <div {...n('categoriesWrapper').withE2EId().props}>
                    {categories}
                </div>
            </div>
        );
    } else {
        const priceBlock = !!formattedFullPrice && (
            <div {...n('pricing').withTestId().withE2EId().props} aria-label={i18n.pricing}>
                {`${priceCaption} ${formattedFullPrice}`}
            </div>
        );
        const ctaBlock = Boolean(layout === TALL && ctaLabel) && (
            <div {...n('buyNow').withTestId().props}>{ctaLabel}</div>
        );
        details = (
            <div {...n('contentWrapper').withTestId().props} ref={contentRef}>
                <div {...n('detailsWrapper').withE2EId().props}>
                    <div {...n('name').withTestId().withE2EId().props}>{name}</div>
                    {vendorBlock}
                    {isDescriptionVisible && !!blurb && (
                        <div {...n('description').withTestId().withE2EId().props}>{blurb}</div>
                    )}
                </div>
                {tagsBlock}
                <div {...n('priceWrapper').withE2EId().props}>
                    {!hidePricings && priceBlock}
                    {ratingsBlock}
                    {ctaBlock}
                </div>
            </div>
        );
    }

    const shadowModifier = isShadowVisible && 'with-shadow';
    const containerProps = n('item', [size, layout, shadowModifier]).withVariables(cssVariables).withTestId().withE2EId().props;

    return (
        <div {...containerProps} ref={tileRef}>
            {url && (<a {...n('overlayLink').withE2EId().props} href={url}><span {...n('overlayLabel').props}>{name}</span></a>)}
            <div {...n('content').withE2EId().props}>
                {image}
                {details}
            </div>
        </div>
    );
};

Tile.schema = () => ({
    name: 'Tile',
    iconName: 'tile',
    form: {
        layout: {
            title: 'uieditor.sfbComponent.Tile.layout.title',
            type: RADIO,
            required: false,
            defaultValue: TILE_TYPES[0].value,
            options: TILE_TYPES
        }
    }
});

const SETTINGS = Tile.schema().form;

Tile.propTypes = {
    data: PropTypes.shape(tileShape),
    settings: PropTypes.shape({
        layout: PropTypes.string,
        tileBackgroundColor: PropTypes.string,
        textBackgroundColor: PropTypes.string,
        size: PropTypes.string,
        isDescriptionVisible: PropTypes.bool,
        isVendorVisible: PropTypes.bool,
        isBadgesVisible: PropTypes.bool,
        isRibbonsVisible: PropTypes.bool,
        isRatingsVisible: PropTypes.bool,
        isShadowVisible: PropTypes.bool,
        borderRadius: PropTypes.string,
        cardBorderRadius: PropTypes.number
    }),
    tileIndex: PropTypes.number,
    isMobile: PropTypes.bool,
    reportHeight: PropTypes.func,
    height: PropTypes.number,
    ctaLabel: PropTypes.string,
    i18n: PropTypes.shape(i18nShape)
};

Tile.defaultProps = {
    data: {
        name: '',
        id: 0,
        vendorName: '',
        blurb: '',
        description: '',
        hidePricings: false,
        startingPrice: {
            priceCaption: '',
            formattedFullPrice: ''
        },
        url: '',
        iconUrl: PRODUCT_DEFAULT_IMG,
        rating: 0,
        ratingCaption: '',
        numRatings: 0,
        tags: null,
        badges: [],
        productRibbon: null
    },
    settings: {
        layout: SETTINGS.layout.defaultValue,
        tileBackgroundColor: TILE_BACKGROUND,
        textBackgroundColor: TILE_TEXT_BACKGROUND,
        isDescriptionVisible: true,
        isVendorVisible: true,
        isBadgesVisible: true,
        isRibbonsVisible: true,
        isRatingsVisible: true,
        isShadowVisible: false,
        size: SMALL.value,
        borderRadius: '8',
        cardBorderRadius: 8
    },
    isMobile: false,
    tileIndex: 0,
    reportHeight: null, // deliberately setting this to null because an empty function is truthy
    height: 0,
    ctaLabel: 'Buy Now',
    i18n: i18nMock
};

export default Tile;
