import React from 'react';
import find from 'lodash/find';
import findIndex from 'lodash/findIndex';
import isArray from 'lodash/isArray';
import { Swipeable } from 'react-swipeable';
import clsx from 'clsx';
import Button from '@material-ui/core/Button';
import DialogContent from '@material-ui/core/DialogContent';
import Typography from '@material-ui/core/Typography';
import DialogActions from '@material-ui/core/DialogActions';
import PropTypes from 'prop-types';
import { withStyles } from '@material-ui/core/styles';
import withWidth from '@material-ui/core/withWidth';
import { getConf } from 'App/configuration';
import LoadingOverlay from 'Framework/Components/LoadingOverlay';
import { T } from 'Framework/i18n';
import CustomDialog from 'Shared/Components/CustomDialog';
import OptionlistitemAmount from '../OptionAmount';
import getImageUrl from '../../../../Utils/getImageUrl';
import Markdown from '../../../../../Framework/Components/Markdown';
import ZoomImage from './ZoomImage';

const styles = theme => ({
    detailNav: {
        display: 'flex',
        justifyContent: 'center',
        alignItems: 'center',
        cursor: 'pointer',
        fontWeight: 'bold',
        fontSize: '72px',
        transform: 'scaleX(0.5)',
        opacity: 0.7,
        '&.prev': {
            '&:before': {
                content: '"<"',
            },
        },
        '&.next': {
            '&:before': {
                content: '">"',
            },
        },
    },

    detailFooter: {
        clear: 'both',
        padding: '10px 0 0 32px',
        textAlign: 'right',

        '& button': {
            marginLeft: 10,
            background: theme.palette.primary.darkText,
            color: 'white',
            '&:hover': {
                cursor: 'pointer',
                backgroundColor: '#616161',
            },
        },
    },
    detailContainerWithNav: {
        display: 'grid',
        gridTemplateColumns: 'auto 1fr auto',
    },
    detailContainer: {},
    detailsWithImageContainer: {
        display: 'grid',
        gridTemplateColumns: 'minMax(300px, 4fr) 6fr',
        columnGap: 24,
    },
    priceFormatted: {
        marginTop: 32,
        marginBottom: 32,
    },
    detailsTitle: {
        fontWeight: 'bold',
    },
    detailsTypography: {
        marginTop: 12,
        marginBottom: 12,
    },
    imageContainer: {
        display: 'grid',
        placeItems: 'start center',
    },
    imageContainerWithZoom: {
        display: 'grid',
        placeItems: 'start center',
        cursor: 'zoom-in',
    },
    image: {
        marginTop: 12,
        maxWidth: '100%',
        maxHeight: '100%',
    },
    imageHidden: {
        visibility: 'hidden',
    },
    dialogContentRootWithZoom: {
        overflow: 'hidden',
    },
    detailDialogContentRoot: {
        overflowY: 'auto',
    },
});

class Optiondetail extends React.Component {
    static propTypes = {
        details: PropTypes.object,
        optionclassification: PropTypes.object,
        classes: PropTypes.object,
        options: PropTypes.array,
        viewDetails: PropTypes.func,
        onSelect: PropTypes.func,
        OptionlistitemAmount: PropTypes.elementType,
        show: PropTypes.bool,
        title: PropTypes.string,
        onClose: PropTypes.func,
        LoadingComponent: PropTypes.elementType,
        width: PropTypes.string,
        isLoading: PropTypes.bool,
    };

    static swipeDelta = 50;

    constructor(props) {
        super(props);

        const { details, options } = props;

        this.state = {
            currentOption: find(options, { identifier: details.identifier }),
            loading: false,
            zoomImage: false,
            hasZoomSet: false,
            hasZoom: false,
            imageStatus: 'loading',
        };

        this.optionDetailsWithImage = getConf('optionDetailsWithImage') || false;
        this.detailContainerRef = React.createRef();
        this.detailImageRef = React.createRef();
    }

    componentDidUpdate(prevProps) {
        const { details, options } = this.props;
        if (details.identifier !== prevProps.details.identifier) {
            this.setState({
                currentOption: find(options, { identifier: details.identifier }),
                loading: false,
            });
        }
    }

