import {MouseEvent, FocusEvent} from 'react'
import {InputWrapper} from '@befe/brick-comp-input'
import {SvgCalendar} from '@befe/brick-icon'
import {Icon} from '@befe/brick-comp-icon'
import {createRef, createStateOperator, safeInvoke} from '@befe/brick-utils'
import dayjs, {Dayjs} from 'dayjs'
import {debounce} from 'lodash-es';
import {syncDisplayedDatePair} from '../utils/sync-range-displayed-date-pair';
import {createFocusHandler} from './range-input-utils/create-focus-handler'
import {createRangeFieldUserInput} from './range-input-utils/create-range-field-input'

import {TypePickerSize, TypePickerStatus} from './prop-types'
import {InnerRangePicker} from './inner-range-picker'
import {RangeInputCloseableIcon} from './partial-input-closable-icon'
import {preventPickerBlurred} from './create-inner-date-picker';

export function createRangePickerUserInput(
    comp: InnerRangePicker
) {
    const [
        getState,
        setState,
    ] = createStateOperator(comp)

    const startValueProp = 'startValue'
    const endValueProp = 'endValue'

    const startUserInputTextProp = 'startUserInputText'
    const endUserInputTextProp = 'endUserInputText'

    const startDisplayedProp = 'startDisplayedDate'
    const endDisplayedProp = 'endDisplayedDate'

    const startInputRef = createRef<HTMLInputElement>()
    const endInputRef = createRef<HTMLInputElement>()

    const closeableIcon = new RangeInputCloseableIcon(comp, {
        hoverState: 'isHoverOverCalendar',
        isClearable: () => !!comp.props.clearable,
        isDisabled: () => !!comp.props.disabled,

        hasValue: () => {
            let hasValue = false

            if (comp.state.isStartUserInput) {
                hasValue = hasValue || !!comp.state.startUserInputText
            } else {
                hasValue = hasValue || !!comp.state.startValue
            }

            if (comp.state.isEndUserInput) {
                hasValue = hasValue || !!comp.state.endUserInputText
            } else {
                hasValue = hasValue || !!comp.state.endValue
            }

            return hasValue
        },

        renderNode: function renderNode() {
            return (
                <Icon
                    svg={SvgCalendar}
                    onClick={comp.handleCalendarIconClick}
                />
            )
        },

        isFocus: () => comp.state.isStartUserInput || comp.state.isEndUserInput,

        onClear: () => {
            comp.setState({
                open: false,
                startValue: null,
                endValue: null,
                startUserInputText: '',
                endUserInputText: '',
            })

            comp.triggerChange({
                startValue: null,
                endValue: null,
            })

            comp.setOpenStatus({
                open: false,
                isStartUserInput: false,
                isEndUserInput: false,
            })
        },
    })

    function setRangeValue(
        startValue: Dayjs | null,
        endValue: Dayjs | null,
        isChangingEndValue = false
    ) {
        let [
            startUserInputText,
            endUserInputText,
        ] = getState([
            startUserInputTextProp,
            endUserInputTextProp,
        ]) as [string, string]

        const today = dayjs();

        // start 日期在 disabled 范围内, 整体清空
        if (startValue && comp.props.getDisabledItem && comp.props.getDisabledItem(startValue, today)) {
            startValue = null;
            startUserInputText = '';
            endValue = null;
            endUserInputText = '';
        }

        // end 日期在 disabled 范围内, 仅清空 end 值
        if (endValue && comp.props.getDisabledItem && comp.props.getDisabledItem(endValue, today)) {
            endValue = null;
            endUserInputText = '';
        }

        // 如果 end 日期早于 start 日期, 清空 end 值
        if (startValue && endValue && endValue.isBefore(startValue)) {
            endValue = null;
            endUserInputText = '';
        }

        const {
            startDisplayedDate,
            endDisplayedDate,
        } = syncDisplayedDatePair(
            comp.props.mode,
            startValue,
            endValue
        );

        const nextRangeState = {
            [startValueProp]: startValue,
            [startUserInputTextProp]: startUserInputText as (string | undefined),
            [endValueProp]: endValue,
            [endUserInputTextProp]: endUserInputText as (string | undefined),
            [startDisplayedProp]: startDisplayedDate,
            [endDisplayedProp]: endDisplayedDate,
            isSelecting: false,
        };

        if (isChangingEndValue) {
            delete nextRangeState[endUserInputTextProp];
        }
        else {
            delete nextRangeState[startUserInputTextProp];
        }

        setState(nextRangeState)

        comp.triggerChange({startValue, endValue})
    }

    const handleBlurOnInput = debounce((e: FocusEvent) => {
        if (
            startInputRef.elem !== document.activeElement
            && endInputRef.elem !== document.activeElement
        ) {
            safeInvoke(comp.props.onBlur, e);
        }
    }, 0)

    const context = {
        startFocusHandler: createFocusHandler(
            comp,
            'startFocus'
        ),

        endFocusHandler: createFocusHandler(
            comp,
            'endFocus'
        ),

        inputStart: createRangeFieldUserInput(comp, {
            isInputProp: 'isStartUserInput',
            inputValueProp: startUserInputTextProp,
            valueProp: startValueProp,
            displayedProp: startDisplayedProp,

            onCustomChange: (startValue: Dayjs | null) => {
                const endValue = getState(endValueProp) as Dayjs
                setRangeValue(startValue, endValue);
            },

            onBlur: e => {
                handleBlurOnInput(e);
            },
        }),

        inputEnd: createRangeFieldUserInput(comp, {
            isInputProp: 'isEndUserInput',

            inputValueProp: endUserInputTextProp,
            valueProp: endValueProp,
            displayedProp: endDisplayedProp,

            onCustomChange: (endValue: Dayjs | null) => {
                const startValue = getState(startValueProp) as Dayjs
                setRangeValue(startValue, endValue, true);
            },

            onBlur: e => {
                handleBlurOnInput(e);
            },
        }),

        refInput: createRef<HTMLDivElement>(),
    }


    function isFocus() {
        const {startFocus, endFocus} = comp.state
        return startFocus || endFocus
    }

    function init() {
        context.startFocusHandler.init({
            onFocus: context.inputStart.inputStart,
            onBlur: context.inputStart.inputEnd,
        })

        context.endFocusHandler.init({
            onFocus: context.inputEnd.inputStart,
            onBlur: context.inputEnd.inputEnd,
        })
    }

    init()

    const iconHooks = closeableIcon.hooks

    const handleMouseEnter = (e: MouseEvent) => {
        safeInvoke(comp.props.onMouseEnter, e);
        iconHooks.mouseEnter();
    }

    const handleMouseLeave = (e: MouseEvent) => {
        safeInvoke(comp.props.onMouseLeave, e);
        iconHooks.mouseLeave();
    }

    const handleStartMouseDown = () => {
        if (startInputRef.elem === document.activeElement) {
            comp.setState({
                isStartUserInput: true,
            })
        }
    }

    const handleEndMouseDown = () => {
        if (endInputRef.elem === document.activeElement) {
            comp.setState({
                isEndUserInput: true,
            })
        }
    }

    return {
        renderInput(opts: {
            status?: TypePickerStatus
            disabled?: boolean
            size?: TypePickerSize
        }) {
            const disabled = opts.disabled
            const {
                autoFocus,
            } = comp.props;

            return (
                <InputWrapper
                    suffix={closeableIcon.render()}
                    focus={isFocus()}
                    status={opts.status}
                    disabled={disabled}
                    refWrapper={context.refInput.ref}
                    onMouseEnter={handleMouseEnter}
                    onMouseLeave={handleMouseLeave}
                    size={opts.size}
                >
                    <input
                        type={'text'}
                        ref={startInputRef.ref}
                        disabled={disabled}
                        placeholder={comp.props.startPlaceholder}
                        value={context.inputStart.getValue() as string}
                        onChange={context.inputStart.handleChange}
                        onMouseDown={handleStartMouseDown}
                        onKeyDown={context.inputStart.handleKeyDown}
                        onFocus={context.startFocusHandler.handleFocus}
                        onBlur={context.startFocusHandler.handleBlur}
                        autoFocus={autoFocus}
                    />
                    <span
                        className={'brick-sep'}
                        onMouseDown={preventPickerBlurred}
                        onClick={comp.handleCalendarIconClick}
                    >～
                    </span>
                    <input
                        type={'text'}
                        ref={endInputRef.ref}
                        disabled={disabled}
                        placeholder={comp.props.endPlaceholder}
                        value={context.inputEnd.getValue() as string}
                        onChange={context.inputEnd.handleChange}
                        onMouseDown={handleEndMouseDown}
                        onKeyDown={context.inputEnd.handleKeyDown}
                        onFocus={context.endFocusHandler.handleFocus}
                        onBlur={context.endFocusHandler.handleBlur}
                    />
                </InputWrapper>
            )
        },
        get inputWrapperElem(): HTMLDivElement | null {
            return context.refInput.elem
        },
        get startInputElem(): HTMLInputElement | null {
            return startInputRef.elem
        },
        get endInputElem(): HTMLInputElement | null {
            return endInputRef.elem
        },

        formatStartInputValue: context.inputStart.formatInputValue,
        formatEndInputValue: context.inputEnd.formatInputValue,
    }
}
