import {ChangeEvent, FocusEvent, KeyboardEvent, MouseEvent} from 'react'
import {Input} from '@befe/brick-comp-input'
import {createRef, safeInvoke} from '@befe/brick-utils'
import {Dayjs} from 'dayjs'
import {throughDisabledCheck} from '../utils/through-disabled-check'
import {createInnerDatePicker, Type_InnerDatePicker} from './create-inner-date-picker'
import {handleUserInputChange} from './handle-user-input-change'
import {SingleInputCloseableIcon} from './partial-input-closable-icon'
import {CalendarIcon} from './partial-calender-icon'

export function createDatePickerUserInput(
    comp: InstanceType<ReturnType<typeof createInnerDatePicker>> & Type_InnerDatePicker,
    defaultIconOpts: CalendarIcon['opts']
) {
    const inputRef = createRef<Input>()
    const calendarIcon = new CalendarIcon(defaultIconOpts)

    function formatInputValue(value: Dayjs | null) {
        return value ? value.format(comp.computedItemFormat) : ''
    }

    function getDisplayedValue() {
        if (comp.state.isUserInput) {
            return comp.state.userInputText
        }

        const value = comp.state.value
        return formatInputValue(value)
    }


    function renderCalendarIcon() {
        return calendarIcon.render({
            iconSvg: comp.props.iconSvg,
            iconNode: comp.props.iconNode,
            onClick: comp.handleCalendarIconClick,
        })
    }

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

        hasValue: () => !!getDisplayedValue(),

        // eslint-disable-next-line @typescript-eslint/no-use-before-define
        renderNode: renderCalendarIcon,

        isFocus: () => comp.state.isUserInput,

        onClear: () => {
            const showTime = Boolean(comp.props.showTime)

            comp.setState({
                value: null,
                isUserInput: showTime,
                open: false,
                userInputText: '',
                timeState: {
                    hour: undefined,
                    minute: undefined,
                    second: undefined,
                },
            })
            comp.setOpen(showTime, null)
            comp.changeValue(null)

            comp.processOpenStatus({
                open: showTime,
                isUserInput: showTime,
            })
        },
    })

    function renderSuffix() {
        return closeableIcon.render()
    }

    const startUserInput = (e: FocusEvent) => {
        const onFocus = comp.props.onFocus
        onFocus && onFocus(e)

        comp.setState({
            isUserInput: true,
            isZooming: false,
            userInputText: getDisplayedValue(),
        })
        comp.setOpen(true)

        comp.processOpenStatus({
            open: true,
            isUserInput: true,
        })

        inputRef.elem!.elemInput.select()
    }

    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    const completeUserInput = (e: FocusEvent) => {
        comp.setState({
            open: false,
            isUserInput: false,
        })

        comp.processOpenStatus({
            open: false,
            isUserInput: false,
        })

        const onBlur = comp.props.onBlur
        onBlur && onBlur(e)
    }

    const handleChange = (e: ChangeEvent) => {
        /**
         * @note: 有任何的更改, 需要确保 isUserInput 被打开, 因为可能被用户用 `Enter` 关闭面板了
         */
        comp.setState({
            isUserInput: true,
        });
        handleUserInputChange({
            e,
            comp,

            itemFormatProp: 'computedItemFormat',
            valueProp: 'value',
            displayedProp: 'displayedDate',
            inputTextProp: 'userInputText',
            isInputProp: 'isUserInput',

            onCustomChange: (value, displayedDate) => {
                const getDisabledItem = comp.props.getDisabledItem

                value = throughDisabledCheck(value, getDisabledItem)

                comp.setState({
                    value,
                    displayedDate,
                })
                comp.changeValue(value, {
                    onStateChanged: () => {
                        comp.tryScrollToSelectedTime()
                    },
                })
            },
        })
    }

    const handleKeyDown = (e: KeyboardEvent) => {
        const code = e.nativeEvent.code;
        if (code === 'Tab' || code === 'Enter') {
            comp.setState({
                open: false,
                isUserInput: false,
            })
            comp.processOpenStatus({
                open: false,
                isUserInput: false,
            })
        }
    }

    const iconHooks = closeableIcon.hooks

    const handleMouseDown = () => {
        if (inputRef.elem?.elemInput === document.activeElement) {
            comp.setState({
                isUserInput: true,
            })
        }
    }

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

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

    return {
        renderInput() {
            const size = comp.size;

            const {
                placeholder,
                status,
                disabled,
                autoFocus,
            } = comp.props

            return (
                <Input
                    ref={inputRef.ref}
                    value={getDisplayedValue()}
                    disabled={disabled}
                    status={status}
                    suffix={renderSuffix()}
                    placeholder={placeholder}
                    size={size}
                    onFocus={startUserInput}
                    onBlur={completeUserInput}
                    onChange={handleChange}
                    onKeyDown={handleKeyDown}
                    onMouseDown={handleMouseDown}
                    onMouseEnter={handleMouseEnter}
                    onMouseLeave={handleMouseLeave}
                    autoFocus={autoFocus}
                />
            )
        },

        formatInputValue,

        get inputElem() {
            return inputRef.elem ? inputRef.elem.elemInput : undefined
        },
    }
}
