import React from 'react'
import { useGridComponents } from '../context/grid-components-context/hook'
import { useColumnResize } from '../hooks/use-column-resize'
import { useGridContext, useGridSelector } from '../context/grid-context/hook'
import { SELECTION_COLUMN_ID } from '../constants'
import { useColumnIndex, useFocusHandler } from '../hooks'
import { useSubFocusHandler } from '../context/grid-sub-navigation-context'
import { size, useIsFocusVisible } from '@planview/pv-utilities'
import { useDraggable } from '@dnd-kit/core'
import { DRAG_TYPE_HEADER_CELL } from './grid-drag-controller'
import { useColumnBorders } from '../hooks/use-column-borders'

export type GridHeaderCellProps = {
    columnId: string
    tabIndex?: number
    placeholder?: boolean
    lastPlaceholder?: boolean
}

const stopPropagation: React.MouseEventHandler = (e): void => {
    e.stopPropagation()
}

export const GridHeaderCell = ({
    columnId,
    placeholder = false,
    lastPlaceholder = false,
}: GridHeaderCellProps) => {
    const {
        GridHeaderCellLayout,
        GridCellFocusLayout,
        GridHeaderCellExpand,
        GridColumnMoveHandle,
        GridColumnResizeHandle,
    } = useGridComponents()

    const { selectors, api, getState } = useGridContext()

    const sort = useGridSelector((state) =>
        selectors.selectColumnSort(state, columnId)
    )

    const {
        actualWidth,
        data: column,
        parentId,
    } = useGridSelector((state) => selectors.selectColumn(state, columnId))

    const isTreeColumn = !!column.tree

    const focusProps = useIsFocusVisible()

    const { levels, groups } = useGridSelector((state) =>
        selectors.selectHeaderHierarchy(state)
    )

    const rowId = useGridSelector((state) => {
        const ids = selectors.selectHeaderRowIds(state)
        return ids[ids.length - 1]
    })

    const {
        onKeyDown,
        rendererWantsToDisplayFocusStyles,
        registeredIndexes,
        subFocus,
    } = useSubFocusHandler(columnId, 'header', rowId, isTreeColumn ? 1 : 0)

    const CellHeaderComponent = column.header?.Renderer
    const [hovering, setHovering] = React.useState(false)

    const columnIndex = useColumnIndex(columnId)
    const [hasFocus, handleFocus] = useFocusHandler(columnId, 'header', rowId)

    const isCellEditing = useGridSelector((state) =>
        selectors.selectIsEditingCellInColumn(state, columnId)
    )

    const { showLeftBorder, showRightBorder } = useColumnBorders(
        columnId,
        'header'
    )

    const parent = groups.find((g) => g.id === parentId)

    const onEnter = React.useCallback(() => setHovering(true), [])
    const onLeave = React.useCallback(() => setHovering(false), [])

    const [resizing, resizeHandlers] = useColumnResize(column.id, actualWidth)

    const onSort = React.useCallback(
        (replaceSort: boolean) => {
            api.column.toggleSort(columnId, replaceSort)
        },
        [api.column, columnId]
    )

    const handleClick = React.useCallback<React.MouseEventHandler>(
        (e) => {
            const modifierPressed = e.ctrlKey || e.metaKey || e.shiftKey
            onSort(!modifierPressed)
        },
        [onSort]
    )

    const handleKeyDown = React.useCallback<
        React.KeyboardEventHandler<HTMLElement>
    >(
        (e) => {
            onKeyDown(e)
            if (e.key === ' ' || e.key === 'Enter') {
                e.preventDefault()
                if (
                    e.key === 'Enter' &&
                    e.shiftKey &&
                    selectors.selectColumnMenuEnabled(getState())
                ) {
                    const el = e.currentTarget
                    const { top, left } = el.getBoundingClientRect()
                    api.column.showMenu({
                        top: top + size.xsmall,
                        left: left + size.xsmall,
                    })
                } else {
                    const modifierPressed = e.ctrlKey || e.metaKey || e.shiftKey
                    onSort(!modifierPressed)
                }
            }
        },
        [onKeyDown, getState, selectors, api.column, onSort]
    )

    const parentOffsetHeight = parent
        ? (parent.level - 1) * size.small
        : levels * size.small

    const height = size.small - 1 + (levels ? parentOffsetHeight : 0)

    const {
        attributes,
        listeners,
        setNodeRef,
        setActivatorNodeRef,
        isDragging,
    } = useDraggable({
        id: columnId,
        data: {
            type: DRAG_TYPE_HEADER_CELL,
            columnsDragged: [column.id],
            column,
            height,
            offsetY: parentOffsetHeight,
        },
        disabled: !column.movable,
    })

    React.useEffect(() => {
        if (isDragging) {
            setHovering(false)
        }
    }, [isDragging])

    const onPointerDown = listeners?.onPointerDown

    const handlePointerDown = React.useCallback<
        React.PointerEventHandler<HTMLElement>
    >(
        (e) => {
            handleFocus()
            onPointerDown?.(e)
        },
        [handleFocus, onPointerDown]
    )

    const functionalTabIndex =
        isTreeColumn && hasFocus && (subFocus === 'first' || subFocus === 0)
            ? 0
            : -1

    /** This means the renderer OR child of renderer has focus */
    const rendererHasFocus = hasFocus && functionalTabIndex === -1
    /** This is the tabIndex passed to the renderer, we dot use it if sub-navigation is enabled */
    const rendererTabIndex = rendererHasFocus && !registeredIndexes ? 0 : -1

    const sticky =
        column.sticky === 'left' || column.sticky === 'right'
            ? column.sticky
            : null

    const stickyOffset = useGridSelector((state) =>
        selectors.selectColumnStickyOffset(state, columnId)
    )

    const extraStyle: Partial<React.CSSProperties> | null = sticky
        ? {
              [sticky]: stickyOffset,
          }
        : null

    return (
        <GridHeaderCellLayout
            ref={setNodeRef}
            {...attributes}
            role="columnheader"
            aria-roledescription={undefined}
            aria-label={column.accessibleLabel}
            aria-colindex={columnIndex + 1}
            aria-sort={
                sort?.direction
                    ? sort?.direction === 'asc'
                        ? 'ascending'
                        : 'descending'
                    : 'none'
            }
            aria-disabled={
                column.sortable || column.id === SELECTION_COLUMN_ID
                    ? undefined
                    : attributes['aria-disabled']
            }
            scope="col"
            style={{
                ...extraStyle,
                width: actualWidth,
                height,
                transform: `translateY(-${parentOffsetHeight}px)`,
            }}
            tabIndex={
                column.id !== SELECTION_COLUMN_ID ? rendererTabIndex : undefined
            }
            {...listeners}
            onKeyDown={(ev: React.KeyboardEvent<HTMLElement>) => {
                if (column.sortable) {
                    handleKeyDown(ev)
                }
                listeners?.onKeyDown?.(ev)
            }}
            onMouseEnter={onEnter}
            onMouseLeave={onLeave}
            onClick={column.sortable ? handleClick : undefined}
            onPointerDown={
                column.id !== SELECTION_COLUMN_ID
                    ? handlePointerDown
                    : handleFocus
            }
            $sortable={column.sortable}
            $movable={column.movable}
            $hovering={!placeholder && hovering}
            $placeholder={placeholder && !lastPlaceholder}
            $resizing={!!resizing}
            $highlight={isCellEditing}
            data-current-cell={hasFocus ? 'true' : undefined}
            $sticky={!!sticky}
            $leftBorder={showLeftBorder}
            $rightBorder={showRightBorder}
            onFocus={focusProps.onFocus}
            onBlur={focusProps.onBlur}
        >
            {isTreeColumn && !placeholder && (
                <GridHeaderCellExpand
                    tabIndex={functionalTabIndex}
                    collapseOnly={column.header?.treeCollapseOnly}
                />
            )}
            <GridCellFocusLayout
                $focusVisible={
                    rendererHasFocus &&
                    focusProps.focusVisible &&
                    !rendererWantsToDisplayFocusStyles
                }
                style={{
                    paddingTop: parentOffsetHeight,
                    height: size.small - 1 + parentOffsetHeight,
                }}
                $editable={false}
                $editing={false}
            >
                {!placeholder ? (
                    CellHeaderComponent ? (
                        <CellHeaderComponent
                            tabIndex={
                                column.id === SELECTION_COLUMN_ID && hasFocus
                                    ? rendererTabIndex
                                    : -1
                            }
                            columnId={columnId}
                        />
                    ) : (
                        column.label
                    )
                ) : null}
                {column.movable && hovering && !resizing && !placeholder ? (
                    <GridColumnMoveHandle
                        ref={setActivatorNodeRef}
                        horizontal
                        $resizable={!!column.resizable}
                    />
                ) : null}
                {(column.resizable && hovering && !placeholder) || resizing ? (
                    <GridColumnResizeHandle
                        $resizing={!!resizing}
                        onClick={stopPropagation}
                        {...resizeHandlers}
                    />
                ) : null}
            </GridCellFocusLayout>
        </GridHeaderCellLayout>
    )
}

export const GridHeaderCellMemo = React.memo(
    GridHeaderCell
) as typeof GridHeaderCell
