import { TinyDatePicker, DateRangePicker } from 'tiny-date-picker/dist/date-range-picker';
import { fireEvent } from '@nimius/event-utility';
import locales from './locales';
import moment from 'moment';
import { revertTimezoneOffset } from '../common/date-formatting';
import { config } from './config';

export default class ToubizDatePicker {

    /**
     * @param {HTMLElement} node
     */
    constructor(node) {

        /** @private {HTMLElement} */
        this.node = node;

        /** @private {HTMLButtonElement} */
        this.button = node.querySelector(config.selectors.button);

        /** @private {HTMLElement} */
        this.label = node.querySelector(config.selectors.label);

        /** @private {HTMLElement} */
        this.overlay = node.querySelector(config.selectors.overlay);

        /** @private {HTMLButtonElement} */
        this.resetButton = node.querySelector(config.selectors.resetButton);

        /** @private {HTMLElement} */
        this.rangeSelectionNode = node.querySelector(config.selectors.rangeSelection);

        /** @private {HTMLElement} */
        this.singleSelectionNode = node.querySelector(config.selectors.singleSelection);

        /** @private {string} */
        this.type = this.singleSelectionNode ? 'single' : 'range';

        this.init();
    }

    togglePicker() {
        if (this.node.classList.contains(config.classes.active)) {
            this.hidePicker();
        } else {
            this.showPicker();
        }
    }

    resetValue() {
        this.node.classList.remove(config.classes.dateSelected);
        this.datePicker.setState({
            start: null,
            end: null,
            selectedDate: null,
        });
    }

    showPicker() {
        this.node.classList.add(config.classes.active);
        fireEvent(this.node, 'toubiz-date-picker.open');
    }

    hidePicker() {
        this.node.classList.remove(config.classes.active);
        fireEvent(this.node, 'toubiz-date-picker.close');
    }

    formatDate(date) {
        return date.toLocaleDateString();
    }

    onRangeSelected(start, end) {
        start = start ? revertTimezoneOffset(start) : null;
        end = end ? revertTimezoneOffset(end) : null;

        const formattedDateStart = start ? this.formatDate(start) : '';
        const formattedDateEnd = end ? this.formatDate(end) : '';

        if (start && end) {
            this.node.classList.add(config.classes.dateSelected);

            if (this.label) {
                this.label.innerHTML = `${formattedDateStart} - ${formattedDateEnd}`;
            }

            this.hidePicker();

            fireEvent(this.node, 'toubiz-date-picker.change', {
                range: {
                    start,
                    end,
                },
                formatted: {
                    start: formattedDateStart,
                    end: formattedDateEnd,
                },
            });
        }
    }

    onDateSelected(selectedDate, highlightedDate) {
        selectedDate = revertTimezoneOffset(selectedDate);
        highlightedDate = revertTimezoneOffset(highlightedDate);

        this.node.classList.add(config.classes.dateSelected);
        const formattedDateSelected = this.formatDate(selectedDate);
        const formattedDateHighlighted = highlightedDate ? this.formatDate(highlightedDate) : '';

        if (this.label) {
            this.label.innerHTML = `${selectedDate ? formattedDateSelected : ''}`;
        }

        this.hidePicker();
        fireEvent(this.node, 'toubiz-date-picker.change', {
            date: {
                selectedDate,
                highlightedDate,
            },
            formatted: {
                selectedDate: formattedDateSelected,
                highlightedDate: formattedDateHighlighted,
            },
        });
    }

    initRangeSelection() {
        this.datePicker = DateRangePicker(this.rangeSelectionNode, {
            startOpts: this.datePickerConfig,
            endOpts: this.datePickerConfig,
        });

        this.node.addEventListener('toubiz-date-picker.set', event => {
            this.datePicker.setState({ start: event.detail.start, end: event.detail.end });
        });

        this.datePicker.on(
            'statechange',
            (_, datePicker) => this.onRangeSelected(datePicker.state.start, datePicker.state.end)
        );
    }

    initSingleSelection() {
        this.datePicker = TinyDatePicker(this.singleSelectionNode, this.datePickerConfig);
        this.datePicker.on(
            'select',
            (_, datePicker) => this.onDateSelected(datePicker.state.selectedDate, datePicker.state.highlightedDate)
        );
    }

    buildSelectionConfig(parsedConfig) {
        const datePickerConfig = { mode: 'dp-permanent' };

        // Set inRange function of TinyDatePicker based on array of excluded dates
        if (parsedConfig.excludedDates) {
            const excludedDatesArray = JSON.parse(parsedConfig.excludedDates);
            datePickerConfig.inRange = date => !excludedDatesArray.includes(moment(date).format('MM/DD/YYYY'));
        }

        // Set min and max dates if configured.
        if (parsedConfig.minDate) {
            datePickerConfig.min = parsedConfig.minDate;
        }
        if (parsedConfig.maxDate) {
            datePickerConfig.max = parsedConfig.maxDate;
        }

        // Set locale labels and dayOffset based on configured locale
        if (parsedConfig.locale) {
            if (parsedConfig.locale !== 'en') {
                if (parsedConfig.locale !== 'custom') {
                    const localeObject = locales[parsedConfig.locale];
                    datePickerConfig.lang = localeObject.labels;
                    datePickerConfig.dayOffset = localeObject.dayOffset;
                } else {
                    // TODO: parse individual locale labels from markup.
                }
            }
        }

        // Set highlighted date if one was configured
        if (parsedConfig.highlightedDate) {
            datePickerConfig.highlightedDate = parsedConfig.highlightedDate;
        }

        /*
         * TODO: set dateClass function of TinyDatePicker based on
         * configured object of specially highlighted dates, for assigning
         * classes, e.g.
         *
         *  specialDates: {
         *      'available-day': [dates]
         *  }
         */

        return datePickerConfig;
    }

    parseConfig() {
        const parsedConfig = {};

        Object.keys(config.attributes).forEach(attrKey => {
            const attr = config.attributes[attrKey];
            const value = this.node.getAttribute(attr);
            if (value) {
                parsedConfig[attrKey] = value;
            }
        });

        return parsedConfig;
    }

    init() {
        const parsedConfig = this.parseConfig();
        this.datePickerConfig = this.buildSelectionConfig(parsedConfig);

        if (this.type === 'single') {
            this.initSingleSelection();
        } else {
            this.initRangeSelection();
        }

        this.setInitialState();

        if (this.button) {
            this.button.addEventListener('click', event => {
                event.preventDefault();
                this.togglePicker();
            });

            this.button.addEventListener('focus', event => {
                event.preventDefault();
                this.showPicker();
            });

            this.button.addEventListener('focusout', event => {
                event.preventDefault();
                if (!this.node.contains(event.relatedTarget)) {
                    this.hidePicker();
                }
            });
        }

        if (this.overlay) {
            this.overlay.addEventListener('click', event => {
                if (event.target === this.overlay) {
                    this.hidePicker();
                }
            });
        }

        if (this.resetButton) {
            this.resetButton.addEventListener('click', () => this.resetValue());
        }

        this.node.addEventListener('toubiz-date-picker.reset', () => this.resetValue());
    }

    setInitialState() {
        if (this.node.hasAttribute(config.attributes.initialState)) {
            const initialState = JSON.parse(this.node.getAttribute(config.attributes.initialState));
            if (initialState.start) {
                initialState.start = new Date(initialState.start);
            }
            if (initialState.end) {
                initialState.end = new Date(initialState.end);
            }
            if (initialState.selectedDate) {
                initialState.selectedDate = new Date(initialState.selectedDate);
            }
            this.datePicker.setState(initialState);
        }
    }

}
