import React, { FC, useState, useEffect } from 'react';
import {
    Button,
    Dropdown,
    DropdownComponents,
    DropdownMenuProps,
    DropdownOption,
    DropdownValueContainerProps
} from '@theorchard/suite-components';
import { formatMessage } from '@theorchard/suite-frontend';
import cx from 'classnames';
import dayjs from 'dayjs';
import { get, find } from 'lodash';
import { useLocation } from 'react-router-dom';
import Segment from 'src/segment';
import { validateDate, ensureString, useRouteParams } from './utils/route';
import { DEFAULT_DATE_RANGE_DAYS } from '../constants';
import DateRangePicker, {
    DatePickerRange,
    RangePickerCallback
} from '../dateRangePicker/dateRangePicker';

const CLASSNAME = 'DateFilter';
export const CLASSNAME_CUSTOM_MENU = `${CLASSNAME}-custom-menu`;
export const CLASSNAME_ORIGINAL_MENU = `${CLASSNAME}-original-menu`;
const CLASSNAME_CUSTOM_MENU_INFO = `${CLASSNAME_CUSTOM_MENU}-info`;
const CLASSNAME_CONTROL_BUTTONS = `${CLASSNAME_CUSTOM_MENU}-control-buttons`;
const CLASSNAME_SELECTED_DAYS = `${CLASSNAME_CUSTOM_MENU}-selected-days`;
const CLASSNAME_CUSTOM_VALUE_CONTAINER = `${CLASSNAME}-custom-value-container`;
const CLASSNAME_CUSTOM_VALUE_CONTAINER_DATES = `${CLASSNAME}-custom-value-container-dates`;
const CANCEL_TERM = 'generic.cancel';
const COUNT_TERM = 'filtering.selectedDaysCount';
const RESET_TERM = 'filtering.reset';
const APPLY_TERM = 'filtering.apply';
const DATE_FORMAT = 'YYYY-MM-DD';
const DATE_FORMAT_STANDARD = 'D MMM YYYY';
const CUSTOM = 'custom';
const QUERY_PARAM_DATE_PICKER_START = 'startDate';
const QUERY_PARAM_DATE_PICKER_END = 'endDate';
const QUERY_PARAM_DATE_RANGE = 'dateRange';
const DATE_FILTER = 'dateFilter';
const segmentCategory = 'Date filter - Custom';

interface OptionsProps {
    label: string;
    value: string;
    disabled?: boolean;
}

interface OnChangeProps {
    value?: string;
}

export interface Props {
    selectedValue?: string;
    className?: string;
    disabled?: boolean;
    onChange: (params: OnChangeProps) => void;
    options: Array<OptionsProps>;
    disableDatesBefore: Date;
    disableDatesAfter: Date;
}

const nonNullable = <T extends object | string | number | Date>(data?: T | null) => (data === null ? undefined : data as T);

const defaultDateRange = {
    from: dayjs().add(-DEFAULT_DATE_RANGE_DAYS, 'day').format(DATE_FORMAT),
    to: dayjs().format(DATE_FORMAT),
};

