import React from 'react'
import {
    useClickOutside,
    text,
    spacingPx,
    states,
    useEscapeListener,
} from '@planview/pv-utilities'
import { Switch } from '@planview/pv-uikit'
import styled from 'styled-components'
import type { CellEditorParams, GridRowData } from '../types'
import { useGridColumn } from '../context/grid-context'
import { useGridEditor } from '../context/grid-editor-context'

const StyledSwitch = styled(Switch)`
    * {
        outline: none;
    }
`

const CCell = styled.div<{
    $focusVisible: boolean
}>`
    ${text.regular};
    height: 100%;
    width: 100%;
    padding: 0 ${spacingPx.small};
    display: flex;
    flex-direction: row;
    align-items: center;
    min-width: 0;
    overflow: hidden;
    outline: none;
    ${(props) => states.focus({ focusVisible: props.$focusVisible })}
`

export const GridEditorSwitch = <TDataModel extends GridRowData>({
    columnId,
    tabIndex,
    value,
    mode,
    onCancel,
    onConfirm,
}: CellEditorParams<TDataModel>) => {
    const ref = React.useRef<HTMLInputElement>(null)

    const column = useGridColumn(columnId)
    const columnLabel = column?.data?.label

    const { editable } = useGridEditor()

    const handleOnClickOutside = React.useCallback(() => {
        if (mode === 'editing') {
            onCancel()
        }
    }, [onCancel, mode])

    useClickOutside(ref, handleOnClickOutside)

    React.useEffect(() => {
        if (mode === 'editing') {
            ref.current?.focus()
        }
    }, [mode])

    const handleClick = React.useCallback<React.MouseEventHandler>(
        (e) => e.stopPropagation(),
        []
    )

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

    useEscapeListener({
        onEscape: handleEscape,
        enabled: mode === 'editing',
        stack: true,
    })

    const onKeyDown = React.useCallback<React.KeyboardEventHandler>(
        (e) => {
            if (mode === 'navigation') {
                return
            }

            if (e.key === 'Tab') {
                e.stopPropagation()
                e.preventDefault()
                onConfirm(value, {
                    continueEditing: e.shiftKey ? 'previous' : 'next',
                })
            }
        },
        [onConfirm, mode, value]
    )
    const handleChange = React.useCallback(
        (val: boolean) => {
            onConfirm(val, {
                continueEditing: 'current',
            })
        },
        [onConfirm]
    )

    /**
     * These attributes are added in case the Switch is disabled in which
     * case the wrapper takes over the focus and adds descriptive attributes for
     * assistive technology.
     */
    const disabledProps = React.useMemo(
        () =>
            !editable
                ? {
                      role: 'checkbox',
                      tabIndex,
                      'aria-disabled': true,
                      'aria-checked': !!value,
                      'aria-label': columnLabel,
                  }
                : {},
        [columnLabel, editable, tabIndex, value]
    )

    return (
        <CCell {...disabledProps} $focusVisible={mode === 'editing'}>
            <StyledSwitch
                aria-hidden={!editable ? true : undefined}
                disabled={!editable}
                ref={ref}
                tabIndex={tabIndex}
                checked={!!value}
                onClick={handleClick}
                onKeyDown={onKeyDown}
                onChange={handleChange}
                aria-label={columnLabel}
            />
        </CCell>
    )
}
