import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import _ from 'lodash';
import classnames from 'classnames';
import EventBus from '../../../utils/EventBus';
import {LocalStorage} from '../../../data/LocalStorage';
import {GridSorter} from './GridSorter';

const RESIZE_OVERLAY_ID = 'GridHeaderCellResizeOverlay';
const COLUMNS_KEY = 'columns';
const RESIZE_EVENT = 'GridHeaderCellResize';

export class GridHeaderCell extends React.Component {
    static propTypes = {
        ...GridSorter.propTypes,
        isSortable: PropTypes.bool,
        isResizable: PropTypes.bool,
        append: PropTypes.node,
        minWidth: PropTypes.number,
        maxWidth: PropTypes.number,
        ariaDescription: PropTypes.string,
        ariaDescriptionId: PropTypes.string,
        skipContentForSR: PropTypes.bool,
        noRightBorder: PropTypes.bool
    };

    static defaultProps = {
        ...GridSorter.defaultProps,
        isSortable: true,
        isResizable: false,
        append: null,
        minWidth: 50,
        maxWidth: 500,
        skipContentForSR: false,
        ariaDescriptionId: null,
        noRightBorder: false
    };

    static contextTypes = {
        localStorage: PropTypes.instanceOf(LocalStorage)
    };

    state = {
        minWidth: null,
        resizing: null
    };

    componentDidMount() {
        EventBus.on(RESIZE_EVENT, this.handleSiblingResize);
    }

    componentWillUnmount() {
        EventBus.off(RESIZE_EVENT, this.handleSiblingResize);
        this.cancelResizing();
    }

    getResizeColumnWidth() {
        const {resizing, minWidth} = this.state;
        if (resizing) {
            return resizing.width;
        }

        if (minWidth) {
            return minWidth;
        }

        const {localStorage} = this.context;
        const {property} = this.props;
        if (localStorage && property) {
            const columns = localStorage.getItem(COLUMNS_KEY);
            if (columns && columns[property]) {
                return columns[property].width;
            }
        }
    }

    getParentCell(element) {
        let cell = element;
        while (cell && cell.tagName !== 'TH' && cell.tagName !== 'TD') {
            cell = cell.parentNode;
        }
        return cell;
    }

    handleSiblingResize = localStoragePrefix => {
        const {localStorage} = this.context;
        const {resizing} = this.state;

        if (resizing || !localStorage || localStoragePrefix !== localStorage.prefix) {
            return;
        }

        let cell = this.cellRef || ReactDOM.findDOMNode(this.gridSorterRef);
        if (!cell) {
            return;
        }

        const {minWidth} = this.props;
        if (!minWidth) {
            return;
        }

        const width = cell.clientWidth;
        if (width <= minWidth) {
            if (minWidth !== this.state.minWidth) {
                this.setState({minWidth});
            } else {
                // Reset minWidth -> Render -> Re-calculate minWidth
                this.setState({minWidth: null}, () => this.handleSiblingResize(localStoragePrefix));
            }
        } else if (this.state.minWidth) {
            this.setState({minWidth: null});
        }
    };

    createResizeOverlay() {
        const container = document.createElement('div');
        const overlay = document.createElement('div');
        container.id = RESIZE_OVERLAY_ID;
        container.className = 'bootstrap-rc';
        overlay.className = 'resizing-overlay resizing-overlay-ew';
        container.appendChild(overlay);
        return container;
    }

    startResizing = event => {
        event.stopPropagation();

        const cell = this.getParentCell(event.target);
        if (!cell) {
            return;
        }

        const overlay = this.createResizeOverlay();
        document.body.appendChild(overlay);
        document.addEventListener('mousemove', this.resize);
        document.addEventListener('mouseup', this.stopResizing);

        this.setState({
            minWidth: null,
            resizing: {
                x: event.pageX,
                width: cell.clientWidth,
                startWidth: cell.clientWidth
            }
        });
    };

