import React from 'react';
import PropTypes from 'prop-types';
import {TextInputControl} from '../TextInputControl';
import {DayPickerSingleDateController} from 'react-dates';
import moment from 'moment';
import {TimePicker} from './TimePicker';
import {is24HoursMode} from './datePickerHelpers';
import {DatePickerBase} from './DatePickerBase';
import {MonthYearDropDown} from './MonthYearDropDown';
import {Button} from '../../Button';
import {translate} from '../../../../utils';
import {playMessage} from '../../../utils/playMessage';
import {renderCalendarDayContent} from './renderCalendarDayContent';
import {wait} from '../../..';

const WAIT_CALENDAR_READY_TIMEOUT = 1;

export class SingleDatePickerInputControl extends DatePickerBase {
    static propTypes = {
        ...TextInputControl.propTypes,
        ...DatePickerBase.propTypes,

        dateFormat: PropTypes.string,
        timeFormat: PropTypes.string,

        utc: PropTypes.bool,
        hasOnlyDate: PropTypes.bool,
        hasOnlyTime: PropTypes.bool,
        hasStringFormat: PropTypes.bool,
        hasMomentFormat: PropTypes.bool,
        hasTimestampFormat: PropTypes.bool,

        onChange: PropTypes.func,
        numberOfMonths: PropTypes.number,
        isValidDate: PropTypes.func,

        singleDatePickerProps: PropTypes.object,
        inputProps: PropTypes.object,

        monthYearFormat: PropTypes.string,

        ariaTextDatePickerDescribe: PropTypes.string,
        ariaTextSelectedDateValue: PropTypes.string,
        ariaTextNextMonth: PropTypes.string,
        ariaTextPrevMonth: PropTypes.string,

        timePickerAriaDescription: PropTypes.string,
        timePickerCurrentTimeSettingAriaPrompt: PropTypes.string,
        timePickerHourIncreaseBtnAriaLabel: PropTypes.string.isRequired,
        timePickerHourDecreaseBtnAriaLabel: PropTypes.string.isRequired,
        timePickerMinuteIncreaseBtnAriaLabel: PropTypes.string.isRequired,
        timePickerMinuteDecreaseBtnAriaLabel: PropTypes.string.isRequired,
        timeAmAriaLabel: PropTypes.string,
        timePmAriaLabel: PropTypes.string,
        amPmSwitcherLabel: PropTypes.string,
        enablePMTitle: PropTypes.string,
        enableAMTitle: PropTypes.string,

        monthYearBtnAriaLabel: PropTypes.string,
        monthAriaLabel: PropTypes.string,
        yearAriaLabel: PropTypes.string,

        yearsBeforeCurrent: PropTypes.number,
        yearsAfterCurrent: PropTypes.number
    };

    static defaultProps = {
        ...DatePickerBase.defaultProps,
        ...TextInputControl.defaultProps,
        testAutomationId: 'dateTimePicker',
        dateFormat: 'L',
        timeFormat: 'h:mm A',
        utc: false,
        hasOnlyTime: false,
        hasOnlyDate: false,
        numberOfMonths: 1,
        isValidDate: (mode, currentDay) => true,
        singleDatePickerProps: {},
        inputProps: {},
        monthYearFormat: 'MMMM YYYY',
        timePickerCurrentTimeSettingAriaPrompt: 'TRANSLATE:TIME_PICKER_ARIA_CURRENT_TIME',
        timePickerAriaDescription: 'TRANSLATE:TIME_PICKER_ARIA_DESCRIPTION',
        timePickerHourIncreaseBtnAriaLabel: 'TRANSLATE:TIME_PICKER_HOUR_INCREASE_ARIA_LABEL',
        timePickerHourDecreaseBtnAriaLabel: 'TRANSLATE:TIME_PICKER_HOUR_DECREASE_ARIA_LABEL',
        timePickerMinuteIncreaseBtnAriaLabel: 'TRANSLATE:TIME_PICKER_MINUTE_INCREASE_ARIA_LABEL',
        timePickerMinuteDecreaseBtnAriaLabel: 'TRANSLATE:TIME_PICKER_MINUTE_DECREASE_ARIA_LABEL',
        timeAmAriaLabel: 'TRANSLATE:TIME_PICKER_AM',
        timePmAriaLabel: 'TRANSLATE:TIME_PICKER_PM',
        monthYearBtnAriaLabel: 'TRANSLATE:DATE_PICKER_MONTH_BUTTON_ARIA_LABEL',
        monthAriaLabel: 'TRANSLATE:DATE_PICKER_MONTH_DROPDOWN_ARIA_LABEL',
        yearAriaLabel: 'TRANSLATE:DATE_PICKER_YEAR_DROPDOWN_ARIA_LABEL',
        amPmSwitcherLabel: 'TRANSLATE:AM_PM_SWITCHER',
        enablePMTitle: 'TRANSLATE:CLICK_TO_ENABLE_PM',
        enableAMTitle: 'TRANSLATE:CLICK_TO_ENABLE_AM',
        yearsBeforeCurrent: 100,
        yearsAfterCurrent: 100,

        hasStringFormat: false,
        hasMomentFormat: false,
        hasTimestampFormat: false
    };

