import type { FC, ReactNode } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import { ListViewOptionLabel, MultiSelect } from '@theorchard/suite-components';
import { orderBy } from 'lodash-es';
import {
    CustomAside,
    CustomFooter,
    CustomHeader,
    CustomList,
} from './components';
import './styles.scss';
import type { ListViewItem } from '@theorchard/suite-components';

export interface StatementPeriodItem {
    statementPeriodId: string;
    statementPeriodName: string;
}

export enum ListMode {
    SINGLE = 'single',
    RANGE = 'range',
}

export enum FieldName {
    START = 'start',
    END = 'end',
}

export enum RangeType {
    LATEST = 'latest',
    LAST_12 = 'last12',
    LAST_CALENDAR_YEAR = 'lastCalendarYear',
    THIS_CALENDAR_YEAR = 'thisCalendarYear',
    CUSTOM = 'custom',
}

export type PeriodOption = ListViewItem & { data?: StatementPeriodItem };

export const CLASS_NAME = 'StatementPeriodRangeSwitcher';

export const PLACEHOLDER_START: PeriodOption = {
    label: 'start',
    value: 'start',
};
export const PLACEHOLDER_END: PeriodOption = { label: 'end', value: 'end' };

const RANGE_TYPE_LABEL: Record<RangeType, string> = {
    [RangeType.LATEST]: 'Latest Statement Period',
    [RangeType.LAST_12]: 'Last 12 Statement Periods',
    [RangeType.LAST_CALENDAR_YEAR]: 'Last Calendar Year',
    [RangeType.THIS_CALENDAR_YEAR]: 'This Calendar Year',
    [RangeType.CUSTOM]: '',
};

const getOption = (el: StatementPeriodItem): PeriodOption => ({
    label: el.statementPeriodName,
    value: el.statementPeriodId,
    data: el,
});

const CompactSelectValue: FC<{
    label: string;
    value?: ReactNode;
    subtext?: string;
}> = ({ label, value, subtext }) => (
    <div className="CompactSelectValue">
        <span className="CompactSelectValue-label">{label}</span>
        <span>{value}</span>
        {subtext && (
            <span className="CompactSelectValue-subtext">{subtext}</span>
        )}
    </div>
);

interface Props {
    periods: StatementPeriodItem[];
    // Committed range, by statement period name (matches the transfer-list filter).
    fromValue: string;
    toValue: string;
    onChange: (from: string, to: string) => void;
}