    getOptionData() {
        if (!this.state.currentOption) {
            return null;
        }
        const { optionclassification } = this.props;
        const { currentOption } = this.state;
        const { identifier } = currentOption;
        return find(optionclassification.selectedoptions, { identifier }) || currentOption;
    }

    prev = () => {
        const { details, options, viewDetails } = this.props;
        if (options.length) {
            const optionIndex = findIndex(options, { identifier: details.identifier });
            const prevIndex = optionIndex === 0 ? options.length - 1 : optionIndex - 1;
            const prev = options[prevIndex];
            viewDetails(prev);
            this.setState({
                currentOption: prev,
                loading: true,
                loadingContainerHeight: this.detailContainerRef.clientHeight,
            });
        }
    };

    next = () => {
        const { details, options, viewDetails } = this.props;
        if (options.length) {
            const optionIndex = findIndex(options, { identifier: details.identifier });
            const nextIndex = optionIndex === options.length - 1 ? 0 : optionIndex + 1;
            const next = options[nextIndex];
            viewDetails(next);
            this.setState({
                currentOption: next,
                loading: true,
                loadingContainerHeight: this.detailContainerRef.clientHeight,
            });
        }
    };

    swiped = (e, deltaX, deltaY) => {
        // use only horizontal swiping
        if (Math.abs(deltaX) >= Math.abs(deltaY)) {
            if (deltaX > 0) {
                this.next();
            } else {
                this.prev();
            }
        }
    };

    onAmountChange(delta) {
        const { onSelect } = this.props;
        const option = this.getOptionData();
        let amount = option.amount + delta;
        if (amount < 0) {
            return; // prevent setting below 0
        }
        onSelect(
            {
                ...option,
                amount,
            },
            true,
            true
        );
    }

    onSelect() {
        const { onSelect, onClose } = this.props;
        // just close it if it's already selected
        if (this.state.currentOption.amount > 0) {
            onClose && onClose();
        } else {
            onSelect({
                ...this.state.currentOption,
                amount: this.state.currentOption.amount || 1,
            });
        }
    }

    renderControls() {
        const { OptionlistitemAmount } = this.props;
        const option = this.getOptionData();
        if (!option) {
            return null;
        }
        if (option.amount_is_selectable) {
            return <OptionlistitemAmount option={option} onAmountChange={this.onAmountChange.bind(this)} />;
        } else {
            return (
                <Button className="primary-button" onClick={this.onSelect.bind(this)} variant="contained">
                    {T('optiondetail.choose')}
                </Button>
            );
        }
    }

    handleImageLoaded = () => this.setState({ imageStatus: 'loaded' });
    toggleZoomImage = () => this.setState({ zoomImage: !this.state.zoomImage });
    setDetailsImageRef = ref => {
        this.detailImageRef = ref;
    };
    setDetailsContainerRef = ref => {
        this.detailContainerRef = ref;
    };

    getMaxZoomedWidth = width => {
        const isWideScreen = width === 'sm' || width === 'md' || width === 'lg' || width === 'xl';
        const maxZoomedWidth = width === 'xl' ? 1080 : 720;
        return isWideScreen ? maxZoomedWidth : '100%';
    };

