import {nanoid} from "nanoid"
import * as React from "react"
import {useMergeRefs} from "react-merge-refs"

import type {
    BottomSheetToolboxProps,
    BottomSheetToolboxRef,
} from "@attio/react-native-bottom-sheet-toolbox"

import {BottomSheetToolboxStackSheet} from "./bottom-sheet-toolbox-stack-sheet"
import {
    BottomSheetToolboxStackProvider,
    type BottomSheetToolboxStackProviderRef,
    useBottomSheetToolboxStackContext,
    useIsBottomSheetToolboxStackContext,
} from "./context/bottom-sheet-toolbox-stack-context"
import {useBottomSheetToolboxStackSheet} from "./use-bottom-sheet-toolbox-stack-sheet"

export interface BottomSheetToolboxStackSheetAutoProviderRef {
    open: () => void
    close: () => void
}

interface BottomSheetToolboxStackSheetAutoProviderDynamicSheetProps {
    ref?: React.ForwardedRef<BottomSheetToolboxRef> | undefined
    onClose?: () => void
    children: (
        p: Pick<BottomSheetToolboxProps, "onSnapChange" | "onClose" | "onReadyChange"> & {
            sheetRef?: React.ForwardedRef<BottomSheetToolboxRef>
        }
    ) => React.ReactNode
    openSheetOnMount: boolean
}

function BottomSheetToolboxStackSheetAutoProviderDynamicSheet({
    ref,
    onClose,
    children,
    openSheetOnMount,
}: BottomSheetToolboxStackSheetAutoProviderDynamicSheetProps) {
    const [sheetId] = React.useState(() => nanoid())

    const {wrapperProps, sheetProps} = useBottomSheetToolboxStackSheet(sheetId)

    const mergedRefs = useMergeRefs([ref, sheetProps.ref])
    const innerSheetProps = React.useMemo(() => {
        const {ref: _ref, onClose: sheetPropsOnClose, ...rest} = sheetProps

        return {
            ...rest,
            sheetRef: mergedRefs,
            onClose: () => {
                sheetPropsOnClose()
            },
        }
    }, [sheetProps, mergedRefs])

    const stack = useBottomSheetToolboxStackContext()

    const onMountFn = () => {
        if (openSheetOnMount) {
            stack.push(sheetId)
        }
    }
    const onMountRef = React.useRef(onMountFn)
    onMountRef.current = onMountFn

    const unmountFn = () => {
        stack.notifyClosed(sheetId)
    }
    const onUnmountRef = React.useRef(unmountFn)
    onUnmountRef.current = unmountFn

    React.useLayoutEffect(() => {
        onMountRef.current()

        return () => onUnmountRef.current()
    }, [])

    const isInStack = stack.sheetStack.some((sheet) => sheet === sheetId)
    const wasInStackRef = React.useRef<boolean>(false)
    React.useEffect(() => {
        wasInStackRef.current = isInStack
    }, [isInStack])
    const wasInStack = wasInStackRef.current

    const stableOnCloseRef = React.useRef(onClose)
    React.useEffect(() => {
        stableOnCloseRef.current = onClose
    }, [onClose])

    React.useEffect(() => {
        if (!isInStack && wasInStack) {
            stableOnCloseRef.current?.()
        }
    }, [isInStack, wasInStack])

    return (
        <BottomSheetToolboxStackSheet {...wrapperProps}>
            {children(innerSheetProps)}
        </BottomSheetToolboxStackSheet>
    )
}

type BottomSheetToolboxStackSheetAutoProviderInjectProviderProps =
    BottomSheetToolboxStackSheetAutoProviderDynamicSheetProps
function BottomSheetToolboxStackSheetAutoProviderInjectProvider({
    onClose,
    ...rest
}: BottomSheetToolboxStackSheetAutoProviderInjectProviderProps) {
    const ref = React.useRef<BottomSheetToolboxStackProviderRef>(null)

    const innerOnClose = React.useCallback(() => {
        ref.current?.close()
    }, [])

    return (
        <BottomSheetToolboxStackProvider
            ref={ref}
            initialSheetName={null}
            {...(onClose && {onClose})}
        >
            <BottomSheetToolboxStackSheetAutoProviderDynamicSheet
                {...rest}
                onClose={innerOnClose}
            />
        </BottomSheetToolboxStackProvider>
    )
}

/**
 * A higher-order component that provides automatic stack context handling for bottom sheets.
 * If rendered within an existing BottomSheetToolboxStack context, it will integrate with that stack.
 * If no stack context exists, it will create a new BottomSheetToolboxStack context to manage the sheet.
 * This allows bottom sheets to work seamlessly both standalone and as part of a larger sheet stack, allowing for
 * more composable components.
 */
export type BottomSheetToolboxStackSheetAutoProviderProps =
    BottomSheetToolboxStackSheetAutoProviderDynamicSheetProps
export function BottomSheetToolboxStackSheetAutoProvider(
    props: BottomSheetToolboxStackSheetAutoProviderProps
) {
    const isStackContext = useIsBottomSheetToolboxStackContext()

    return isStackContext ? (
        <BottomSheetToolboxStackSheetAutoProviderDynamicSheet {...props} />
    ) : (
        <BottomSheetToolboxStackSheetAutoProviderInjectProvider {...props} />
    )
}
