import React, { useCallback, useEffect, useMemo, useRef } from "react";
import {
	Animated,
	Dimensions,
	Easing,
	Modal,
	PanResponder,
	Pressable,
	StyleSheet,
	View,
} from "react-native";
import { useResponsiveScale } from "../../hooks/useResponsiveScale";
import { useSafeAreaInsets } from "react-native-safe-area-context";

const DISMISS_DRAG_RATIO = 0.22;
const DISMISS_VELOCITY = 1.2;
const DEFAULT_HEIGHT_RATIO = 0.75;

type AnimatedBottomSheetProps = {
	visible: boolean;
	onClose: () => void;
	children: React.ReactNode;
	heightRatio?: number;
	backdropLabel?: string;
};

const AnimatedBottomSheet = ({
	visible,
	onClose,
	children,
	heightRatio = DEFAULT_HEIGHT_RATIO,
	backdropLabel = "Close",
}: AnimatedBottomSheetProps) => {
	const { rs } = useResponsiveScale();
    const insets = useSafeAreaInsets();
	const styles = useMemo(() => createStyles(rs), [rs]);

	const sheetMaxHeight = useMemo(
		() => Dimensions.get("window").height * heightRatio,
		[heightRatio],
	);

	const translateY = useRef(new Animated.Value(0)).current;
	const dragY = useRef(new Animated.Value(0)).current;
	const backdropOpacity = useRef(new Animated.Value(0)).current;

	const sheetStyle = useMemo(
		() => ({
			maxHeight: sheetMaxHeight,
			height: sheetMaxHeight,
			paddingBottom: Math.max(insets.bottom, rs(16)),
		}),
		[insets.bottom, rs, sheetMaxHeight],
	);

	const sheetTranslateY = useMemo(
		() => Animated.add(translateY, dragY),
		[translateY, dragY],
	);

	const resetSheetPosition = useCallback(() => {
		dragY.setValue(0);
		translateY.setValue(sheetMaxHeight);
		backdropOpacity.setValue(0);
	}, [backdropOpacity, dragY, sheetMaxHeight, translateY]);

	const animateSheetClosed = useCallback(() => {
		Animated.parallel([
			Animated.timing(backdropOpacity, {
				toValue: 0,
				duration: 200,
				useNativeDriver: true,
			}),
			Animated.timing(translateY, {
				toValue: sheetMaxHeight,
				duration: 250,
				easing: Easing.in(Easing.cubic),
				useNativeDriver: true,
			}),
		]).start(({ finished }) => {
			if (finished) {
				resetSheetPosition();
				onClose();
			}
		});
	}, [
		backdropOpacity,
		onClose,
		resetSheetPosition,
		sheetMaxHeight,
		translateY,
	]);

	const closeWithAnimation = useCallback(() => {
		translateY.stopAnimation((baseY) => {
			dragY.stopAnimation((offsetY) => {
				dragY.setValue(0);
				translateY.setValue(baseY + offsetY);
				animateSheetClosed();
			});
		});
	}, [animateSheetClosed, dragY, translateY]);

	useEffect(() => {
		if (!visible) {
			return;
		}

		dragY.setValue(0);
		translateY.setValue(sheetMaxHeight);
		backdropOpacity.setValue(0);

		Animated.parallel([
			Animated.timing(backdropOpacity, {
				toValue: 1,
				duration: 250,
				useNativeDriver: true,
			}),
			Animated.spring(translateY, {
				toValue: 0,
				useNativeDriver: true,
				damping: 22,
				stiffness: 220,
			}),
		]).start();
	}, [visible, backdropOpacity, dragY, sheetMaxHeight, translateY]);

	const panResponder = useMemo(
		() =>
			PanResponder.create({
				onStartShouldSetPanResponder: () => true,
				onMoveShouldSetPanResponder: (_, gesture) =>
					gesture.dy > 6 && Math.abs(gesture.dy) > Math.abs(gesture.dx),
				onPanResponderMove: (_, gesture) => {
					if (gesture.dy > 0) {
						dragY.setValue(gesture.dy);
					}
				},
				onPanResponderRelease: (_, gesture) => {
					const shouldDismiss =
						gesture.dy > sheetMaxHeight * DISMISS_DRAG_RATIO ||
						gesture.vy > DISMISS_VELOCITY;

					if (shouldDismiss) {
						closeWithAnimation();
						return;
					}

					Animated.spring(dragY, {
						toValue: 0,
						useNativeDriver: true,
						damping: 20,
						stiffness: 300,
					}).start();
				},
				onPanResponderTerminate: () => {
					Animated.spring(dragY, {
						toValue: 0,
						useNativeDriver: true,
					}).start();
				},
			}),
		[closeWithAnimation, dragY, sheetMaxHeight],
	);

	return (
		<Modal
			transparent
			visible={visible}
			animationType="none"
			onRequestClose={closeWithAnimation}
			statusBarTranslucent
		>
			<View style={styles.root}>
				<Pressable
					accessibilityRole="button"
					accessibilityLabel={backdropLabel}
					style={StyleSheet.absoluteFillObject}
					onPress={closeWithAnimation}
				>
					<Animated.View
						style={[
							StyleSheet.absoluteFillObject,
							styles.backdropDim,
							{ opacity: backdropOpacity },
						]}
					/>
				</Pressable>

				<View style={styles.sheetAlign} pointerEvents="box-none">
					<Animated.View
						style={[
							styles.sheet,
							sheetStyle,
							{ transform: [{ translateY: sheetTranslateY }] },
						]}
					>
						<View style={styles.handleHitArea} {...panResponder.panHandlers}>
							<View style={styles.handle} />
						</View>

						<View style={styles.content}>{children}</View>
					</Animated.View>
				</View>
			</View>
		</Modal>
	);
};

export default AnimatedBottomSheet;

const createStyles = (rs: (value: number) => number) =>
	StyleSheet.create({
		root: {
			flex: 1,
		},
		backdropDim: {
			backgroundColor: "rgba(0, 0, 0, 0.45)",
		},
		sheetAlign: {
			...StyleSheet.absoluteFillObject,
			justifyContent: "flex-end",
		},
		sheet: {
			backgroundColor: "#FFFFFF",
			borderTopLeftRadius: rs(20),
			borderTopRightRadius: rs(20),
			paddingHorizontal: rs(18),
			paddingTop: rs(10),
			overflow: "hidden",
			flexDirection: "column",
			width: "100%",
		},
		handleHitArea: {
			alignItems: "center",
			paddingVertical: rs(8),
			marginBottom: rs(12),
		},
		handle: {
			width: rs(60),
			height: rs(5),
			borderRadius: rs(8),
			backgroundColor: "#E6EAF0",
		},
		content: {
			flex: 1,
			minHeight: 0,
		},
	});