const StatementPeriodRangeSwitcher: FC<Props> = ({
    periods,
    fromValue,
    toValue,
    onChange,
}) => {
    const statementPeriodOptions = useMemo(
        () => orderBy(periods.map(getOption), option => +option.value, 'desc'),
        [periods]
    );

    const optionByName = (name: string) =>
        statementPeriodOptions.find(option => option.label === name);

    const [listMode, setListMode] = useState<ListMode>(ListMode.RANGE);
    const [focusedField, setFocusedField] = useState<FieldName>(
        FieldName.START
    );

    // Draft selection in the open dropdown (committed on close).
    const [selectedValueInDropdown, setSelectedValueInDropdown] = useState<
        PeriodOption[]
    >([PLACEHOLDER_START, PLACEHOLDER_END]);

    // Keep the draft in sync with the committed (props) value.
    useEffect(() => {
        const start =
            (fromValue && optionByName(fromValue)) || PLACEHOLDER_START;
        const end = (toValue && optionByName(toValue)) || PLACEHOLDER_END;
        setSelectedValueInDropdown([start, end]);
        setListMode(
            fromValue && fromValue === toValue
                ? ListMode.SINGLE
                : ListMode.RANGE
        );
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [fromValue, toValue, statementPeriodOptions]);

    const quickSelections = useMemo(() => {
        const last12 = statementPeriodOptions.slice(0, 12);
        const currentYear = `${new Date().getFullYear()}`;
        const lastYear = `${new Date().getFullYear() - 1}`;
        const thisCalendarYear = statementPeriodOptions.filter(option =>
            String(option.label).includes(currentYear)
        );
        const lastCalendarYear = statementPeriodOptions.filter(option =>
            String(option.label).includes(lastYear)
        );

        const groups = [
            {
                label: RANGE_TYPE_LABEL[RangeType.LATEST],
                key: RangeType.LATEST,
                options: [statementPeriodOptions[0], statementPeriodOptions[0]],
                range: false,
                hidden: !statementPeriodOptions.length,
            },
            {
                label: RANGE_TYPE_LABEL[RangeType.LAST_12],
                key: RangeType.LAST_12,
                options: [last12[last12.length - 1], last12[0]],
                range: true,
                hidden: !last12.length,
            },
            {
                label: RANGE_TYPE_LABEL[RangeType.LAST_CALENDAR_YEAR],
                key: RangeType.LAST_CALENDAR_YEAR,
                options: lastCalendarYear.length
                    ? [
                          lastCalendarYear[lastCalendarYear.length - 1],
                          lastCalendarYear[0],
                      ]
                    : [],
                range: true,
                hidden: !lastCalendarYear.length,
            },
            {
                label: RANGE_TYPE_LABEL[RangeType.THIS_CALENDAR_YEAR],
                key: RangeType.THIS_CALENDAR_YEAR,
                options: thisCalendarYear.length
                    ? [
                          thisCalendarYear[thisCalendarYear.length - 1],
                          thisCalendarYear[0],
                      ]
                    : [],
                range: true,
                hidden: !thisCalendarYear.length,
            },
        ];

        return groups.filter(group => !group.hidden);
    }, [statementPeriodOptions]);

    const handleClose = () => {
        const [start, end] = selectedValueInDropdown;
        if (!start || !end) return;

        const startReal = start.value !== PLACEHOLDER_START.value;
        const endReal = end.value !== PLACEHOLDER_END.value;

        if (!startReal && !endReal) {
            if (fromValue || toValue) onChange('', '');
            return;
        }
        if (!startReal || !endReal) return;

        onChange(String(start.label), String(end.label));
    };

    const subtext = useMemo(() => {
        const match = quickSelections.find(
            group =>
                group.options[0]?.label === fromValue &&
                group.options[1]?.label === toValue
        );
        return match ? RANGE_TYPE_LABEL[match.key] : undefined;
    }, [quickSelections, fromValue, toValue]);

    const committedValue = fromValue
        ? toValue && toValue !== fromValue
            ? `${fromValue} - ${toValue}`
            : fromValue
        : undefined;

    return (
        <MultiSelect
            className={CLASS_NAME}
            options={statementPeriodOptions}
            variant="compact"
            menuMinHeight={400}
            menuWidth={790}
            testId={CLASS_NAME}
            hideClearButton
            width="max-content"
            onClose={handleClose}
            onSelect={options =>
                setSelectedValueInDropdown([options[0], options[1]])
            }
            selectedValue={[
                selectedValueInDropdown[0].value,
                selectedValueInDropdown[1].value,
            ]}
            placeholder="Statement Period"
            components={{
                List: props => (
                    <CustomList
                        {...props}
                        mode={listMode}
                        focusedField={focusedField}
                        setFocusedField={setFocusedField}
                        selectedValueInDropdown={selectedValueInDropdown}
                    />
                ),
                OptionLabel: ({ option }) => (
                    <ListViewOptionLabel>{option.label}</ListViewOptionLabel>
                ),
                InputValue: () => (
                    <CompactSelectValue
                        label="Statement Period"
                        value={committedValue}
                        subtext={subtext}
                    />
                ),
                Header: props => (
                    <CustomHeader
                        {...props}
                        mode={listMode}
                        onModeChange={setListMode}
                        focusedField={focusedField}
                        setFocusedField={setFocusedField}
                    />
                ),
                Aside: props => (
                    <CustomAside
                        {...props}
                        mode={listMode}
                        onModeChange={setListMode}
                        selections={quickSelections}
                    />
                ),
                Footer: props => <CustomFooter {...props} mode={listMode} />,
            }}
        />
    );
};

export default StatementPeriodRangeSwitcher;