    state = {
        ...super.state,
        isMonthYearEditing: false,
        value: this.getDateTimeValue()
    };

    parseUtcValue(value) {
        const {utc} = this.props;
        if (value) {
            return utc ? moment.utc(value) : moment(value);
        }

        return null;
    }

    getDateTimeValue() {
        const {value} = this.getPropsFromContext();
        return this.parseUtcValue(value);
    }

    notifyDateTimeChange = momentValue => {
        const value = moment(momentValue);
        let {onChange} = this.getPropsFromContext();
        onChange(this.getFormattedReturnValue(value));
    };

    getFormattedReturnValue(value) {
        const {hasStringFormat, hasTimestampFormat, hasMomentFormat} = this.props;

        if (!value) {
            return null;
        }
        if (typeof value === 'string') {
            return value;
        }
        if (hasStringFormat) {
            return value.format();
        }
        if (hasTimestampFormat) {
            return Number(value.toDate());
        }
        if (hasMomentFormat) {
            return value;
        }
        return value.toDate();
    }

    /**
     * @param {Moment} newValue
     * */
    onDateChange = newValue => {
        const oldValue = this.getDateTimeValue();

        const oldMomentValue = moment(oldValue);

        let value = moment(newValue);

        if (oldValue) {
            value = value
                .hour(oldMomentValue.hour())
                .minute(oldMomentValue.minute())
                .second(oldMomentValue.second());
        }

        this.setState({value}, () => this.notifyDateTimeChange(value));

        this.closeDayPicker();
        this.toggleIsInputFocusedFromOutside(false);
        this.focusToInput();
    };

    getDateTimeMomentValue() {
        let value = this.getDateTimeValue();

        if (!value || !moment(value).isValid()) {
            value = moment();
        } else {
            value = moment(value);
        }

        return value;
    }

    getTimeValue() {
        const value = this.getDateTimeMomentValue();

        return {
            hours: value.hour(),
            minutes: value.minute()
        };
    }

    onTimeChange = ({hours, minutes}) => {
        const newValue = moment(this.getDateTimeMomentValue())
            .hour(hours)
            .minute(minutes);
        this.setState({value: newValue}, () => {
            this.notifyDateTimeChange(newValue);
            playMessage(this.getTimePickerPromptMessage(newValue));
        });
    };

    initialVisibleMonth = () => {
        const value = this.getDateTimeValue();
        return value ? moment(value) : moment();
    };

    handleSwitchToMonthYearEditing = () => {
        return this.blurDateCell().then(() => this.setState({isMonthYearEditing: true}));
    };

    onSwitchToMonthYearText = () => this.setState({isMonthYearEditing: false});

    /* eslint-disable react/prop-types */
    renderMonthsDropDown = ({month, onMonthSelect, onYearSelect}) => {
        const {monthAriaLabel, yearAriaLabel, yearsAfterCurrent, yearsBeforeCurrent} = this.props;

        const onMonthSelectHandler = (month, value) => {
            onMonthSelect(month, value);
            this.onSwitchToMonthYearText();
        };

        const onYearSelectHandler = (month, value) => {
            onYearSelect(month, value);
            this.onSwitchToMonthYearText();
        };

        return (
            <MonthYearDropDown
                month={month}
                onMonthSelect={onMonthSelectHandler}
                onYearSelect={onYearSelectHandler}
                monthAriaLabel={monthAriaLabel}
                yearAriaLabel={yearAriaLabel}
                yearsBeforeCurrent={yearsBeforeCurrent}
                yearsAfterCurrent={yearsAfterCurrent}
            />
        );
    };

    renderMonthYearText = ({month}) => {
        const {monthYearFormat, monthYearBtnAriaLabel} = this.props;

        const ariaLabel = translate(monthYearBtnAriaLabel, {
            month: month.format('MMMM'),
            year: month.year()
        });

        return (
            <Button
                testAutomationId="editYearMonthButton"
                bsStyle="link"
                onClick={this.handleSwitchToMonthYearEditing}
                aria-label={ariaLabel}>
                <strong>{month.format(monthYearFormat)}</strong>
            </Button>
        );
    };

