import { ReactNode, useEffect, useRef, useState } from 'react';
import {
  Animated,
  Dimensions,
  Keyboard,
  Modal,
  PanResponder,
  ScrollView,
  Text,
  TouchableOpacity,
  View,
} from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { useKeyboardHandler } from 'react-native-keyboard-controller';
import { runOnJS } from 'react-native-reanimated';
import { useColors } from '../lib/theme';

/**
 * App-weites Bottom-Sheet — DAS eine Pattern für alle Custom-Modals.
 *
 *  - **Default = Auto-Fit**: Sheet misst seinen Inhalt (interner ScrollView via
 *    `onContentSizeChange`) und wird genau so hoch wie nötig. Reicht der Cap
 *    nicht, scrollt der Inhalt intern.
 *  - **Cap**: SCREEN_H − statusBar − `navHeaderOffset` (default 56dp). So
 *    überschreitet das Sheet niemals den App-Nav-Header.
 *  - **Legacy-Mode**: Wer `initialHeightPct` setzt, bekommt das alte
 *    Fixed-Pct-Layout mit `<View flex:1>` children-wrapper (backwards-compat).
 *  - **Keyboard (iOS + Android)**: `useKeyboardHandler` aus
 *    `react-native-keyboard-controller` liefert den Modal-aware nativen
 *    Keyboard-Frame. Sheet wächst um `keyboardHeight` (gedeckelt),
 *    `paddingBottom: keyboardHeight` schiebt Inhalt exakt über die Tastatur —
 *    kein Doppel-Compensation auf Android (das war der Bug mit manuellem
 *    `Keyboard.addListener`-Pattern, da RN-Modal `adjustResize` ignoriert).
 *  - **Resize per Drag**: Grabber/Header sind drag-area → User kann größer
 *    ziehen (bis Max) oder zum dismissen runterswipen.
 *
 * Driver-Trennung (sonst „Style property 'height' is not supported by native
 * animated module"-Crash): äußere View animiert `height` im JS-Driver, innere
 * View animiert `transform: translateY` (Slide/Dismiss) im Native-Driver.
 */

const DRAG_FLICK_VELOCITY = 1.5;
const DEFAULT_NAV_HEADER_OFFSET = 56;
// Grabber (8 + 5 + 6) + Header (4 + ~20 + 12 + 1) = ~56. Etwas Puffer.
const CHROME_HEIGHT = 60;

export interface FormSheetProps {
  visible: boolean;
  onClose: () => void;
  /** Titel links im Header. */
  title: string;
  children: ReactNode;
  /**
   * Wenn gesetzt → Legacy-Fixed-Pct-Mode (alter `<View flex:1>` children-wrap).
   * Ohne diesen Prop läuft Auto-Fit: Sheet wächst genau auf Content-Höhe.
   */
  initialHeightPct?: number;
  /** Drag-down unter diesen Anteil (oder Flick) → dismiss. Default 0.25. */
  minHeightPct?: number;
  /**
   * Pixel-Offset unter dem Status-Bar, bei dem das Sheet aufhört zu wachsen.
   * Default 56 — Standard-App-Nav-Header (Material/iOS). 0 = darf bis zur
   * Status-Bar gehen.
   */
  navHeaderOffset?: number;
  /** Backdrop-Deckkraft (0 = kein Dim). Default 0.12. */
  backdropOpacity?: number;
  /** Default true — Tap auf Backdrop schließt das Sheet. */
  dismissOnBackdrop?: boolean;
  /** Default true — fügt unten einen Safe-Area-Spacer ein wenn die Tastatur zu ist. */
  safeAreaBottom?: boolean;
  /** Default true — Sheet wächst mit der Tastatur (Inputs bleiben sichtbar). */
  growWithKeyboard?: boolean;
  /** Border-Radius oben. Default 24. */
  topRadius?: number;
}

