import React, { useState, useEffect } from 'react';
import { useQuery } from '@apollo/client';
// I need to import the component with HOC withListener when the component
// needs to support theme settings
import withGraphQL from '../withGraphQL';
import VariantSelectionPreview from './atoms/variant-selection-preview/VariantSelectionPreview';
import VariantOptionList from './atoms/variant-option-list/VariantOptionList';
import VariantOptionSwatch from './atoms/variant-option-swatch/VariantOptionSwatch';
import { VARIANT_INPUT_TYPE } from '../../constants/components/product-variants';
import { parseProduct } from '../../tools/parsers/productParser';
import { productVariantsShape, productVariantsAdapterShape } from '../../constants/components/prop-types/product-variants';
import { createNamespace } from '../../tools/namingTools';
import GridContainer from '../../atoms/grid-container/GridContainer';
import StickyBannerContainer from '../../atoms/sticky-banner-container/StickyBannerContainer';
import Icon from '../../atoms/icon/Icon';
import Preloader from '../../atoms/preloader/Preloader';
import Alert, { alertTypes } from '../../atoms/alert/Alert';
import Image from '../../atoms/image/Image';
import { useContainerWidth } from '../../tools/widths/useContainerWidth';
import { bMedium } from '../../constants/breakpoints';
import { getDefaultSelectedEdition } from '../product-configurator/ProductConfigurator';
import { DEFAULT_IMG } from '../../constants/images';
import { PRODUCT_VARIANTS_GRAPHQL_QUERY } from './graphql/query';
import { getImageSrc } from '../../tools/images';
// IMPORTANT: (s)css should be imported after other atoms/components
import './styles/ProductVariants.scss';

const n = createNamespace('ProductVariants');

