import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import Rating from '../rating/Rating';
import Badge from '../badge/Badge';
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 } from '../../constants/backgroundSettings';
import { PRODUCT_DEFAULT_IMG } from '../../constants/images';
import { TAG_TYPE_CATEGORY } from '../../constants/tags';
import { darken } from '../../tools/colorHelper';
import { renderMerchandisingBadges } from '../../tools/rendering/merchandisingBadges';
import { SMALL, LARGE } from '../../constants/sizes';
import './styles/Tile.scss';

const n = createNamespace('Tile');

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

    // 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,
        'border-radius': `${borderRadius}px`
    };
    if (height) {
        cssVariables['tile-height'] = `${height}px`;
    }

    useEffect(() => {
        if (reportHeight) {
            let newHeight = contentRef.current.scrollHeight;
            if (layout !== HORIZONTAL) {
                newHeight += imageRef.current.scrollHeight;
            }
            if (newHeight > height) {
                reportHeight(tileIndex, newHeight);
            }
        }
    }, [height, tileIndex, layout, size, reportHeight, imageRef, contentRef]);

    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}
                numRatings={numRatings}
                displayRatingNumber={false}
                showLongNumRatings={showLongNumRatings}
            />
        </div>
    );

    const ribbonBlock = productRibbon && (
        <span {...n('ribbon').withTestId().props}>
            <Badge label={productRibbon.text} {...n('ribbonWrapper').withTestId().props} />
        </span>
    );

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

    const merchandisingBadgesBlock = merchandisingBadges && merchandisingBadges.length ? (
        <div {...n('merchandisingBadges').withTestId().props}>
            {renderMerchandisingBadges(merchandisingBadges.slice(0, TILE_MAX_MERCHANDISING_BADGES))}
        </div>
    ) : null;

    const tagsBlock = (
        <React.Fragment>
            {merchandisingBadgesBlock}
            <div {...n('tags').withTestId().withE2EId().props}>
                {ribbonBlock}
                {badgeBlock}
            </div>
        </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(tag => {
            if (tag.type === TAG_TYPE_CATEGORY) {
                return (
                    <span key={`${Date.now()}-${tag.name}`}>{tag.name}</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}
                        <div {...n('vendor').withTestId().props}>{vendorName}</div>
                    </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}>{`${priceCaption} ${formattedFullPrice}`}</div>
        );
        const ctaBlock = Boolean(layout === TALL && ctaLabel) && (
            <div {...n('buyNow').withTestId().props}>{ctaLabel}</div>
        );
        details = (
            <div {...n('contentWrapper').props} ref={contentRef}>
                <div {...n('detailsWrapper').withE2EId().props}>
                    <div {...n('name').withTestId().withE2EId().props}>{name}</div>
                    <div {...n('vendor').withTestId().props}>{vendorName}</div>
                    {isDescriptionVisible && !!blurb && (
                        <div {...n('description').withTestId().withE2EId().props}>{blurb}</div>
                    )}
                </div>
                {tagsBlock}
                <div {...n('priceWrapper').withE2EId().props}>
                    {priceBlock}
                    {ratingsBlock}
                    {ctaBlock}
                </div>
            </div>
        );
    }

    const containerProps = n('item', [size, layout]).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,
            validation: {
                availableOptions: TILE_TYPES.map(({ value }) => value),
                message: 'uieditor.sfbComponent.radio.validation.message',
                default: 'uieditor.sfbComponent.dropdown.defaultValue'
            }
        }
    }
});

const SETTINGS = Tile.schema().form;

export const defaultTileProps = PropTypes.shape({
    id: PropTypes.number,
    name: PropTypes.string,
    vendorName: PropTypes.string,
    blurb: PropTypes.string,
    description: PropTypes.string,
    startingPrice: PropTypes.shape({
        priceCaption: PropTypes.string,
        formattedFullPrice: PropTypes.string
    }),
    rating: PropTypes.number,
    numRatings: PropTypes.number,
    url: PropTypes.string,
    iconUrl: PropTypes.string,
    iconSrcset: PropTypes.shape({
        default: PropTypes.string,
        '1x': PropTypes.string,
        '2x': PropTypes.string
    }),
    tags: PropTypes.arrayOf(
        PropTypes.shape({
            type: PropTypes.string,
            name: PropTypes.string
        })
    ),
    productRibbon: PropTypes.shape({
        text: PropTypes.string,
        textColor: PropTypes.string,
        ribbonColor: PropTypes.string
    }),
    badges: PropTypes.arrayOf(
        PropTypes.shape({
            id: PropTypes.string,
            label: PropTypes.string,
            type: PropTypes.string
        })
    ),
    merchandisingBadges: PropTypes.arrayOf(
        PropTypes.shape({
            uuid: PropTypes.string,
            name: PropTypes.string,
            image: PropTypes.string
        })
    )
});

Tile.propTypes = {
    data: defaultTileProps,
    settings: PropTypes.shape({
        layout: PropTypes.string,
        tileBackgroundColor: PropTypes.string,
        size: PropTypes.string,
        isDescriptionVisible: PropTypes.bool,
        isRatingsVisible: PropTypes.bool,
        borderRadius: PropTypes.number
    }),
    tileIndex: PropTypes.number,
    isMobile: PropTypes.bool,
    reportHeight: PropTypes.func,
    height: PropTypes.number,
    ctaLabel: PropTypes.string
};

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

export default Tile;