const DateFilter: FC<Props> = ({
    className,
    onChange,
    selectedValue,
    disabled,
    options,
    disableDatesBefore,
    disableDatesAfter
}) => {
    const [customView, setCustomView] = useState(false);
    const location = useLocation();
    const [params, setParams] = useRouteParams(location.pathname);
    const startDate = ensureString(get(params, QUERY_PARAM_DATE_PICKER_START));
    const endDate = ensureString(get(params, QUERY_PARAM_DATE_PICKER_END));
    const calendarStartsFrom = validateDate(disableDatesBefore);
    const calendarEndsOn = validateDate(disableDatesAfter);
    const [selectedDropdownValue, setSelectedDropdownValue] = useState<OptionsProps>();
    const [dateRangeData, setDateRangeData] = useState<DatePickerRange>({
        from: (startDate ? dayjs(startDate) : dayjs().add(-DEFAULT_DATE_RANGE_DAYS, 'day')).toString(),
        to: dayjs(endDate).toString(),
    });

    const handleRangePickerChange: RangePickerCallback = (range) => {
        setDateRangeData(range);
    };

    useEffect(() => {
        const value = find(options, { value: selectedValue });
        setSelectedDropdownValue(value);
    }, [selectedValue, options]);

    const handleCloseCustomMenu = () => {
        setCustomView(false);
        Segment.trackEvent('Click - Reset', { category: segmentCategory });
    };

    const handleApplyCustomOption = (setValue: (value: DropdownOption | null) => void) => {
        if (selectedValue !== CUSTOM)
            setValue(selectedValue ? { value: selectedValue } : null);

        const queryStartDate = dayjs(dateRangeData?.from).format(DATE_FORMAT);
        const queryEndDate = dayjs(dateRangeData?.to).format(DATE_FORMAT);
        setParams?.({
            [QUERY_PARAM_DATE_PICKER_START]: queryStartDate,
            [QUERY_PARAM_DATE_PICKER_END]: queryEndDate,
            [QUERY_PARAM_DATE_RANGE]: CUSTOM
        });
        handleCloseCustomMenu();
        Segment.trackEvent('Click - Apply', { category: segmentCategory, label: { startDate: queryStartDate, endDate: queryEndDate } });
    };

    const handleChange = (name:string, value?: string | string[]) => {
        const isCustom = value === CUSTOM;

        if (!isCustom) {
            const singleValue = Array.isArray(value) ? value?.[0] : value;

            onChange({ value: singleValue });
            setParams?.({
                [QUERY_PARAM_DATE_PICKER_START]: '',
                [QUERY_PARAM_DATE_PICKER_END]: '',
                [QUERY_PARAM_DATE_RANGE]: value
            });
        } else {
            setCustomView(true);
            Segment.trackEvent('Click - Custom', { category: segmentCategory });
        }
    };

    const selectedDayRange = (dayjs(nonNullable(dateRangeData?.to)).diff(dateRangeData?.from ?? '', 'days') + 1) || 1;

    const resetDateRage = () => {
        setDateRangeData(defaultDateRange);
    };

    const ValueContainer: React.FC<DropdownValueContainerProps<DropdownOption>> = ({ children, ...props }) => {
        const isCustom = selectedValue === CUSTOM;
        return (
            <DropdownComponents.ValueContainer
                { ...props }
                className={ isCustom ? CLASSNAME_CUSTOM_VALUE_CONTAINER : undefined }
            >
                {isCustom ? (
                    <>
                        {children}
                        <span className={ CLASSNAME_CUSTOM_VALUE_CONTAINER_DATES }>
                            <span>
                                {dayjs(startDate).format(DATE_FORMAT_STANDARD)}
                            </span>
                            <span>-</span>
                            <span>
                                {dayjs(endDate).format(DATE_FORMAT_STANDARD)}
                            </span>
                        </span>
                    </>
                ) : children}
            </DropdownComponents.ValueContainer>
        );
    };

    const Menu = (menuProps: DropdownMenuProps<DropdownOption>) => (
        <DropdownComponents.Menu { ...menuProps }>
            {customView ? (
                <div className={ CLASSNAME_CUSTOM_MENU } data-testid={ CLASSNAME_CUSTOM_MENU }>
                    <div className={ CLASSNAME_CUSTOM_MENU_INFO }>
                        <span className={ CLASSNAME_SELECTED_DAYS }>{formatMessage(COUNT_TERM, { days: selectedDayRange })}</span>
                        <Button variant="link" size="sm" onClick={ resetDateRage }>{formatMessage(RESET_TERM)}</Button>
                    </div>
                    <DateRangePicker
                        onChange={ handleRangePickerChange }
                        data={ dateRangeData }
                        calendarStartsFrom={ calendarStartsFrom }
                        calendarEndsOn={ calendarEndsOn }
                    />
                    <div className={ CLASSNAME_CONTROL_BUTTONS }>
                        <Button variant="link" size="sm" onClick={ handleCloseCustomMenu }>
                            {formatMessage(CANCEL_TERM)}
                        </Button>
                        <Button
                            variant="link"
                            size="sm"
                            disabled={ dateRangeData.from === dateRangeData.to }
                            onClick={ () => handleApplyCustomOption(value => menuProps.setValue(value, 'select-option')) }
                        >
                            {formatMessage(APPLY_TERM)}
                        </Button>
                    </div>
                </div>
            ) : (
                <div>{menuProps.children}</div>
            )}
        </DropdownComponents.Menu>
    );

    return (
        <Dropdown
            name={ DATE_FILTER }
            className={ cx(CLASSNAME, className) }
            isDisabled={ disabled }
            onValueChanged={ handleChange }
            options={ options }
            value={ selectedDropdownValue }
            menuIsOpen={ customView || undefined }
            components={ { Menu, ValueContainer } }
            onMenuClose={ () => setCustomView(false) }
            onBlur={ () => setCustomView(false) }
        />
    );
};

export default DateFilter;
