import * as React from "react"

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

export interface BottomSheetToolboxStackContextShape {
    sheetStack: readonly string[]
    sheetRefsRef: React.RefObject<Record<string, BottomSheetToolboxRef>>
    push: (sheetName: string) => void
    pop: () => void
    popToTop: () => void
    closeAll: () => void
    notifyClosed: (sheetName: string) => void
    activeSheet: string | null
}

const BottomSheetToolboxStackContext = React.createContext<
    BottomSheetToolboxStackContextShape | undefined
>(undefined)

export function useIsBottomSheetToolboxStackContext() {
    const context = React.useContext(BottomSheetToolboxStackContext)

    return context !== undefined
}

export function useBottomSheetToolboxStackContext() {
    const context = React.useContext(BottomSheetToolboxStackContext)

    if (!context) {
        throw new Error("BottomSheetToolboxStackProvider not found.")
    }

    return context
}

export interface BottomSheetToolboxStackProviderRef {
    close: () => void
}

export interface BottomSheetToolboxStackProviderProps {
    children: React.ReactNode
    initialSheetName: string | null
    onClose?: () => void
    ref?: React.ForwardedRef<BottomSheetToolboxStackProviderRef>
}

type NotifyClosedBehavior = "POP" | "POP_TO_TOP" | "CLOSE_ALL"

export function BottomSheetToolboxStackProvider({
    children,
    initialSheetName,
    onClose,
    ref,
}: BottomSheetToolboxStackProviderProps) {
    const [sheetStack, setSheetStack] = React.useState(() =>
        initialSheetName !== null ? [initialSheetName] : []
    )

    const activeSheet = sheetStack.at(-1) ?? null
    const sheetRefsRef = React.useRef<Record<string, BottomSheetToolboxRef>>({})
    const nextNotifyClosedBehaviorRef = React.useRef<NotifyClosedBehavior>("POP")

    const push = React.useCallback((sheetName: string) => {
        setSheetStack((s) => [...s, sheetName])
    }, [])

    const pop = React.useCallback(() => {
        if (!activeSheet) return

        const sheetRefs = sheetRefsRef.current
        const topSheet = sheetRefs[activeSheet]
        topSheet?.close()
    }, [activeSheet])

    const canPopToTop = sheetStack.length > 1
    const popToTop = React.useCallback(() => {
        if (!canPopToTop) return
        nextNotifyClosedBehaviorRef.current = "POP_TO_TOP"

        pop()
    }, [canPopToTop, pop])

    const closeAll = React.useCallback(() => {
        nextNotifyClosedBehaviorRef.current = "CLOSE_ALL"
        pop()
    }, [pop])

    const notifyClosed = React.useCallback((name: string) => {
        switch (nextNotifyClosedBehaviorRef.current) {
            case "POP":
                setSheetStack((s) => {
                    const currentActiveSheet = s.at(-1) ?? null
                    return name === currentActiveSheet ? s.slice(0, -1) : s
                })
                break
            case "POP_TO_TOP":
                setSheetStack((s) => s.slice(0, 1))
                break
            case "CLOSE_ALL":
                setSheetStack([])
                break
        }
        nextNotifyClosedBehaviorRef.current = "POP"
    }, [])

    // When the last sheet is removed, consider the stack to be closed.
    const hadContentRef = React.useRef(sheetStack.length > 0)
    React.useEffect(() => {
        const hasContent = sheetStack.length > 0
        const hadContent = hadContentRef.current

        if (hadContent && !hasContent) {
            onClose?.()
        }

        hadContentRef.current = hasContent
    }, [onClose, sheetStack])

    const contextValue = React.useMemo(
        () => ({
            sheetStack,
            push,
            popToTop,
            pop,
            notifyClosed,
            activeSheet,
            closeAll,
            sheetRefsRef,
        }),
        [sheetStack, push, notifyClosed, activeSheet, popToTop, pop, closeAll]
    )

    React.useImperativeHandle(ref, () => ({close: closeAll}), [closeAll])

    return (
        <BottomSheetToolboxBackdropProvider reset>
            <BottomSheetToolboxStackContext.Provider value={contextValue}>
                {children}
            </BottomSheetToolboxStackContext.Provider>
        </BottomSheetToolboxBackdropProvider>
    )
}