export const ProductVariantsComponent = ({ data }) => {
    const {
        isLoadingVariantsData = false,
        hasErrorsLoadingVariantsData = null,
        variantOptions = [],
        variants = [],
        callToActions = {},
        configurator = { selectedEdition: null, backAction: {}, i18n: {} },
        editions = {},
        summary = {}
    } = parseProduct(data);
    const enabledVariants = variants.filter(it => it.isEnabled).map(it => it.configuration);
    const getDefaultSelectedOptions = () => variantOptions.map(({ name, code, defaultValue }) => ({
        optionName: name,
        optionCode: code,
        optionValue: defaultValue ? defaultValue.name : null,
        valueCode: defaultValue ? defaultValue.code : null
    }));

    const [selectedOptions, setSelectedOptions] = useState([]);
    useEffect(() => {
        setSelectedOptions(getDefaultSelectedOptions());
    }, [data]);
    const getCurrentSelectedVariant = () => {
        let filteredVariants = variants;
        selectedOptions.forEach(selectedOption => {
            filteredVariants = filteredVariants.filter(variant => {
                const conf = variant.configuration.find(config => config.optionCode === selectedOption.optionCode);
                return conf ? selectedOption.valueCode === conf.valueCode : false;
            });
        });
        if (filteredVariants.length) {
            return filteredVariants[0];
        }
        return { price: '', images: [] };
    };

    const [selectedVariant, setSelectedVariant] = useState(getCurrentSelectedVariant());
    const [containerRef, containerWidth] = useContainerWidth();

    useEffect(() => {
        setSelectedVariant(getCurrentSelectedVariant());
    }, [selectedOptions]);
    const [selectedEdition, setSelectedEdition] = useState(
        getDefaultSelectedEdition(configurator.selectedEdition, editions.items)
    );
    useEffect(() => {
        if (selectedVariant) {
            setSelectedEdition(
                editions.items.find(edition => edition.productEditionRefId === selectedVariant.editionId)
            );
        }
    }, [selectedVariant, selectedOptions]);
    const isMobileXL = Boolean(containerWidth && containerWidth < bMedium);
    const getSelectedVariantImage = () => {
        if (selectedVariant && selectedVariant.images && selectedVariant.images.length) {
            return selectedVariant.images[0];
        }

        return {
            name: summary.title || '',
            imageUrl: summary.logoSrc || '',
            imageUrlSrcset: summary.logoSrcset || {}
        };
    };

    const renderBackButton = () => {
        if (!configurator
            || !configurator.backAction
            || !configurator.backAction.url
            || !configurator.backAction.label) {
            return null;
        }
        return (
            <div {...n('backAction').props}>
                <a {...n('backActionLink').withTestId().props} href={configurator.backAction.url}>
                    <Icon type="caretLeft" {...n('backActionIcon').props} />
                    <span {...n('backActionLabel').withTestId().props}>
                        {configurator.backAction.label}
                    </span>
                </a>
            </div>
        );
    };

    const renderStickyBannerContent = () => (
        <div ref={containerRef}>
            {!isMobileXL && renderBackButton()}
            <VariantSelectionPreview
                selectedOptions={selectedOptions}
                productName={summary.title}
                variantImage={getSelectedVariantImage()}
                isVariantEnabled={selectedVariant.isEnabled}
                isCondensed={isMobileXL}
                callToActions={callToActions}
                configurator={configurator}
                selectedEdition={selectedEdition}
            />
        </div>
    );

    const getCodeOfSelectedValue = variantOptionCode => {
        const currentSelectedOption = selectedOptions.find(
            selectedOption => selectedOption.optionCode === variantOptionCode
        );
        return currentSelectedOption ? currentSelectedOption.valueCode : null;
    };

    const findVariantOptionByCode = optionCode => variantOptions.find(option => option.code === optionCode);

    const optionValueClickHandler = (valueCode, optionCode) => {
        const valueCodeHasId = valueCode && valueCode.id;
        const selectedValueCode = valueCodeHasId ? valueCode.id : valueCode;
        const selectedValue = findVariantOptionByCode(optionCode).values.find(
            value => value.code === selectedValueCode
        );

        setSelectedOptions(currentSelectedOptions => currentSelectedOptions.map(selectedOption => {
            if (selectedOption.optionCode === optionCode) {
                return {
                    ...selectedOption,
                    optionValue: selectedValue.name,
                    valueCode: selectedValue.code
                };
            }
            return selectedOption;
        }));
    };

    const optionValueMatching = (optionValue1, optionValue2) => optionValue1.optionCode === optionValue2.optionCode
        && optionValue1.valueCode === optionValue2.valueCode;
    const configMatching = (config1, config2) => config1
        .filter(optionValue1 => !config2.some(optionValue2 => optionValueMatching(optionValue1, optionValue2)))
        .length === 0;

    function getOptionsWithDisabled(variantOption) {
        const optionWithDisabled = { ...variantOption };
        optionWithDisabled.values = variantOption.values.map(value => {
            const configPossibility = [
                ...selectedOptions.filter(selectedOption => selectedOption.optionCode !== variantOption.code
                    && selectedOption.valueCode !== null),
                { optionCode: variantOption.code, valueCode: value.code }
            ];
            return {
                ...value,
                isDisabled: enabledVariants
                    .filter(config => configMatching(configPossibility, config)).length === 0
            };
        });
        return optionWithDisabled;
    }

    const renderVariantOptions = () => {
        if (isLoadingVariantsData) {
            return (<Preloader isLoading />);
        }

        if (hasErrorsLoadingVariantsData) {
            // FIXME: We need to ask what will be the design for the error case
            // we need to remove the hardcoded message and pass an i18n prop
            return (
                <Alert
                    alertMessage="There has been an error!"
                    type={alertTypes.ERROR_ALERT}
                />
            );
        }
        return variantOptions.length ? variantOptions.map(variantOption => {
            const optionWithDisabled = getOptionsWithDisabled(variantOption);
            switch (variantOption.inputType) {
                case VARIANT_INPUT_TYPE.COLOUR:
                    return (
                        <VariantOptionSwatch
                            key={variantOption.code}
                            variantOption={optionWithDisabled}
                            selectedColorId={getCodeOfSelectedValue(variantOption.code)}
                            onClickColorCallback={
                                valueCode => optionValueClickHandler(valueCode, variantOption.code)
                            }
                            isCondensed={isMobileXL}
                        />
                    );
                default:
                    return (
                        <VariantOptionList
                            key={variantOption.code}
                            variantOption={optionWithDisabled}
                            selectedItemId={getCodeOfSelectedValue(variantOption.code)}
                            onClickItemCallback={
                                valueCode => optionValueClickHandler(valueCode, variantOption.code)
                            }
                            isCondensed={isMobileXL}
                        />
                    );
            }
        }) : null;
    };

    const renderVariantImages = () => {
        const {
            name = '',
            imageUrl = '',
            imageUrlSrcset = {}
        } = getSelectedVariantImage();

        return (
            <Image
                {...n('variantImage').withTestId().props}
                name={name}
                src={getImageSrc(imageUrl, imageUrlSrcset)}
            />
        );
    };

    const renderContent = () => (
        <GridContainer {...n('gridContainer').withTestId().props}>
            <div {...n('galleryContainer').withTestId().props}>
                {isMobileXL && renderBackButton()}
                {renderVariantImages()}
            </div>
            <div {...n('variantOptionsContainer').withTestId().props}>
                {renderVariantOptions()}
            </div>
        </GridContainer>
    );

    return (
        <StickyBannerContainer
            stickyBannerContent={renderStickyBannerContent()}
            content={renderContent()}
        />
    );
};

