// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {createContext, ReactElement, ReactNode, useCallback, useContext, useMemo} from 'react'

import {Constants} from '../../constants'

export type ColumnResizeContextType = {
    updateRef: (cardId: string, columnId: string, element: HTMLDivElement | null) => void
    cellRef: (columnId: string) => HTMLDivElement | undefined
    width: (columnId: string) => number
    updateOffset: (columnId: string, offset: number) => void
    updateWidth: (columnId: string, width: number) => void
}

const ColumnResizeContext = createContext<ColumnResizeContextType | null>(null)

export function useColumnResize(): ColumnResizeContextType {
    const context = useContext(ColumnResizeContext)
    if (!context) {
        throw new Error('ColumnResizeContext is not available!')
    }
    return context
}

export type ColumnResizeProviderProps = {
    children: ReactNode
    columnWidths: Record<string, number>
    onResizeColumn: (columnId: string, width: number) => void
}

const columnWidth = (columnId: string, columnWidths: Record<string, number>, offset: number): string => {
    return `${Math.max(Constants.minColumnWidth, (columnWidths[columnId] || 0) + offset)}px`
}

export const ColumnResizeProvider = (props: ColumnResizeProviderProps): ReactElement => {
    const {children, columnWidths, onResizeColumn} = props

    type ElementsMap = Map<string, HTMLDivElement>
    const columns = useMemo(() => new Map<string, ElementsMap>(), [])

    const updateWidth = useCallback((columnId: string, elements: ElementsMap, offset: number) => {
        const width = columnWidth(columnId, columnWidths, offset)
        for (const element of elements.values()) {
            element.style.width = width
        }
    }, [columnWidths])

    const contextValue = useMemo((): ColumnResizeContextType => ({
        updateRef: (cardId, columnId, element) => {
            let elements = columns.get(columnId)
            if (element) {
                if (!elements) {
                    elements = new Map()
                    columns.set(columnId, elements)
                }
                elements.set(cardId, element)
            } else if (elements) {
                elements.delete(cardId)
            }
        },
        cellRef: (columnId): HTMLDivElement | undefined => {
            const iter = columns.get(columnId)?.values()
            if (iter) {
                const {value, done} = iter.next()
                return done ? value : iter.next().value
            }
            return undefined
        },
        width: (columnId) => {
            return Math.max(Constants.minColumnWidth, (columnWidths[columnId] || 0))
        },
        updateOffset: (columnId, offset) => {
            const elements = columns.get(columnId)
            if (elements) {
                updateWidth(columnId, elements, offset)
            }
        },
        updateWidth: (columnId, width) => {
            onResizeColumn(columnId, width)
        },
    }), [columnWidths, onResizeColumn])

    return (
        <ColumnResizeContext.Provider value={contextValue}>
            {children}
        </ColumnResizeContext.Provider>
    )
}
