import React from 'react'
import { render, fireEvent, createEvent } from '../utils/test-utils'
import userEvent from '@testing-library/user-event'
import { GridEditorRadio } from './radio'
import type { CellEditorParams } from '../types'
import { GridEditorContext } from '../context/grid-editor-context/context'

jest.mock('../context/grid-context', () => ({
    useGridColumn: () => ({ data: { label: 'Is Default' } }),
}))

const baseProps = {
    rowId: '1',
    mode: 'editing' as CellEditorParams<any>['mode'],
    tabIndex: 0,
    label: '',
    value: false,
    columnId: 'isDefault',
}

describe('GridEditorRadio', () => {
    let onConfirm: jest.Mock
    let onCancel: jest.Mock

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

    function renderRadio(props: Partial<typeof baseProps> = {}) {
        return render(
            <GridEditorRadio
                {...baseProps}
                onCancel={onCancel}
                onConfirm={onConfirm}
                {...props}
            />
        )
    }

    it('should render a radio button', () => {
        const { getByRole } = renderRadio()
        expect(getByRole('radio')).toBeInTheDocument()
    })

    it('should reflect value as checked', () => {
        const { getByRole } = renderRadio({ value: true })
        expect(getByRole('radio')).toBeChecked()
    })

    it('should reflect value as unchecked', () => {
        const { getByRole } = renderRadio({ value: false })
        expect(getByRole('radio')).not.toBeChecked()
    })

    it('should call onConfirm(true) on click', async () => {
        const { getByRole } = renderRadio({ value: false })
        await userEvent.click(getByRole('radio'))
        expect(onConfirm).toHaveBeenCalledWith(true, {
            continueEditing: 'current',
        })
    })

    it('should call onConfirm with continueEditing:next on Tab', async () => {
        renderRadio()
        await userEvent.keyboard('{Tab}')
        expect(onConfirm).toHaveBeenCalledWith(false, {
            continueEditing: 'next',
        })
    })

    it('should call onConfirm with continueEditing:previous on Shift+Tab', async () => {
        renderRadio()
        await userEvent.keyboard('{Shift>}{Tab}{/Shift}')
        expect(onConfirm).toHaveBeenCalledWith(false, {
            continueEditing: 'previous',
        })
    })

    it('should call onCancel on Escape', async () => {
        renderRadio()
        await userEvent.keyboard('{Escape}')
        expect(onConfirm).not.toHaveBeenCalled()
        expect(onCancel).toHaveBeenCalled()
    })

    it('should not intercept Tab in navigation mode', async () => {
        renderRadio({ mode: 'navigation' })
        await userEvent.keyboard('{Tab}')
        expect(onConfirm).not.toHaveBeenCalled()
        expect(onCancel).not.toHaveBeenCalled()
    })

    it.each(['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'])(
        'should swallow %s key',
        (key) => {
            const { getByRole } = renderRadio()
            const radio = getByRole('radio')
            const event = createEvent.keyDown(radio, { key, bubbles: true })
            fireEvent(radio, event)
            expect(event.defaultPrevented).toBe(true)
        }
    )

    describe('disabled state', () => {
        function renderDisabled(value: boolean) {
            return render(
                <GridEditorContext.Provider value={{ editable: false }}>
                    <GridEditorRadio
                        {...baseProps}
                        value={value}
                        onCancel={onCancel}
                        onConfirm={onConfirm}
                    />
                </GridEditorContext.Provider>
            )
        }

        const getDisabledWrapper = (
            container: ReturnType<typeof renderDisabled>
        ) => container.getByRole('radio')

        it('should expose role=radio with aria-disabled on the wrapper', () => {
            const wrapper = getDisabledWrapper(renderDisabled(false))
            expect(wrapper).toBeInTheDocument()
            expect(wrapper).toHaveAttribute('aria-disabled', 'true')
        })

        it('should expose aria-checked on wrapper matching value', () => {
            const wrapper = getDisabledWrapper(renderDisabled(true))
            expect(wrapper).toHaveAttribute('aria-checked', 'true')
        })

        it('should expose aria-label on the wrapper', () => {
            const wrapper = getDisabledWrapper(renderDisabled(false))
            expect(wrapper).toHaveAttribute('aria-label', 'Is Default')
        })
    })
})