export function FormSheet({
  visible,
  onClose,
  title,
  children,
  initialHeightPct,
  minHeightPct = 0.25,
  navHeaderOffset = DEFAULT_NAV_HEADER_OFFSET,
  backdropOpacity = 0.12,
  dismissOnBackdrop = true,
  safeAreaBottom = true,
  growWithKeyboard = true,
  topRadius = 24,
}: FormSheetProps) {
  const colors = useColors();
  const insets = useSafeAreaInsets();
  // Dimensions.get('screen') = physische Screen-Höhe, statisch, ignoriert
  // Keyboard-Resize auf Android. useWindowDimensions würde live schrumpfen
  // wenn Keyboard auf und Activity adjustResize macht → maxHeight kollabiert →
  // Sheet kann nicht über den Keyboard-Bereich wachsen.
  const SCREEN_H = Dimensions.get('screen').height;

  const autoMode = initialHeightPct === undefined;

  // Cap: nicht über den App-Header. 200px Mindest-Cap als Fallback.
  const maxHeight = Math.max(200, SCREEN_H - insets.top - navHeaderOffset);

  // Startwert: Auto → kleiner Platzhalter bis onContentSizeChange misst.
  // Legacy → der vom Caller gesetzte Pct-Wert.
  const fallbackInitial = autoMode
    ? Math.min(SCREEN_H * 0.35, maxHeight)
    : Math.min(SCREEN_H * (initialHeightPct ?? 0.5), maxHeight);

  const dismissHeight = SCREEN_H * minHeightPct;

  const sheetHeight = useRef(new Animated.Value(fallbackInitial)).current; // JS driver
  const dismissY = useRef(new Animated.Value(0)).current; // native driver
  const currentHeight = useRef(fallbackInitial); // letzte „Ruhe"-Höhe
  const keyboardHeightRef = useRef(0);
  const userDraggedRef = useRef(false); // sobald user manuell zieht, kein Auto-Re-Fit mehr
  const [keyboardHeight, setKeyboardHeight] = useState(0);

  // Reset bei (Wieder-)Öffnen
  useEffect(() => {
    if (visible) {
      // keyboardHeight reset: applyKeyboardHeight hat `if (!visible) return`,
      // also kommt das `h=0`-Hide-Event beim Schließen NIE durch → ohne reset
      // öffnet das Sheet beim 2. Mal mit altem paddingBottom.
      setKeyboardHeight(0);
      keyboardHeightRef.current = 0;
      sheetHeight.setValue(fallbackInitial);
      dismissY.setValue(0);
      currentHeight.current = fallbackInitial;
      userDraggedRef.current = false;
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [visible]);

  const handleClose = () => {
    Keyboard.dismiss();
    sheetHeight.setValue(fallbackInitial);
    dismissY.setValue(0);
    currentHeight.current = fallbackInitial;
    userDraggedRef.current = false;
    onClose();
  };

  // Auto-Fit: ScrollView meldet seine natürliche Content-Höhe.
  const onContentSize = (_w: number, h: number) => {
    if (!autoMode || userDraggedRef.current) return;
    const safeArea = safeAreaBottom ? insets.bottom : 0;
    const target = Math.min(h + CHROME_HEIGHT + safeArea, maxHeight);
    if (Math.abs(target - currentHeight.current) < 4) return;
    currentHeight.current = target;
    Animated.timing(sheetHeight, {
      toValue: Math.min(target + keyboardHeightRef.current, maxHeight),
      duration: 180,
      useNativeDriver: false,
    }).start();
  };

  // Keyboard: react-native-keyboard-controller liefert reliable native frame
  // (Modal-aware auf Android — kein adjustResize-Doppel-Compensation-Bug).
  // Wir setzen state → Animated.Value für Sheet-Höhe + paddingBottom-Anker.
  const applyKeyboardHeight = (h: number) => {
    // Hook feuert global — nur reagieren wenn dieses Sheet sichtbar ist,
    // sonst rumpelt's mit Keyboard-Events anderer Screens.
    if (!visible) return;
    keyboardHeightRef.current = h;
    setKeyboardHeight(h);
    if (!growWithKeyboard) return;
    Animated.timing(sheetHeight, {
      toValue: Math.min(currentHeight.current + h, maxHeight),
      duration: 220,
      useNativeDriver: false,
    }).start();
  };

  useKeyboardHandler({
    onStart: (e) => {
      'worklet';
      runOnJS(applyKeyboardHeight)(e.height);
    },
    onEnd: (e) => {
      'worklet';
      runOnJS(applyKeyboardHeight)(e.height);
    },
  });

  const panResponder = useRef(
    PanResponder.create({
      onStartShouldSetPanResponder: () => true,
      onMoveShouldSetPanResponder: () => true,
      onPanResponderTerminationRequest: () => false,
      onPanResponderMove: (_, g) => {
        const base = currentHeight.current + keyboardHeightRef.current;
        const next = base - g.dy;
        sheetHeight.setValue(Math.max(dismissHeight - 60, Math.min(maxHeight + 16, next)));
      },
      onPanResponderRelease: (_, g) => {
        const base = currentHeight.current + keyboardHeightRef.current;
        const finalH = base - g.dy;
        const v = g.vy;
        if (finalH < dismissHeight || v > DRAG_FLICK_VELOCITY) {
          Animated.timing(dismissY, {
            toValue: SCREEN_H,
            duration: 200,
            useNativeDriver: true,
          }).start(() => handleClose());
          return;
        }
        let target = finalH;
        if (v < -DRAG_FLICK_VELOCITY) target = maxHeight;
        const clamped = Math.max(SCREEN_H * minHeightPct, Math.min(maxHeight, target));
        Animated.spring(sheetHeight, {
          toValue: clamped,
          useNativeDriver: false,
          friction: 9,
          tension: 70,
        }).start();
        currentHeight.current = Math.max(0, clamped - keyboardHeightRef.current);
        userDraggedRef.current = true; // ab jetzt Auto-Re-Fit ignorieren
      },
    }),
  ).current;

  const dragHandlers = panResponder.panHandlers;

  return (
    <Modal visible={visible} transparent animationType="slide" onRequestClose={handleClose} statusBarTranslucent>
      {/* Backdrop */}
      <TouchableOpacity
        activeOpacity={1}
        onPress={dismissOnBackdrop ? handleClose : undefined}
        style={{ flex: 1, backgroundColor: `rgba(0,0,0,${backdropOpacity})` }}
      />

      {/* Outer: animated height (JS driver) */}
      <Animated.View
        style={{ position: 'absolute', bottom: 0, left: 0, right: 0, height: sheetHeight }}
      >
        {/* Inner: animated transform (native driver) — getrennt, kein Driver-Mix */}
        <Animated.View
          style={{
            flex: 1,
            backgroundColor: colors.bg,
            borderTopLeftRadius: topRadius,
            borderTopRightRadius: topRadius,
            overflow: 'hidden',
            // iOS + Android beide: Modal-Window honoriert keyboard-resize nicht
            // zuverlässig, also manuell padden damit Inputs über der Tastatur sitzen.
            paddingBottom: keyboardHeight,
            transform: [{ translateY: dismissY }],
            shadowColor: '#000',
            shadowOffset: { width: 0, height: -2 },
            shadowOpacity: 0.08,
            shadowRadius: 8,
          }}
        >
          {/* Grabber-Bar (mittig, drag-area) — paddingY für 44pt-Hit-Area */}
          <View
            {...dragHandlers}
            style={{ alignItems: 'center', paddingTop: 14, paddingBottom: 12 }}
          >
            <View style={{ width: 42, height: 6, borderRadius: 3, backgroundColor: colors.border }} />
          </View>

          {/* Header: Titel links — keine Buttons. Auch drag-area. */}
          <View
            {...dragHandlers}
            style={{
              paddingHorizontal: 20,
              paddingTop: 4,
              paddingBottom: 12,
              borderBottomWidth: 1,
              borderBottomColor: colors.border,
            }}
          >
            <Text style={{ fontSize: 16, fontFamily: 'Nunito_700Bold', color: colors.text }}>
              {title}
            </Text>
          </View>

          {/* Inhalt */}
          {autoMode ? (
            <ScrollView
              style={{ flex: 1 }}
              keyboardShouldPersistTaps="handled"
              showsVerticalScrollIndicator={false}
              onContentSizeChange={onContentSize}
            >
              {children}
            </ScrollView>
          ) : (
            <View style={{ flex: 1 }}>{children}</View>
          )}

          {/* Safe-Area-Spacer (nur wenn Tastatur zu) */}
          {safeAreaBottom && <View style={{ height: keyboardHeight > 0 ? 0 : insets.bottom }} />}
        </Animated.View>
      </Animated.View>
    </Modal>
  );
}
