import React from 'react'
import { render } from '../../utils/test-utils'
import type { GridEditorComboboxMultiProps } from './multi'
import { GridEditorComboboxMulti } from './multi'
import type { ComboboxOption } from '@planview/pv-uikit'
import type { UseGridReturn } from '../../hooks'
import { createGrid } from '../../hooks'
import { GridProvider } from '../../context/grid-context'
import userEvent from '@testing-library/user-event'

const options: ComboboxOption[] = [
    {
        value: 1,
        label: 'One',
    },
    {
        value: 2,
        label: 'Two',
    },
    {
        value: 3,
        label: 'Three',
    },
    {
        value: 4,
        label: 'Four',
    },
]

const baseProps = {
    rowId: '1',
    mode: 'editing' as GridEditorComboboxMultiProps<any>['mode'],
    tabIndex: 0,
    label: '',
    value: [{ value: 1, label: 'One' }],
    columnId: 'name',
}

describe('GridEditorComboboxMulti', () => {
    let onConfirm: jest.Mock
    let onCancel: jest.Mock
    let grid: UseGridReturn

    beforeEach(() => {
        onConfirm = jest.fn()
        onCancel = jest.fn()
        grid = createGrid()
    })

    function renderEditor(
        props: Partial<GridEditorComboboxMultiProps<any>> = {}
    ) {
        return render(
            <GridProvider {...grid}>
                <GridEditorComboboxMulti
                    {...baseProps}
                    onCancel={onCancel}
                    onConfirm={onConfirm}
                    options={options}
                    {...props}
                />
            </GridProvider>
        )
    }

    it('should render', async () => {
        const { findByRole } = renderEditor()
        expect(await findByRole('combobox')).toBeInTheDocument()
    })

    it('should support editing (continue with Tab)', async () => {
        renderEditor()

        await userEvent.keyboard('{Tab}2{Tab}')

        expect(onConfirm).toHaveBeenCalledWith([{ value: 1, label: 'One' }], {
            continueEditing: 'next',
        })
    })

    it('should support editing (continue with Shift+Tab)', async () => {
        renderEditor()

        await userEvent.keyboard('{Tab}2{Shift>}{Tab}{/Shift}')

        expect(onConfirm).toHaveBeenCalledWith([{ value: 1, label: 'One' }], {
            continueEditing: 'previous',
        })
    })

    it('should support editing (selection of item)', async () => {
        const { findByRole } = renderEditor()

        await userEvent.keyboard('{Tab}')

        await userEvent.click(await findByRole('option', { name: 'Two' }))

        expect(onConfirm).toHaveBeenCalledWith(
            [
                { value: 1, label: 'One' },
                { value: 2, label: 'Two' },
            ],
            {
                continueEditing: 'current',
            }
        )
    })

    it('should support cancelling an edit with Esc', async () => {
        renderEditor()

        await userEvent.keyboard('{Tab}2{Escape}')

        expect(onConfirm).not.toHaveBeenCalled()
        expect(onCancel).toHaveBeenCalled()
    })

    it('should not reselect text if value changes on re-render', async () => {
        const TestComponent = () => {
            const [_search, setSearch] = React.useState('')

            return (
                <GridProvider {...grid}>
                    <GridEditorComboboxMulti
                        {...baseProps}
                        onCancel={onCancel}
                        onConfirm={onConfirm}
                        options={options}
                        onInputChange={setSearch}
                        value={[]}
                        label="Text combobox"
                    />
                </GridProvider>
            )
        }

        const { findByRole } = render(<TestComponent />)

        await userEvent.keyboard('{Tab}Testing')

        // The bug caused only the final `g` to be present in the input
        expect(
            await findByRole('combobox', { name: 'Text combobox' })
        ).toHaveValue('Testing')
    })
})
