import {
    CLASS_NAME,
    CUSTOM_VALUE,
    DEFAULT_DATE_PICKER_HEIGHT,
    DEFAULT_DATE_RANGE_PICKER_HEIGHT,
} from './constants';
import type { DatePickerBaseProps, DatePickerParsedOption } from './types';
import { ListViewAside, ListViewAsideSection, isListViewGroup } from '../listView';
import React, { useCallback, useMemo, useState } from 'react';
import type { SelectDropdownIndicatorProps, SelectInputValueProps } from '../select';
import { createDateOptions, dateToString, parseDateOptions } from './utils';

import { DatePickerDropdownIndicator } from './components/datePickerDropdownIndicator';
import { DatePickerInputValue } from './components/datePickerInputValue';
import { DateSelect } from './components/dateSelect';
import type { FC } from 'react';
import type { ListViewComponentProps } from '../listView';
import { Select } from '../select';
import cx from 'classnames';
import { t as listViewT } from '../listView/i18n';
import { t } from './i18n';

export const sub = (date: Date, count: number): Date => {
    const day = new Date(date);
    day.setDate(day.getDate() - count);
    return day;
};

export const add = (date: Date, count: number): Date => {
    const day = new Date(date);
    day.setDate(day.getDate() + count);
    return day;
};

export const dateToStr = (date: Date): Date => date;

const INPUT_MIN_WIDTH = '260px';

export const DatePickerBase: FC<DatePickerBaseProps> = ({
    className,
    testId = CLASS_NAME,
    onChange,
    selectedValue,
    disabled,
    options = [],
    minWidth = INPUT_MIN_WIDTH,
    customDateRange,
    hideCalendarButton = false,
    placeholder,
    isClearable,
    onClear,
    isRange,
    showOptionLabel = false,
    controlId,
    compact = false,
    menuHeight,
    requireApply = isRange,
    quickSelectionsTitle,
    singleDateRange = false,
    portalElement,
    components,
    formatLabel,
}) => {
    const [open, setOpen] = useState(false);

    const placeholderText = placeholder || t(isRange ? 'select_period' : 'select_date');

    const dateOptions = useMemo(
        () => createDateOptions(options),
        // eslint-disable-next-line react-hooks/exhaustive-deps
        [options, customDateRange?.start, customDateRange?.end]
    );

    const selectedDateOption = useMemo(
        () => (selectedValue ? parseDateOptions(options, selectedValue) : undefined),
        [options, selectedValue]
    );

    const listHeight = useMemo(() => {
        // The calendar list needs a fixed height since it's using react-window.
        // We calculate the final height by subtracting the height of inputs/paddings
        // from the full menu height.
        let height = menuHeight;
        if (!height)
            height = isRange ? DEFAULT_DATE_RANGE_PICKER_HEIGHT : DEFAULT_DATE_PICKER_HEIGHT;
        // Header height = 96px
        // Footer height = 41px
        return height - 96 - (requireApply ? 41 : 0);
    }, [menuHeight, requireApply, isRange]);

    const disableApply = useCallback(
        (value?: DatePickerParsedOption) => {
            return isRange ? !value?.start || !value?.end : !value?.start;
        },
        [isRange]
    );

    const handleChange = useCallback(
        (option?: DatePickerParsedOption) => {
            if (!option) return onChange(undefined);

            const { start, end } = option;

            if (option.value !== CUSTOM_VALUE) setOpen(false);
            else {
                setOpen(isRange);
                if (!start && !end) return false;
            }

            return onChange({
                ...option,
                start: start ? dateToString(start) : undefined,
                end: end ? dateToString(end) : undefined,
            });
        },
        [isRange, onChange]
    );

    const handleSerializeValue = useCallback((item: DatePickerParsedOption) => {
        if (item.start && item.end) return `${dateToString(item.start)}:${dateToString(item.end)}`;
        if (item.start) return dateToString(item.start);
        return item.value;
    }, []);

    const handleMenuOpen = () => {
        setOpen(true);
    };

    const handleMenuClose = () => {
        setOpen(false);
    };

    const InputValue = useCallback(
        (props: SelectInputValueProps<DatePickerParsedOption>) => (
            <DatePickerInputValue
                {...props}
                isRange={isRange}
                showOptionLabel={showOptionLabel}
                placeholder={placeholderText}
                compact={compact}
                formatLabel={formatLabel}
            />
        ),
        [isRange, showOptionLabel, compact, placeholderText, formatLabel]
    );

    const DropdownIndicator = useCallback(
        (props: SelectDropdownIndicatorProps) => (
            <DatePickerDropdownIndicator {...props} hideCalendar={hideCalendarButton} />
        ),
        [hideCalendarButton]
    );

    const quickSelections: DatePickerParsedOption[] = useMemo(
        () => dateOptions.flatMap((o) => (isListViewGroup(o) ? o.options : [o])),
        [dateOptions]
    );

    const Aside = useCallback(
        ({
            quickSelections,
            quickSelectionsTitle,
            onSelect,
            selectedValue,
        }: ListViewComponentProps<DatePickerParsedOption, false>) => {
            if (!quickSelections || quickSelections.length === 0) return null;

            return (
                <ListViewAside>
                    <ListViewAsideSection
                        title={quickSelectionsTitle ?? listViewT('quickSelections')}
                        selectedValue={selectedValue}
                        groups={[
                            ...quickSelections.map((group) => ({
                                ...group,
                                options: [],
                                onClick: () => {
                                    onSelect(group, { replace: true });
                                },
                            })),
                        ]}
                        isGroupSelected={(group, selectedValue) =>
                            !Array.isArray(selectedValue) && group.label === selectedValue?.label
                        }
                    />
                </ListViewAside>
            );
        },
        []
    );

    const List = useCallback(
        (props: ListViewComponentProps<DatePickerParsedOption, false>) => {
            return (
                <DateSelect
                    onChange={props.onSelect}
                    dates={props.selectedValue}
                    limit={{
                        start: customDateRange?.start ?? '',
                        end: customDateRange?.end ?? '',
                    }}
                    isRange={isRange}
                    listHeight={listHeight}
                    singleDateRange={singleDateRange}
                    components={components}
                />
            );
        },
        [
            customDateRange?.start,
            customDateRange?.end,
            isRange,
            listHeight,
            singleDateRange,
            components,
        ]
    );

    return (
        <div className={cx(CLASS_NAME, className)} data-testid={testId}>
            <Select
                controlId={controlId}
                disabled={disabled}
                portalElement={portalElement}
                onChange={handleChange}
                minWidth={compact ? undefined : minWidth}
                menuHeight={menuHeight}
                menuMaxHeight="none"
                options={dateOptions}
                hideFilter
                onClose={handleMenuClose}
                onOpen={handleMenuOpen}
                selectedValue={selectedDateOption}
                requireApply={customDateRange ? requireApply : false}
                open={open}
                onSerializeValue={handleSerializeValue}
                hideClearButton={isClearable !== true}
                placeholder={placeholderText}
                quickSelections={customDateRange && quickSelections}
                quickSelectionsTitle={quickSelectionsTitle}
                components={{
                    DropdownIndicator,
                    InputValue,
                    ListContainer: customDateRange ? List : undefined,
                    Aside: customDateRange ? Aside : undefined,
                }}
                compact={compact}
                onClear={onClear}
                disableApply={!isClearable ? disableApply : undefined}
            />
        </div>
    );
};
