// import { nanoid } from "nanoid/non-secure";
import * as React from "react"
import {useAnimatedReaction, useSharedValue} from "react-native-reanimated"

import {useBottomSheetToolboxInternalContext} from "../context"
import {DEFAULT_CLOSE_SHEET_SNAP_POINT_SPRING_CONFIG} from "../utils"
import {BottomSheetPositionBuiltInLockPriority, CLOSED_SNAP_POINT} from "../utils/constants"
import {useBottomSheetOnSnapDetection} from "./use-bottom-sheet-on-snap-detection"
import {useBottomSheetPositionLock} from "./use-bottom-sheet-position-lock"
import {useWorkletUiCallback} from "./use-worklet-ui-callback"

export interface BottomSheetToolboxActions {
    snapToIndex: (snapPointIndex: number) => void
    expand: () => void
    close: () => void
    getCurrentSnapIndex: () => number | null
}

/**
 * Creates a set of actions that can be used to imperatively control the bottom sheet.
 */
export function useBottomSheetActions(id: string) {
    const {currentPositionSharedValue, normalizedSnapPointsSharedValue} =
        useBottomSheetToolboxInternalContext()

    const {
        lock: snapLock,
        release: setSnapRelease,
        setPositionAnimate: setSnapPositionAnimate,
        setVelocity: setSnapVelocity,
        hasLockSharedValue: hasSnapLockSharedValue,
    } = useBottomSheetPositionLock(
        `useBottomSheetActions:${id}:snap`,
        BottomSheetPositionBuiltInLockPriority.ACTIONS_SNAP
    )

    // We apply imperative snaps reactively, like this, so we can adjust for changes in the snap points etc.
    const snapDestinationIndexSharedValue = useSharedValue<number | null>(null)
    const lastSnapPointsSharedValue = useSharedValue<number[] | null>(null)
    useAnimatedReaction(
        () => ({
            currentPosition: currentPositionSharedValue.get(),
            snapDestinationIndex: snapDestinationIndexSharedValue.get(),
            snapPoints: normalizedSnapPointsSharedValue.get(),
            hasSnapLock: hasSnapLockSharedValue.get(),
        }),
        ({currentPosition, snapPoints, snapDestinationIndex, hasSnapLock}, lastResult) => {
            if (!hasSnapLock && lastResult?.hasSnapLock === true) {
                snapDestinationIndexSharedValue.set(null)

                return
            }
            if (snapPoints === null || snapDestinationIndex === null) return

            const lastSnapPoints = lastSnapPointsSharedValue.get()
            const didSnapChange =
                lastSnapPoints === null ||
                lastSnapPoints[snapDestinationIndex] !== snapPoints[snapDestinationIndex] ||
                lastResult?.snapDestinationIndex !== snapDestinationIndex

            const snapPoint = snapPoints[snapDestinationIndex] ?? null

            if (snapPoint === null || currentPosition === snapPoint) {
                snapDestinationIndexSharedValue.set(null)
                setSnapRelease()

                return
            }

            if (!didSnapChange) return

            lastSnapPointsSharedValue.set(snapPoints)

            setSnapVelocity(0)
            setSnapPositionAnimate(snapPoint)
        },
        [hasSnapLockSharedValue, currentPositionSharedValue, normalizedSnapPointsSharedValue]
    )

    const snapToIndex = useWorkletUiCallback(
        (snapPointIndex: number) => {
            "worklet"

            const normalizedSnapPoints = normalizedSnapPointsSharedValue.get()
            if (!normalizedSnapPoints || normalizedSnapPoints.length === 0) {
                throw new Error("No snap-points to snap to.")
            }

            if (!(snapPointIndex in normalizedSnapPoints)) {
                throw new Error(`Invalid snap point index: ${snapPointIndex}`)
            }

            snapLock()
            snapDestinationIndexSharedValue.set(snapPointIndex)
        },
        [snapLock, normalizedSnapPointsSharedValue]
    )

    const {
        lock: closeLock,
        release: closeRelease,
        setPositionAnimate: setClosePositionAnimate,
        setVelocity: setCloseVelocity,
        hasLockSharedValue: hasCloseLockSharedValue,
    } = useBottomSheetPositionLock(
        `useBottomSheetActions:${id}:close`,
        BottomSheetPositionBuiltInLockPriority.ACTIONS_CLOSE
    )

    const expand = useWorkletUiCallback(() => {
        "worklet"

        const normalizedSnapPoints = normalizedSnapPointsSharedValue.get()
        if (!normalizedSnapPoints || normalizedSnapPoints.length === 0) {
            throw new Error("No snap-points to snap to.")
        } else if (normalizedSnapPoints.length === 1 && normalizedSnapPoints[0] === 0) {
            throw new Error("Sheet cannot expand as only available snap-point is '0'.")
        }

        snapToIndex(normalizedSnapPoints.length - 1)
    }, [normalizedSnapPointsSharedValue, snapToIndex])

    const close = useWorkletUiCallback(() => {
        "worklet"
        if (hasCloseLockSharedValue.get()) return

        closeLock()
        setCloseVelocity(0)
        setClosePositionAnimate(
            CLOSED_SNAP_POINT,
            DEFAULT_CLOSE_SHEET_SNAP_POINT_SPRING_CONFIG,
            closeRelease
        )
    }, [
        hasCloseLockSharedValue,
        closeLock,
        setCloseVelocity,
        setClosePositionAnimate,
        closeRelease,
    ])

    const currentSnapRef = React.useRef<number | null>(null)
    useBottomSheetOnSnapDetection(
        React.useCallback((snap: number) => {
            currentSnapRef.current = snap
        }, [])
    )
    const getCurrentSnapIndex = React.useCallback(() => currentSnapRef.current, [])

    return React.useMemo<BottomSheetToolboxActions>(
        () => ({snapToIndex, expand, close, getCurrentSnapIndex}),
        [snapToIndex, expand, close, getCurrentSnapIndex]
    )
}
