/**
 * Animates an element using keyframes. Returns a promise that resolves after the animation completes or gets canceled.
 */
export function animateTo(
    el: HTMLElement,
    keyframes: Keyframe[],
    options?: KeyframeAnimationOptions
) {
    return new Promise(resolve => {
        if (options?.duration === Infinity) {
            throw new Error('Promise-based animations must be finite.')
        }

        const animation = el.animate(keyframes, {
            ...options,
            duration: prefersReducedMotion() ? 0 : options!.duration,
        })

        animation.addEventListener('cancel', resolve, { once: true })
        animation.addEventListener('finish', resolve, { once: true })
    })
}

/** Parses a CSS duration and returns the number of milliseconds. */
export function parseDuration(delay: number | string) {
    delay = delay.toString().toLowerCase()

    if (delay.indexOf('ms') > -1) {
        return parseFloat(delay)
    }

    if (delay.indexOf('s') > -1) {
        return parseFloat(delay) * 1000
    }

    return parseFloat(delay)
}

/** Tells if the user has enabled the "reduced motion" setting in their browser or OS. */
export function prefersReducedMotion() {
    const query = window.matchMedia('(prefers-reduced-motion: reduce)')
    return query.matches
}

/**
 * Stops all active animations on the target element. Returns a promise that resolves after all animations are canceled.
 */
export function stopAnimations(el: HTMLElement) {
    return Promise.all(
        el.getAnimations().map(animation => {
            return new Promise(resolve => {
                animation.cancel()
                requestAnimationFrame(resolve)
            })
        })
    )
}

/**
 * We can't animate `height: auto`, but we can calculate the height and shim keyframes by replacing it with the
 * element's scrollHeight before the animation.
 */
export function shimKeyframesHeightAuto(
    keyframes: Keyframe[],
    calculatedHeight: number
) {
    return keyframes.map(keyframe => ({
        ...keyframe,
        height:
            keyframe.height === 'auto' ? `${calculatedHeight}px` : keyframe.height,
    }))
}
