import * as React from 'react'
import type {
    CellEditorParams,
    EditorConfirmPayload,
    GridRowData,
} from '../../types'
import { Portal } from '@planview/pv-uikit'
import type {
    ComboboxCloseReason,
    ComboboxOption,
    ComboboxProps,
} from '@planview/pv-uikit'
import {
    useFloating,
    autoUpdate,
    hide,
    offset,
    shift,
} from '@floating-ui/react-dom'
import { useGridColumn, useGridSelector } from '../../context/grid-context'
import { useGrid } from '../../hooks'
import { useMergeRefs, isTestEnvironment } from '@planview/pv-utilities'
import { cleanPropForSpread } from './utils'
import {
    AnchoredContainer,
    AnchoredContainerPortal,
    StyledCombobox,
} from './styles'

export type GridEditorComboboxMultiProps<TDataModel extends GridRowData> = Omit<
    CellEditorParams<TDataModel>,
    'value' | 'onConfirm'
> &
    Pick<
        ComboboxProps,
        | 'className'
        | 'clearable'
        | 'creatable'
        | 'disabled'
        | 'error'
        | 'icon'
        | 'inputMode'
        | 'inputProps'
        | 'label'
        | 'loading'
        | 'onRemove'
        | 'onCreate'
        | 'onInputChange'
        | 'onSelect'
        | 'options'
        | 'placeholder'
        | 'virtualized'
    > & {
        /** value */
        value?: ComboboxOption[]
        /**
         * confirm callback
         */
        onConfirm: (
            value?: ComboboxOption[],
            payload?: EditorConfirmPayload
        ) => void
    }

const AnchoredPortal = React.forwardRef<
    HTMLDivElement,
    { children: React.ReactNode; minWidth: number; onHidden: () => void }
>(function AnchoredPortal({ children, minWidth, onHidden }, containerRef) {
    const {
        refs: { setFloating, setReference },
        x,
        y,
        strategy,
        middlewareData,
    } = useFloating({
        placement: 'left-start',
        middleware: [
            offset(({ rects }) => ({
                mainAxis: -rects.floating.width,
            })),
            shift({ crossAxis: true, padding: 8 }),
            isTestEnvironment ? null : hide(),
        ],
        whileElementsMounted: autoUpdate,
    })

    const floatingRef = useMergeRefs([containerRef, setFloating])
    const hidden = !!middlewareData.hide?.referenceHidden

    React.useEffect(() => {
        if (hidden) {
            onHidden()
        }
    }, [hidden, onHidden])

    return (
        <AnchoredContainer ref={setReference}>
            <Portal type="pvds-grid-editor">
                <AnchoredContainerPortal
                    ref={floatingRef}
                    style={{
                        position: strategy,
                        top: y,
                        left: x,
                        minWidth: Math.max(minWidth, 200),
                        visibility: hidden ? 'hidden' : undefined,
                    }}
                >
                    {children}
                </AnchoredContainerPortal>
            </Portal>
        </AnchoredContainer>
    )
})

export const GridEditorComboboxMulti = <TDataModel extends GridRowData>({
    value,
    onCancel,
    onConfirm,
    options,
    label = '',
    ...rest
}: GridEditorComboboxMultiProps<TDataModel>) => {
    const wrapperRef = React.useRef<HTMLDivElement | null>(null)
    const valueRef = React.useRef(value)
    const column = useGridColumn(rest.columnId)

    React.useEffect(() => {
        valueRef.current = value
    }, [value])

    const handleChange = React.useCallback(
        (opt: ComboboxOption[]) => {
            valueRef.current = opt
            onConfirm(opt, { continueEditing: 'current' })
        },
        [onConfirm]
    )

    const handleHidden = React.useCallback(() => {
        onCancel()
    }, [onCancel])

    const handleInputKeyDown = React.useCallback(
        (ev: KeyboardEvent) => {
            if (ev.key === 'Tab') {
                ev.stopPropagation()
                ev.preventDefault()
                onConfirm(valueRef.current, {
                    continueEditing: ev.shiftKey ? 'previous' : 'next',
                })
            }
        },
        [onConfirm]
    )

    const handleClose = React.useCallback(
        (reason: ComboboxCloseReason) => {
            if (reason === 'confirm_click') {
                onConfirm(valueRef.current)
            } else if (!reason.includes('tab')) {
                onCancel()
            }
        },
        [onCancel, onConfirm]
    )

    React.useEffect(() => {
        const el = wrapperRef.current
        if (el) {
            el?.addEventListener('keydown', handleInputKeyDown)
            return () => {
                el?.removeEventListener('keydown', handleInputKeyDown)
            }
        }
    }, [handleInputKeyDown])

    React.useEffect(() => {
        setTimeout(() => {
            const el = wrapperRef.current
            if (el) {
                el.querySelector('input')?.focus()
            }
        }, 100)
    }, [])

    const { selectors } = useGrid()
    const height = useGridSelector(selectors.selectRowHeight)

    return (
        <AnchoredPortal
            ref={wrapperRef}
            minWidth={column?.actualWidth ?? 0}
            onHidden={handleHidden}
        >
            <StyledCombobox
                {...cleanPropForSpread(rest)}
                $height={height}
                multiSelectable={true}
                label={label}
                options={options}
                defaultOpen
                onClose={handleClose}
                onChange={handleChange}
                value={value}
            />
        </AnchoredPortal>
    )
}
