/**
 * Makes the first character of a string uppercase
 */
export function upperFirst(str: string): string {
    return str.charAt(0).toUpperCase() + str.slice(1)
}

interface HTMLExpandElement extends HTMLElement {
    _parent?: (Node & ParentNode & HTMLElement) | null
    _initialStyle: {
        transition: string
        overflow: string | null
        height?: string | null
        width?: string | null
    }
}

export default function (expandedParentClass = '', x = false) {
    const sizeProperty = x ? 'width' : ('height' as 'width' | 'height')
    const offsetProperty = `offset${upperFirst(sizeProperty)}` as 'offsetHeight' | 'offsetWidth'

    return {
        beforeEnter(el: HTMLExpandElement) {
            el._parent = el.parentNode as (Node & ParentNode & HTMLElement) | null
            el._initialStyle = {
                transition: el.style.transition,
                overflow: el.style.overflow,
                [sizeProperty]: el.style[sizeProperty],
            }
        },

        enter(el: HTMLExpandElement) {
            const initialStyle = el._initialStyle

            el.style.setProperty('transition', 'none', 'important')
            el.style.overflow = 'hidden'
            // const offset = `${el[offsetProperty]}px`;

            // el.style[sizeProperty] = '0';

            void el.offsetHeight // force reflow

            el.style.transition = initialStyle.transition

            if (expandedParentClass && el._parent) {
                el._parent.classList.add(expandedParentClass)
            }

            requestAnimationFrame(() => {
                // el.style[sizeProperty] = offset;
            })
        },

        afterEnter: resetStyles,
        enterCancelled: resetStyles,

        leave(el: HTMLExpandElement) {
            el._initialStyle = {
                transition: '',
                overflow: el.style.overflow,
                [sizeProperty]: el.style[sizeProperty],
            }

            el.style.overflow = 'hidden'
            el.style[sizeProperty] = `${el[offsetProperty]}px`

            void el.offsetHeight // force reflow

            requestAnimationFrame(() => (el.style[sizeProperty] = '0'))
        },

        afterLeave,
        leaveCancelled: afterLeave,
    }

    function afterLeave(el: HTMLExpandElement) {
        if (expandedParentClass && el._parent) {
            el._parent.classList.remove(expandedParentClass)
        }
        resetStyles(el)
    }

    function resetStyles(el: HTMLExpandElement) {
        const size = el._initialStyle[sizeProperty]
        el.style.overflow = el._initialStyle.overflow!
        if (size != null) el.style[sizeProperty] = size
        Reflect.deleteProperty(el, '_initialStyle')
    }
}