    resize = event => {
        event.stopPropagation();

        const {minWidth, maxWidth} = this.props;
        const {resizing} = this.state;
        if (resizing) {
            let width = resizing.startWidth - resizing.x + event.pageX;
            if (width < minWidth) {
                width = minWidth;
            } else if (width > maxWidth) {
                width = maxWidth;
            }
            if (width !== resizing.width) {
                this.setState(
                    {
                        minWidth: null,
                        resizing: {...resizing, width}
                    },
                    this.notifySiblings
                );
            }
        }
    };

    stopResizing = event => {
        event.stopPropagation();

        const {localStorage} = this.context;
        const {property} = this.props;
        const {resizing} = this.state;

        if (resizing && localStorage && property) {
            const columns = localStorage.getItem(COLUMNS_KEY) || {};
            columns[property] = {
                ...columns[property],
                width: resizing.width
            };
            localStorage.setItem(COLUMNS_KEY, columns);
        }

        this.cancelResizing();
        this.setState({
            resizing: null
        });
    };

    cancelResizing() {
        const overlay = document.getElementById(RESIZE_OVERLAY_ID);
        if (overlay) {
            overlay.parentNode.removeChild(overlay);
        }
        document.removeEventListener('mousemove', this.resize);
        document.removeEventListener('mouseup', this.stopResizing);
    }

    notifySiblings = () => {
        const {localStorage} = this.context;
        if (localStorage && localStorage.prefix) {
            EventBus.emit(RESIZE_EVENT, localStorage.prefix);
        }
    };

    isResizable() {
        const {localStorage} = this.context;
        const {isResizable, property} = this.props;
        return !!(localStorage && isResizable && property);
    }

    renderResizeHandler() {
        if (this.props.noRightBorder) {
            return null;
        }
        const isResizable = this.isResizable();
        const className = classnames('resize-handler', {active: isResizable});
        return <span className={className} onMouseDown={isResizable ? this.startResizing : null} />;
    }

    renderChildren = (ariaDescription, skipContentForSR, ariaDescriptionId, children) => {
        return (
            <span>
                {children && <span aria-hidden={skipContentForSR}>{children}</span>}
                {ariaDescription && (
                    <span className="sr-only" id={ariaDescriptionId}>
                        {ariaDescription}
                    </span>
                )}
            </span>
        );
    };

    render() {
        const {
            isSortable,
            isResizable: _isResizable,
            append,
            minWidth: _minWidth,
            maxWidth,
            noRightBorder,
            ariaDescription,
            ariaDescriptionId,
            skipContentForSR,
            displayName,
            className,
            style,
            children,
            ...props
        } = this.props;

        const {resizing, minWidth} = this.state;

        const child = this.renderChildren(
            ariaDescription,
            skipContentForSR,
            ariaDescriptionId,
            children
        );

        const isResizable = this.isResizable();
        const resizeHandler = this.renderResizeHandler();

        const classNames = classnames(className, {
            resizable: isResizable,
            resizing: isResizable && resizing
        });

        let styles = style;
        if (isResizable) {
            const width = this.getResizeColumnWidth();
            if (width) {
                styles = {...styles, width};
            }
        } else if (minWidth) {
            styles = {...styles, width: minWidth};
        }

        if (isSortable) {
            return (
                <GridSorter
                    ref={th => (this.gridSorterRef = th)}
                    displayName={displayName || children}
                    className={classNames}
                    style={styles}
                    {...props}
                    append={append}
                    prepend={resizeHandler}>
                    {child}
                </GridSorter>
            );
        } else {
            const {testAutomationId, ...restProps} = props;
            const tableHeaderProps = _.omit(restProps, Object.keys(GridHeaderCell.defaultProps));
            return (
                <th
                    ref={cell => (this.cellRef = cell)}
                    data-test-automation-id={testAutomationId}
                    className={classNames}
                    style={styles}
                    {...tableHeaderProps}>
                    {resizeHandler}
                    {append && <span className="column-header-append">{append}</span>}
                    <div className="text-overflow">{child}</div>
                </th>
            );
        }
    }
}
