import {PixelRatio} from "react-native"
import {type SharedValue, useAnimatedReaction, useDerivedValue} from "react-native-reanimated"

import {useBottomSheetToolboxInternalContext, useKeyboardProgressSharedValue} from "../context"
import {useLastSharedValueBeforeLock} from "./use-last-shared-value-before-lock"

export interface UseFooterBottomPositionSharedValueArgs {
    /**
     * A shared value that tells the footer how it should be moved with the
     * sheet. By default, this is the internal children height of the sheet.
     */
    bufferSharedValue: SharedValue<number | null>

    /**
     * A shared value of the bottom position when the sheet was locked.
     */
    bottomAtTimeOfLockSharedValue: SharedValue<number | null>
}

/**
 * Returns a shared value that tells the footer whether it should freeze its
 * position as the content is expanding.
 */
export function useFooterFreezeWhenExpanding({
    bufferSharedValue,
    bottomAtTimeOfLockSharedValue,
}: UseFooterBottomPositionSharedValueArgs) {
    const {currentPositionDestinationSharedValue} = useBottomSheetToolboxInternalContext()
    const keyboardProgressSharedValue = useKeyboardProgressSharedValue()

    const lastBufferSharedValue = useLastSharedValueBeforeLock(bufferSharedValue)

    const pixelRatio = PixelRatio.get()

    useAnimatedReaction(
        () => currentPositionDestinationSharedValue.get(),
        (destination, lastDestination) => {
            const lastBuffer = lastBufferSharedValue.get()
            if (lastBuffer !== null) {
                const destinationDiff = destination - (lastDestination ?? 0)
                const destinationDiffRounded = Math.round(destinationDiff * pixelRatio) / pixelRatio
                if (destinationDiffRounded < -1) {
                    lastBufferSharedValue.set(bufferSharedValue.get())
                }
            }
        },
        [currentPositionDestinationSharedValue]
    )

    const isExpandingSharedValue = useDerivedValue(() => {
        const buffer = bufferSharedValue.get()
        const lastBuffer = lastBufferSharedValue.get()
        const delta = buffer === null || lastBuffer === null ? null : buffer - lastBuffer
        return delta !== null && delta > 0
    }, [lastBufferSharedValue, bufferSharedValue.get])

    // This happens when opening and quickly closing.
    const wasMovingWithSheetSharedValue = useDerivedValue(() => {
        const bottomAtTimeOfLock = bottomAtTimeOfLockSharedValue.get()
        const roundedBottom =
            bottomAtTimeOfLock !== null
                ? Math.round(bottomAtTimeOfLock * pixelRatio) / pixelRatio
                : null

        return roundedBottom !== null && roundedBottom < 0
    }, [bottomAtTimeOfLockSharedValue, pixelRatio])

    const isKeyboardMovingSharedValue = useDerivedValue(
        () => keyboardProgressSharedValue.get() !== 0 && keyboardProgressSharedValue.get() !== 1,
        [keyboardProgressSharedValue]
    )

    /**
     * If the content-size of the sheet increases, then we actually want the
     * footer to stay where it is (not shift down as if it's animating back in).
     *
     *
     * To determine this, we snapshot the sheet position at the time of the lock
     * and see if we're expanding up or down. We also keep a snapshot of the
     * bottom right before the lock was engaged.
     */
    return useDerivedValue(() => {
        const wasMovingWithSheet = wasMovingWithSheetSharedValue.get()
        const isSettled = !wasMovingWithSheet && !isKeyboardMovingSharedValue.get()

        const isExpanding = isExpandingSharedValue.get()

        return isExpanding && isSettled
    }, [])
}
