import {default as BSDateTimeControl} from 'react-datetime';
import ReactDOM from 'react-dom';
import _ from 'lodash';

const ENTER_KEY_CODE = 13;
const SPACE_KEY_CODE = 32;
const ESC_KEY_CODE = 27;

const VIEWS = {
    DAYS: 'days',
    MONTHS: 'months',
    YEARS: 'years',
    TIME: 'time'
};

const blurSelector = '[tabindex="0"], .rdt > input';

/**
 * @param {string} eventName
 * @param {{[bubbles]: Boolean, [cancelable]: Boolean}} eventArgs
 * */
const createMouseEvent = (eventName, eventArgs = {}) => {
    try {
        return new MouseEvent(eventName, eventArgs);
    } catch (e) {
        // https://jira.ringcentral.com/browse/UIA-68403
        const event = document.createEvent('MouseEvent');
        event.initMouseEvent(
            eventName,
            true,
            true,
            window,
            0,
            0,
            0,
            0,
            0,
            false,
            false,
            false,
            false,
            0,
            null
        );
        return event;
    }
};

export class BSDatePicker extends BSDateTimeControl {
    adjustedView: any;
    stopClosing: any;
    closeCalendar: any;

    componentDidMount() {
        this.adjustForRC();
        this.addEventListeners();
    }

    componentDidUpdate() {
        this.adjustForRC();
    }

    componentWillUnmount() {
        this.removeEventListeners();
    }

    adjustForRC() {
        let {
            disabled,
            changeYearText,
            changeMonthText,
            setPrevDecadeText,
            setNextDecadeText,
            setPrevYearText,
            setNextYearText,
            setPrevMonthText,
            setNextMonthText,
            setPrevHourText,
            setNextHourText,
            setPrevMinuteText,
            setNextMinuteText,
            toggleAMPMText
        } = this.props as any;
        let {currentView} = this.state as any;

        const setAutomationId = (node, testId) => {
            if (node) {
                node.setAttribute('data-test-automation-id', testId);
            }
        };
        const setKeyboardInteractive = (node, interactive = true) => {
            if (node) {
                if (interactive && !disabled) {
                    node.setAttribute('role', 'button');
                    node.setAttribute('tabindex', 0);
                    node.addEventListener('keydown', this.mouseDownByKeyboard);
                } else {
                    node.removeAttribute('role');
                    node.removeAttribute('tabindex');
                }
            }
        };
        const setAriaLabel = (node, ariaLabel) => {
            if (node) {
                if (ariaLabel) {
                    node.setAttribute('aria-label', ariaLabel);
                } else {
                    node.removeAttribute('aria-label');
                }
            }
        };

        let node = ReactDOM.findDOMNode(this) as any;

        let previousButton = node.querySelector('.rdtPrev');
        let nextButton = node.querySelector('.rdtNext');
        let switchViewButton = node.querySelector('.rdtSwitch');
        let switchToTimeViewButton = node.querySelector('.rdtTimeToggle');
        let [hoursUp, hoursDown, minutesUp, minutesDown, AMPMUp, AMPMDown] = _.toArray(
            node.querySelectorAll(`.rdtCounters .rdtBtn`)
        );
        let [hour, minute, ampm] = _.toArray(node.querySelectorAll('.rdtCounters .rdtCount'));

        setAutomationId(node.querySelector('.rdtPicker'), 'datePickerOverlay');
        setAutomationId(previousButton, 'buttonPrev');
        setAutomationId(nextButton, 'buttonNext');
        setAutomationId(switchViewButton, 'buttonSwitch');
        setAutomationId(switchToTimeViewButton, 'buttonSwitchTime');
        setAutomationId(hoursUp, 'buttonUpHour');
        setAutomationId(hoursDown, 'buttonDownHour');
        setAutomationId(minutesUp, 'buttonUpMinute');
        setAutomationId(minutesDown, 'buttonDownMinute');
        setAutomationId(AMPMUp, 'buttonUpPMAM');
        setAutomationId(AMPMDown, 'buttonDownPMAM');
        setAutomationId(hour, 'currentHour');
        setAutomationId(minute, 'currentMinute');
        setAutomationId(ampm, 'currentPMAM');

        setKeyboardInteractive(previousButton);
        setKeyboardInteractive(nextButton);
        setKeyboardInteractive(switchViewButton, currentView !== VIEWS.YEARS);
        setKeyboardInteractive(switchToTimeViewButton);
        setKeyboardInteractive(hoursUp);
        setKeyboardInteractive(hoursDown);
        setKeyboardInteractive(minutesUp);
        setKeyboardInteractive(minutesDown);
        setKeyboardInteractive(AMPMUp);
        setKeyboardInteractive(AMPMDown);

        switch (currentView) {
            case VIEWS.DAYS:
                if (switchViewButton) {
                    setAriaLabel(
                        switchViewButton,
                        `${switchViewButton.innerText}, ${changeMonthText}`
                    );
                }
                setAriaLabel(previousButton, setPrevMonthText);
                setAriaLabel(nextButton, setNextMonthText);
                break;
            case VIEWS.MONTHS:
                if (switchViewButton) {
                    setAriaLabel(
                        switchViewButton,
                        `${switchViewButton.innerText}, ${changeYearText}`
                    );
                }
                setAriaLabel(previousButton, setPrevYearText);
                setAriaLabel(nextButton, setNextYearText);
                break;
            case VIEWS.YEARS:
                if (switchViewButton) {
                    setAriaLabel(switchViewButton, switchViewButton.innerText);
                }
                setAriaLabel(previousButton, setPrevDecadeText);
                setAriaLabel(nextButton, setNextDecadeText);
                break;
            case VIEWS.TIME:
                setAriaLabel(hoursUp, setPrevHourText);
                setAriaLabel(hoursDown, setNextHourText);
                setAriaLabel(minutesUp, setPrevMinuteText);
                setAriaLabel(minutesDown, setNextMinuteText);
                setAriaLabel(AMPMUp, toggleAMPMText);
                setAriaLabel(AMPMDown, toggleAMPMText);
                break;
        }

        if (this.adjustedView && this.adjustedView !== currentView) {
            if (currentView === VIEWS.TIME) {
                hoursUp.focus();
            } else {
                let focusNode =
                    node.querySelector('.rdtPicker .rdtActive') ||
                    node.querySelector('.rdtDay[tabindex="0"]') ||
                    node.querySelector('.rdtMonth[tabindex="0"]') ||
                    node.querySelector('.rdtYear[tabindex="0"]');
                if (focusNode) {
                    focusNode.focus();
                }
            }
        }

        this.adjustedView = currentView;

        if (minutesDown) {
            minutesDown.addEventListener('keydown', this.closeUnfocused);
        }
    }

