/* global document */

import React from 'react';
import PropTypes from 'prop-types';
import onlyUpdateForKeys from 'recompose/onlyUpdateForKeys';
import styled, { css } from 'styled-components';

import { flex_col } from '@descarteslabs/pizzazz/styles/mixins';
import * as colors from '@descarteslabs/pizzazz/styles/colors';
import * as shadows from '@descarteslabs/pizzazz/styles/shadows';
import * as dimensions from '@descarteslabs/pizzazz/styles/dimensions';

/* Styled components. */

// Main container.
const ContextMenuContainer = styled.div`
    ${flex_col()}
    background: ${colors.fill[1]};
    position: absolute;
    left: ${({ left }) => `${left}px`};
    top: ${({ top }) => `${top}px`};
    ${shadows.medium}
    width: 10rem;
`;
const ContextMenuOption = styled.div`
    text-align: center;
    white-space: nowrap;
    padding: ${dimensions.base_small};
    ${({ disabled }) => (disabled
        ? css`
            opacity: 0.4;
            &:hover {
                cursor: not-allowed;
            }
        `
        : css`
            &:hover {
                cursor: pointer;
                background: ${colors.fill[2]};
            }
        `
    )}
`;

/* Context option component. */

const Option = ({ label, handler, disabled, outsideHandler }) => (
    <ContextMenuOption
        className="ContextMenuOption"
        onClick={e => { if (disabled) return void 0; handler(e); return outsideHandler(e); }}
        disabled={disabled}
    >
        {label}
    </ContextMenuOption>
);

Option.propTypes = {
    disabled: PropTypes.bool.isRequired,
    handler: PropTypes.func.isRequired,
    label: PropTypes.string.isRequired,
    outsideHandler: PropTypes.func.isRequired,
};

/* Context menu component. */

class ContextMenu extends React.Component {


    static propTypes = {
        isOpen: PropTypes.bool, // whether menu is open/visible.
        left: PropTypes.number, // left offset in pixels.
        options: PropTypes.array, // Array of options for context menu.
        top: PropTypes.number, // right offset in pixels.
        onClickOutside: PropTypes.func, // handler for clicks outside of context menu.
    };

    static defaultProps = {
        left: -50,
        top: 0,
        options: [],
        isOpen: false,
        onClickOutside: _ => _,
    };

    constructor(props) {
        super(props);
        const { isOpen } = props;
        this.state = {
            isOpen,
        };
    }

    componentDidMount() {
        document.addEventListener('mousedown', this.handleClickOutside);
    }

    componentWillReceiveProps(nextProps) {
        if (!('isOpen' in nextProps)) return;
        const { isOpen } = nextProps;
        this.setState({
            isOpen,
        });
    }

    componentWillUnmount() {
        document.removeEventListener('mousedown', this.handleClickOutside);
    }

    setWrapperRef = node => {
        this.wrapperRef = node;
    }

    handleClickOutside = e => {
        const { isOpen } = this.state;
        if (isOpen && this.wrapperRef && !this.wrapperRef.contains(e.target)) {
            this._onClickOutside(e);
        }
    }

    _onClickOutside = e => {
        this.setState({
            isOpen: false,
        });
        this.props.onClickOutside(e);
    }

    render() {
        const { options, top, left } = this.props;
        const { isOpen } = this.state;
        return (isOpen
            ? <ContextMenuContainer
                className="ContextMenuContainer"
                innerRef={this.setWrapperRef}
                left={left}
                top={top}
            >
                {options.map(({ label, handler, disabled }) => (
                    <Option
                        key={label}
                        label={label}
                        handler={handler}
                        disabled={disabled}
                        outsideHandler={this._onClickOutside}
                    />
                ))}
            </ContextMenuContainer>
            : null
        );
    }
}

// Only update for these props.
const enhance = onlyUpdateForKeys([ 'options', 'top', 'left', 'isOpen', 'onClickOutside' ]);

export default enhance(ContextMenu);
