import React from 'react'
import { useGridContext, useGridSelector } from '../context/grid-context'
import type { MenuCloseReason } from '@planview/pv-uikit'
import { ListGroup, ListItem, Menu } from '@planview/pv-uikit'
import { defineMessages, useIntl } from 'react-intl'
import { buildHeaderHierarchy } from '../state/reducer/column-utils'
import type { ColumnState } from '../state'
const messages = defineMessages({
    labelColumnMenu: {
        id: 'pvds.grid.rows.columnMenuLabel',
        defaultMessage: 'Show or hide columns',
        description:
            'Screen reader label for the column context menu to control the visibility of columns',
    },
})

const EMPTY_ARRAY: never[] = []

const getColumnLabel = (column: ColumnState) =>
    column.data.accessibleLabel ?? column.data.label

const isColumnGroup = (
    column: ColumnState | { label: string; children: ColumnState[] }
): column is { label: string; children: ColumnState[] } =>
    typeof column === 'object' && 'label' in column

export const GridColumnMenu = () => {
    const grid = useGridContext()

    const { formatMessage } = useIntl()

    const { visible, coordinates } = useGridSelector(
        grid.selectors.selectColumnMenuDetails
    )

    const handleClose = React.useCallback(
        (reason: MenuCloseReason) => {
            if (reason !== 'confirm' && reason !== 'confirm_with_focus') {
                grid.api.column.hideMenu()
            }
            if (reason === 'key_escape' || reason === 'key_tab') {
                grid.events.emit('onReturnFocus')
            }
        },
        [grid.api.column, grid.events]
    )

    const {
        selectColumnIdsWithoutInternal,
        selectColumnEntities,
        selectHiddenIds,
    } = grid.selectors

    const hiddenIds = useGridSelector(selectHiddenIds)

    const columnsToRender = useGridSelector((state) => {
        if (!visible) {
            return EMPTY_ARRAY
        }
        const sortedIds = selectColumnIdsWithoutInternal(state)
        const entities = selectColumnEntities(state)
        const { groups } = buildHeaderHierarchy(entities, [])
        return sortedIds
            .map((id) => entities[id])
            .filter(
                (column) =>
                    column.id !== '__actions' &&
                    getColumnLabel(column).trim().length > 0 &&
                    column.data.hideable !== false
            )
            .reduce(
                (acc, column) => {
                    const parent = groups.find((group) =>
                        group.childIds.includes(column.id)
                    )
                    if (parent) {
                        const parentEntity = entities[parent.id]
                        const foundGroup = acc.find(
                            (col) => col.id === parent.id
                        )
                        if (foundGroup && isColumnGroup(foundGroup)) {
                            foundGroup.children.push(column)
                        } else {
                            const group = {
                                label: parentEntity.data.label,
                                id: parentEntity.id,
                                children: [column],
                            }
                            acc.push(group)
                        }
                    } else {
                        acc.push(column)
                    }
                    return acc
                },
                [] as (
                    | ColumnState
                    | { label: string; children: ColumnState[]; id: string }
                )[]
            )
    })

    if (!visible || !coordinates) {
        return null
    }

    const { top, left } = coordinates

    return (
        <Menu
            open
            label={formatMessage(messages.labelColumnMenu)}
            anchor={{ top, left }}
            onClose={handleClose}
            multiSelectable
        >
            {columnsToRender.map((column) =>
                isColumnGroup(column) ? (
                    <ListGroup label={column.label} key={column.id}>
                        {column.children.map((child) => (
                            <ListItem
                                key={child.id}
                                onActivate={() => {
                                    grid.api.column.updateColumnVisibility(
                                        child.id,
                                        hiddenIds.includes(child.id)
                                    )
                                    grid.events.emit('onColumnStateChange')
                                }}
                                selected={!hiddenIds.includes(child.id)}
                                label={getColumnLabel(child)}
                            />
                        ))}
                    </ListGroup>
                ) : (
                    <ListItem
                        key={column.id}
                        onActivate={() => {
                            grid.api.column.updateColumnVisibility(
                                column.id,
                                hiddenIds.includes(column.id)
                            )
                            grid.events.emit('onColumnStateChange')
                        }}
                        selected={!hiddenIds.includes(column.id)}
                        label={getColumnLabel(column)}
                    />
                )
            )}
        </Menu>
    )
}