    addEventListeners() {
        let node = ReactDOM.findDOMNode(this) as any;
        if (node) {
            node.addEventListener('mousedown', this.onDatePickerClick);
            node.addEventListener('keydown', this.mouseDownByKeyboard);
            node.addEventListener('keyup', this.mouseUpByKeyboard);
            node.addEventListener('keypress', this.clickByKeyboard);
            _.toArray(node.querySelectorAll(blurSelector)).forEach(item => {
                item.addEventListener('blur', this.closeUnfocused);
            });
        }
    }

    removeEventListeners() {
        let node = ReactDOM.findDOMNode(this) as any;
        if (node) {
            node.removeEventListener('mousedown', this.onDatePickerClick);
            node.removeEventListener('keydown', this.mouseDownByKeyboard);
            node.removeEventListener('keyup', this.mouseUpByKeyboard);
            node.removeEventListener('keypress', this.clickByKeyboard);
            _.toArray(node.querySelectorAll(blurSelector)).forEach(item => {
                item.removeEventListener('blur', this.closeUnfocused);
            });
        }
    }

    onDatePickerClick = () => {
        // Prevent close date picker by closeUnfocused method
        this.stopClosing = true;
        setTimeout(() => {
            this.stopClosing = false;
        }, 100);
    };

    closeUnfocused = () => {
        // Wait for changing a focus from input to popover and back
        setTimeout(() => {
            if (this.stopClosing) {
                this.stopClosing = false;
                return;
            }
            if (!this.isFocused()) {
                this.closeCalendar();
            }
        }, 50);
    };

    focusInput = () => {
        let node = ReactDOM.findDOMNode(this) as any;
        let input = node.querySelector('.rdt > input.form-control');
        if (input) {
            input.focus();
        }
    };

    isFocused() {
        let node = ReactDOM.findDOMNode(this) as any;
        return !!(node && node.querySelector(':focus'));
    }

    mouseDownByKeyboard = event => {
        this.closeUnfocused();
        if (event.keyCode === ESC_KEY_CODE) {
            event.stopPropagation();
            this.focusInput();
            this.closeCalendar();
        }
        if (this.props['disabled'] || event.target.isMouseDownByKeyboard) {
            return;
        }
        if (event.keyCode === ENTER_KEY_CODE || event.keyCode === SPACE_KEY_CODE) {
            event.target.dispatchEvent(createMouseEvent('mousedown', {bubbles: true}));
            event.target.isMouseDownByKeyboard = true;
        }
    };

    mouseUpByKeyboard = event => {
        if (this.props['disabled']) {
            return;
        }
        if (event.keyCode === ESC_KEY_CODE) {
            event.stopPropagation();
        }

        if (event.keyCode === ENTER_KEY_CODE || event.keyCode === SPACE_KEY_CODE) {
            event.target.isMouseDownByKeyboard = false;
            event.target.dispatchEvent(createMouseEvent('mouseup', {bubbles: true}));
        }
    };

    clickByKeyboard = event => {
        if (this.props['disabled']) {
            return;
        }
        if (event.keyCode === ENTER_KEY_CODE || event.keyCode === SPACE_KEY_CODE) {
            event.target.click();
        }
    };
}
