import React from 'react'
import type { Column, GridRowData, GridRowMeta } from '../../types'

import type { GridApi } from '../../api'
import type { ListGridProps } from './types'
import { Grid } from '../grid'
import { size, useResizeObserverForRef } from '@planview/pv-utilities'
import { GridPrivateSettingsProvider } from '../../context/grid-private-settings-context'
import { DEFAULT_COLUMN_WIDTH } from '../../constants'

const ListGridImpl = <
    TDataModel extends GridRowData,
    TMetaModel extends GridRowMeta<TDataModel>,
>(
    {
        columns,
        flexColumnId = columns[0]?.id,
        className,
        ...props
    }: ListGridProps<TDataModel>,
    ref: React.ForwardedRef<GridApi>
) => {
    const [dimensionRef, { width }] = useResizeObserverForRef()
    const [scrollbarWidth, setScrollbarWidth] = React.useState(0)

    const hasActionsMenu = props.actionsMenu !== undefined
    const preparedColumns = React.useMemo(() => {
        let otherWidth = columns.reduce(
            (acc, column) =>
                column.id === flexColumnId
                    ? acc
                    : acc + (column.width ?? DEFAULT_COLUMN_WIDTH),
            0
        )
        if (hasActionsMenu) {
            otherWidth += size.small
        }
        otherWidth += scrollbarWidth

        return columns.map<Column<TDataModel>>((column) => {
            const columnWidth =
                column.id === flexColumnId ? width - otherWidth : column.width
            return {
                ...column,
                label: '',
                accessibleLabel: column.label,
                treeIndentSize: column.treeIndentSize ?? 'small',
                width: columnWidth,
                sortable: false,
                resizable: false,
                movable: false,
                border: undefined,
                lockedLocation: 'none',
                sticky: 'none',
                hidden: false,
                hideable: false,
            }
        })
    }, [columns, flexColumnId, width, hasActionsMenu, scrollbarWidth])

    return (
        <div ref={dimensionRef} className={className}>
            <GridPrivateSettingsProvider
                hideHeaders
                loopHorizontally
                disableHorizontalScroll
                initialFocusMode={props.rowDrag ? 'row' : 'cell'}
                onScrollbarVisibilityChange={({ verticalWidth }) => {
                    setScrollbarWidth(verticalWidth)
                }}
            >
                <Grid<TDataModel, TMetaModel>
                    ref={ref}
                    selectionMode="none"
                    {...props}
                    columns={preparedColumns}
                />
            </GridPrivateSettingsProvider>
        </div>
    )
}
/**
 * **Important:** The Grid relies heavily on receiving immutable data
 * that changes only when actual changes occur. Even though the examples
 * will show arrays and objects being passed directly into `columns`
 * and `rows` props, in your actual application you'll want to ensure
 * you are passing stable objects into these properties.
 */
export const ListGrid = React.forwardRef(ListGridImpl) as <
    TDataModel extends GridRowData,
    TMetaModel extends GridRowMeta<TDataModel>,
>(
    props: ListGridProps<TDataModel, TMetaModel> & {
        ref?: React.ForwardedRef<GridApi>
    }
) => ReturnType<typeof ListGridImpl>
