import type {FlatList, LayoutChangeEvent} from "react-native"
import * as React from "react"
import {useMergeRefs} from "react-merge-refs"
import Animated, {
    type AnimatedRef,
    type FlatListPropsWithLayout,
    type ScrollHandler,
} from "react-native-reanimated"

import {
    BottomSheetScrollGestureDetector,
    useBottomSheetScrollGestureDetectorConnector,
} from "../bottom-sheet-scroll-gesture-detector"
import {BottomSheetListWrapper} from "./bottom-sheet-list-wrapper"

interface BottomSheetFlatListProps<
    T,
    ScrollContext extends Record<string, unknown> = Record<string, unknown>,
> extends Omit<
        FlatListPropsWithLayout<T>,
        | "bounces"
        | "scrollEventThrottle"
        | "onScroll"
        | "onBeginDrag"
        | "onEndDrag"
        | "onMomentumBegin"
        | "onMomentumEnd"
    > {
    onScroll?: ScrollHandler<ScrollContext>
    onBeginDrag?: ScrollHandler<ScrollContext>
    onEndDrag?: ScrollHandler<ScrollContext>
    onMomentumBegin?: ScrollHandler<ScrollContext>
    onMomentumEnd?: ScrollHandler<ScrollContext>
    ref?: AnimatedRef<FlatList<T>>
}

function makeBottomSheetFlatList(inverted: boolean) {
    return function BottomSheetFlatList<T>({
        onScroll,
        onBeginDrag,
        onEndDrag,
        onMomentumBegin,
        onMomentumEnd,
        onContentSizeChange,
        ref,
        onLayout,
        ...rest
    }: BottomSheetFlatListProps<T>) {
        const {
            scrollHandler,
            scrollOffsetYSharedValue,
            scrollContentHeightSharedValue,
            scrollLayoutHeightSharedValue,
            scrollRef,
        } = useBottomSheetScrollGestureDetectorConnector<FlatList<T>>({
            ...(onScroll && {onScroll}),
            ...(onBeginDrag && {onBeginDrag}),
            ...(onEndDrag && {onEndDrag}),
            ...(onMomentumBegin && {onMomentumBegin}),
            ...(onMomentumEnd && {onMomentumEnd}),
        })

        const refs = useMergeRefs([scrollRef, ref])

        const innerOnContentSizeChange = React.useCallback(
            (w: number, h: number) => {
                if (typeof onContentSizeChange === "function") {
                    onContentSizeChange(w, h)
                } else if (onContentSizeChange?.value) {
                    onContentSizeChange.value(w, h)
                }
                scrollContentHeightSharedValue.set(h)
            },
            [onContentSizeChange, scrollContentHeightSharedValue]
        )

        const innerOnLayout = React.useCallback(
            (evt: LayoutChangeEvent) => {
                if (typeof onLayout === "function") {
                    onLayout(evt)
                } else if (onLayout?.value) {
                    onLayout.value(evt)
                }
                scrollLayoutHeightSharedValue.set(evt.nativeEvent.layout.height)
            },
            [onLayout, scrollLayoutHeightSharedValue]
        )

        return (
            <BottomSheetScrollGestureDetector
                scrollRef={scrollRef}
                scrollOffsetYSharedValue={scrollOffsetYSharedValue}
                scrollContentHeightSharedValue={scrollContentHeightSharedValue}
                scrollLayoutHeightSharedValue={scrollLayoutHeightSharedValue}
                inverted={inverted}
            >
                <BottomSheetListWrapper contentHeightSharedValue={scrollContentHeightSharedValue}>
                    <Animated.FlatList
                        ref={refs as React.ForwardedRef<FlatList<T>>}
                        bounces={false}
                        scrollEventThrottle={1}
                        onScroll={scrollHandler}
                        onContentSizeChange={innerOnContentSizeChange}
                        onLayout={innerOnLayout}
                        {...rest}
                    />
                </BottomSheetListWrapper>
            </BottomSheetScrollGestureDetector>
        )
    }
}

export const BottomSheetFlatList = makeBottomSheetFlatList(false)

export const InvertedBottomSheetFlatList = makeBottomSheetFlatList(true)