    /* eslint-enable react/prop-types */

    renderSingleDatePicker() {
        const {numberOfMonths, singleDatePickerProps, isValidDate} = this.props;
        const {isDayPickerFocused, isDayPickerVisible} = this.state;
        const value = this.getDateTimeValue();
        const renderMonthElement = this.state.isMonthYearEditing
            ? this.renderMonthsDropDown
            : this.renderMonthYearText;

        return isDayPickerVisible ? (
            <DayPickerSingleDateController
                renderCalendarInfo={this.renderTimePicker}
                calendarInfoPosition={'after'}
                isFocused={isDayPickerFocused}
                focused={true}
                initialVisibleMonth={this.initialVisibleMonth}
                onBlur={this.closeDayPicker}
                numberOfMonths={numberOfMonths}
                onDateChange={newValue => this.onDateChange(newValue)}
                renderMonthElement={renderMonthElement}
                date={moment(value)}
                isOutsideRange={day => !isValidDate('days', day)}
                hideKeyboardShortcutsPanel
                phrases={this.getPhrases()}
                renderDayContents={renderCalendarDayContent}
                {...singleDatePickerProps}
            />
        ) : null;
    }

    handleBackFromTimePicker = () => wait(WAIT_CALENDAR_READY_TIMEOUT).then(this.focusOnDateCell);

    renderTimePicker = () => {
        const {
            hasOnlyDate,
            timeFormat,
            timePickerHourDecreaseBtnAriaLabel,
            timePickerHourIncreaseBtnAriaLabel,
            timePickerMinuteDecreaseBtnAriaLabel,
            timePickerMinuteIncreaseBtnAriaLabel,
            timeAmAriaLabel,
            timePmAriaLabel,
            amPmSwitcherLabel,
            enableAMTitle,
            enablePMTitle,
            timePickerAriaDescription
        } = this.props;

        if (hasOnlyDate) {
            return null;
        }

        const value = this.getTimeValue();
        const ariaDescription = translate(timePickerAriaDescription, {
            time: moment(value).format(timeFormat)
        });

        return (
            <div onMouseUp={this.blurDateCell}>
                <TimePicker
                    onChange={this.onTimeChange}
                    value={value}
                    is24hour={is24HoursMode(timeFormat)}
                    onPanelFocus={this.blurDateCell}
                    onBackToCalendar={this.handleBackFromTimePicker}
                    ariaDescription={ariaDescription}
                    hourIncreaseBtnAriaLabel={timePickerHourIncreaseBtnAriaLabel}
                    hourDecreaseBtnAriaLabel={timePickerHourDecreaseBtnAriaLabel}
                    minuteIncreaseBtnAriaLabel={timePickerMinuteIncreaseBtnAriaLabel}
                    minuteDecreaseBtnAriaLabel={timePickerMinuteDecreaseBtnAriaLabel}
                    amAriaLabel={timeAmAriaLabel}
                    pmAriaLabel={timePmAriaLabel}
                    amPmSwitcherLabel={amPmSwitcherLabel}
                    enablePMTitle={enablePMTitle}
                    enableAMTitle={enableAMTitle}
                />
            </div>
        );
    };

    getTimePickerPromptMessage = timeValue => {
        const {timeFormat, timePickerCurrentTimeSettingAriaPrompt} = this.props;

        return translate(timePickerCurrentTimeSettingAriaPrompt, {
            time: moment(timeValue).format(timeFormat)
        });
    };

    getDatePickerContainerClassName() {
        return 'rc-datepicker-single';
    }

    handleMouseUpDayPickerContainer = () => {
        this.onSwitchToMonthYearText();
    };

    /** @override */
    getDisplayValue = () => {
        const value = this.getDateTimeValue();
        const {dateFormat, timeFormat, hasOnlyDate, hasOnlyTime} = this.props;

        if (!value) {
            return '';
        }

        if (!moment(value).isValid()) {
            return value;
        }

        const formatArr = [];

        if (!hasOnlyTime) {
            formatArr.push(dateFormat);
        }

        if (!hasOnlyDate) {
            formatArr.push(timeFormat);
        }

        return moment(value).format(formatArr.join(' '));
    };

    /** @override */
    renderDatePicker = () => {
        const {hasOnlyTime} = this.props;
        return hasOnlyTime ? this.renderTimePicker() : this.renderSingleDatePicker();
    };
}