    render() {
        const {
            LoadingComponent,
            options,
            show,
            onClose,
            details,
            classes,
            width,
            optionclassification,
        } = this.props;
        const { loading, loadingContainerHeight } = this.state;
        const showNav = isArray(options) && options.length > 1;
        const detailImage = details.detailImage;
        const query = new URLSearchParams(location.search);
        const hidePrices = query.has('_hide_prices') && query.get('_hide_prices') === '1';

        const detailsContent = (
            <div>
                {details.abstract && (
                    <Markdown
                        MdComponent={Typography}
                        variant="body2"
                        component="div"
                        className={classes.detailsTypography}
                    >
                        {details.abstract}
                    </Markdown>
                )}
                {!hidePrices && (
                    <Typography component="div" variant="body2" className={classes.priceFormatted}>
                        {details.priceFormatted}
                    </Typography>
                )}
                {details.description && (
                    <Markdown
                        MdComponent={Typography}
                        variant="body2"
                        component="div"
                        className={classes.detailsTypography}
                    >
                        {details.description}
                    </Markdown>
                )}
            </div>
        );

        const showFullScreenModal = ['xs', 'sm'].includes(width);
        let maxZoomedWidth = this.getMaxZoomedWidth(width);
        let isLandscapeFormat = false;
        if (
            !!this.detailImageRef &&
            this.detailImageRef.current !== null &&
            this.state.imageStatus === 'loaded'
        ) {
            if (this.detailImageRef.naturalHeight < this.detailImageRef.naturalWidth) {
                isLandscapeFormat = true;
            }
            if (!this.state.hasZoomSet) {
                const hasZoom =
                    this.detailImageRef.naturalHeight * this.detailImageRef.naturalWidth >=
                    1.2 * this.detailImageRef.width * this.detailImageRef.height;
                this.setState({
                    hasZoomSet: true,
                    hasZoom,
                });
            }
            if (this.detailImageRef.naturalWidth < 720) {
                maxZoomedWidth = this.detailImageRef.naturalWidth;
            }
        }

        return (
            <CustomDialog
                open={show}
                dialogTitle={details.title}
                onClose={onClose}
                onCancel={onClose}
                fullScreen={showFullScreenModal}
            >
                <DialogContent
                    classes={
                        this.state.hasZoom
                            ? {
                                  root: clsx(
                                      classes.detailDialogContentRoot,
                                      classes.dialogContentRootWithZoom
                                  ),
                              }
                            : { root: classes.detailDialogContentRoot }
                    }
                >
                    {loading ? (
                        <div style={{ height: loadingContainerHeight, boxSizing: 'border-box' }}>
                            <LoadingComponent show={loading} />
                        </div>
                    ) : (
                        <Swipeable
                            disabled={!showNav}
                            trackMouse={true}
                            onSwiped={this.swiped}
                            delta={Optiondetail.swipeDelta}
                        >
                            <div
                                ref={this.setDetailsContainerRef}
                                className={showNav ? classes.detailContainerWithNav : classes.detailContainer}
                            >
                                {showNav && (
                                    <div className={clsx(classes.detailNav, 'prev')} onClick={this.prev} />
                                )}

                                {detailImage ? (
                                    <div
                                        className={clsx({
                                            [classes.detailsWithImageContainer]: !showFullScreenModal,
                                        })}
                                    >
                                        <div
                                            className={
                                                this.state.hasZoom
                                                    ? classes.imageContainerWithZoom
                                                    : classes.imageContainer
                                            }
                                            onClick={this.state.hasZoom ? this.toggleZoomImage : null}
                                        >
                                            <img
                                                ref={this.setDetailsImageRef}
                                                onLoad={this.handleImageLoaded}
                                                className={
                                                    this.state.zoomImage && ['xs', 'sm'].includes(width)
                                                        ? classes.imageHidden
                                                        : classes.image
                                                }
                                                src={getImageUrl(detailImage)}
                                                alt={T('ariaLabels.optionDetailsImage', {
                                                    componentTitle: optionclassification.title,
                                                    optionTitle: details.title,
                                                })}
                                            />
                                        </div>
                                        {detailsContent}
                                    </div>
                                ) : (
                                    detailsContent
                                )}

                                {showNav && (
                                    <div className={clsx(classes.detailNav, 'next')} onClick={this.next} />
                                )}
                            </div>
                        </Swipeable>
                    )}
                </DialogContent>
                <DialogActions>
                    <div className={classes.detailFooter}>{this.renderControls()}</div>
                </DialogActions>
                <ZoomImage
                    onClose={this.toggleZoomImage}
                    show={this.state.zoomImage}
                    image={detailImage}
                    loadingContainerHeight={loadingContainerHeight}
                    isLandscapeFormat={isLandscapeFormat}
                    width={maxZoomedWidth}
                    title={details.title}
                />
            </CustomDialog>
        );
    }
}

Optiondetail.defaultProps = {
    OptionlistitemAmount,
    LoadingComponent: LoadingOverlay,
};

export default withStyles(styles, { name: 'Optiondetail' })(withWidth({ initialWidth: 'xl' })(Optiondetail));
