import React, { useEffect, useMemo, useRef } from "react";
import {
  Animated,
  PanResponder,
  StyleSheet,
  useWindowDimensions,
  View,
} from "react-native";
import { radius, theme } from "../theme";

export type SheetSnapPoint = "peek" | "expanded";

export type SheetProps = {
  visible: boolean;
  snapPoint: SheetSnapPoint;
  onSnapPointChange: (point: SheetSnapPoint) => void;
  /** Drag below peek far enough and the sheet asks to be dismissed. */
  onClose?: () => void;
  /** Visible height at the "peek" snap point. Default 160. */
  peekHeight?: number;
  /** Visible height at the "expanded" snap point. Default 70% of window height. */
  expandedHeight?: number;
  children: React.ReactNode;
  testID?: string;
};

const CLOSE_DRAG_THRESHOLD = 80;
const VELOCITY_THRESHOLD = 0.5;

/**
 * Draggable bottom sheet with peek/expanded snap points, floating over the
 * map surface (S5–S7 host their content in one of these). Built on
 * Animated + PanResponder with useNativeDriver so drag/spring stay off the
 * JS thread — no reanimated dependency needed for this gesture.
 */
export function Sheet({
  visible,
  snapPoint,
  onSnapPointChange,
  onClose,
  peekHeight = 160,
  expandedHeight,
  children,
  testID,
}: SheetProps): React.JSX.Element | null {
  const { height: windowHeight } = useWindowDimensions();
  const resolvedExpandedHeight = expandedHeight ?? windowHeight * 0.7;
  const peekTranslate = Math.max(resolvedExpandedHeight - peekHeight, 0);

  const translateY = useRef(new Animated.Value(peekTranslate)).current;
  const dragStartValue = useRef(0);

  const targetForSnapPoint = (point: SheetSnapPoint): number =>
    point === "expanded" ? 0 : peekTranslate;

  useEffect(() => {
    if (!visible) return;
    Animated.spring(translateY, {
      toValue: targetForSnapPoint(snapPoint),
      useNativeDriver: true,
      bounciness: 4,
    }).start();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [snapPoint, visible, resolvedExpandedHeight, peekHeight]);

  const panResponder = useMemo(
    () =>
      PanResponder.create({
        onMoveShouldSetPanResponder: (_evt, gesture) =>
          Math.abs(gesture.dy) > 4,
        onPanResponderGrant: () => {
          dragStartValue.current = targetForSnapPoint(snapPoint);
        },
        onPanResponderMove: (_evt, gesture) => {
          const next = dragStartValue.current + gesture.dy;
          const min = 0;
          const max = peekTranslate + CLOSE_DRAG_THRESHOLD * 1.5;
          translateY.setValue(Math.max(min, Math.min(max, next)));
        },
        onPanResponderRelease: (_evt, gesture) => {
          const current = dragStartValue.current + gesture.dy;

          if (onClose && current > peekTranslate + CLOSE_DRAG_THRESHOLD) {
            onClose();
            return;
          }

          let nextPoint: SheetSnapPoint;
          if (Math.abs(gesture.vy) > VELOCITY_THRESHOLD) {
            nextPoint = gesture.vy > 0 ? "peek" : "expanded";
          } else {
            const midpoint = peekTranslate / 2;
            nextPoint = current > midpoint ? "peek" : "expanded";
          }

          Animated.spring(translateY, {
            toValue: targetForSnapPoint(nextPoint),
            useNativeDriver: true,
            bounciness: 4,
          }).start();

          if (nextPoint !== snapPoint) onSnapPointChange(nextPoint);
        },
      }),
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [peekTranslate, snapPoint, onClose, onSnapPointChange]
  );

  if (!visible) return null;

  return (
    <View style={StyleSheet.absoluteFill} pointerEvents="box-none" testID={testID}>
      <Animated.View
        style={[
          styles.sheet,
          {
            height: resolvedExpandedHeight,
            transform: [{ translateY }],
          },
        ]}
      >
        <View {...panResponder.panHandlers} style={styles.handleArea}>
          <View style={styles.handle} />
        </View>
        <View style={styles.content}>{children}</View>
      </Animated.View>
    </View>
  );
}

const styles = StyleSheet.create({
  sheet: {
    position: "absolute",
    left: 0,
    right: 0,
    bottom: 0,
    backgroundColor: theme.surface,
    borderTopLeftRadius: radius.lg,
    borderTopRightRadius: radius.lg,
    ...theme.shadow,
  },
  handleArea: {
    alignItems: "center",
    paddingVertical: 8,
  },
  handle: {
    width: 36,
    height: 4,
    borderRadius: 2,
    backgroundColor: theme.border,
  },
  content: {
    flex: 1,
  },
});