ProductVariantsComponent.schema = () => ({
    name: 'ProductVariants',
    title: 'uieditor.sfbComponent.productVariants.title',
    form: {},
    marketplaceData: {
        adTenant: 'config.channelSettings.partner',
        baseUrl: 'config.channelSettings.channelCanonicalBaseUrl',
        callToActions: 'application.callToActions',
        configurator: 'application.configurator',
        editions: 'application.editions',
        productRefId: 'application.config.productRefId',
        summary: 'application.summary',
        acceptLanguage: 'config.locale.code'
    }
});

ProductVariantsComponent.propTypes = productVariantsShape;

const commonDefaultProps = {
    adTenant: 'APPDIRECT',
    baseUrl: 'http://localhost:3000',
    callToActions: { items: [] },
    configurator: {},
    editions: { items: [] },
    productRefId: null,
    summary: {
        details: '',
        logoSrc: DEFAULT_IMG,
        logoSrcset: null,
        logoAlt: '',
        overview: '',
        title: ''
    },
    acceptLanguage: ''
};

const getDefaultVariantsData = () => ({
    variantOptions: [],
    variants: []
});

ProductVariantsComponent.defaultProps = {
    data: {
        isLoadingVariantsData: false,
        hasErrorsLoadingVariantsData: null,
        ...getDefaultVariantsData(),
        ...commonDefaultProps
    }
};

export const ProductVariantsAdapter = ({ data }) => {
    const defaultFetchPolicy = 'network-only';
    const {
        data: productVariantsGqlData,
        loading: productVariantsGqlLoading,
        error: productVariantsGqlError
    } = useQuery(
        PRODUCT_VARIANTS_GRAPHQL_QUERY,
        {
            variables: {
                productId: data.productRefId
            },
            fetchPolicy: defaultFetchPolicy
        }
    );

    const [variantsData, setVariantsData] = useState(getDefaultVariantsData());

    useEffect(() => {
        if (productVariantsGqlError || productVariantsGqlLoading) {
            return;
        }
        const { marketplaceProduct: { variants, variantOptions } } = productVariantsGqlData;
        setVariantsData({
            variants,
            variantOptions
        });
    }, [productVariantsGqlData, productVariantsGqlLoading, productVariantsGqlError]);

    return (
        <ProductVariantsComponent
            data={{
                ...data,
                ...variantsData,
                isLoadingVariantsData: productVariantsGqlLoading,
                hasErrorsLoadingVariantsData: productVariantsGqlError
            }}
        />
    );
};

ProductVariantsAdapter.schema = ProductVariantsComponent.schema;
ProductVariantsAdapter.propTypes = productVariantsAdapterShape;
ProductVariantsAdapter.defaultProps = {
    data: {
        ...commonDefaultProps
    }
};

export default withGraphQL(ProductVariantsAdapter, 'api/graphql/preview');
