/**
 * ZoomableImage — Pinch-to-Zoom + Pan fuers Vollbild-Modal.
 *
 * Reine RN-Implementation, ohne react-native-gesture-handler.
 *
 *  - 2 Finger: Pinch (Zoom 1x..5x) + simultaner Pan via Focal-Punkt
 *  - 1 Finger: Pan wenn schon gezoomt (>1.02x)
 *  - Doppel-Tap (<300ms zw. zwei Single-Taps): Toggle 1x ↔ 2.5x
 *
 * Implementierungs-Hinweise zur alten Version (warum's nicht ging):
 *  - `gestureState.numberActiveTouches` ist nicht zuverlaessig direkt
 *    nach onPanResponderGrant. Wir lesen Finger-Anzahl jetzt
 *    ausschliesslich aus `e.nativeEvent.touches.length`.
 *  - Beim Wechsel von 2 → 1 Fingern bleib die Pinch-Referenz haengen.
 *    Jetzt: bei jedem Finger-Wechsel re-snapshotten wir die Geste.
 *  - Animated.Image bekommt jetzt pointerEvents="none" damit der View
 *    GARANTIERT die Touches abbekommt.
 *  - useNativeDriver ist bewusst AUS — sonst koennen wir setValue()
 *    nicht synchron mit dem Pan-Responder zusammen nutzen.
 */

import React, { useMemo, useRef } from 'react';
import {
  Animated,
  PanResponder,
  GestureResponderEvent,
  ImageStyle,
  StyleProp,
  StyleSheet,
  View,
} from 'react-native';

interface Props {
  uri: string;
  containerWidth: number;
  containerHeight: number;
  style?: StyleProp<ImageStyle>;
}

const MIN_SCALE = 1;
const MAX_SCALE = 5;
const DOUBLE_TAP_MS = 300;
const DOUBLE_TAP_DIST = 30;     // Bewegung max. damit ein Tap als Tap gilt
const PAN_SLOP_AT_SCALE_1 = 4;  // Mikro-Movement nicht als Pan werten

const ZoomableImage: React.FC<Props> = ({ uri, containerWidth, containerHeight, style }) => {
  // Animated-Werte fuer die Render-Transformation
  const scale = useRef(new Animated.Value(1)).current;
  const tx = useRef(new Animated.Value(0)).current;
  const ty = useRef(new Animated.Value(0)).current;

  // Logische Zustaende — wir lesen Animated.Value nicht zurueck (waere async)
  const view = useRef({ scale: 1, x: 0, y: 0 }).current;

  // Geste-Snapshot: was war zu Beginn dieser Geste-Phase
  const gesture = useRef({
    fingers: 0,                // aktuelle Finger-Anzahl
    startScale: 1,
    startX: 0,
    startY: 0,
    startDist: 0,              // Pinch-Referenz-Distanz
    startFocalX: 0,
    startFocalY: 0,
    movedSinceTouch: 0,        // fuer Tap-Erkennung
    touchStartedAt: 0,
    touchStartX: 0,
    touchStartY: 0,
  }).current;

  // Doppel-Tap
  const lastTap = useRef({ at: 0, x: 0, y: 0 });

  const clamp = (v: number, lo: number, hi: number) => Math.max(lo, Math.min(hi, v));

  const applyClamped = (s: number, x: number, y: number) => {
    const ns = clamp(s, MIN_SCALE, MAX_SCALE);
    // Translation auf das verfuegbare Volumen begrenzen
    const maxX = Math.max(0, (containerWidth * ns - containerWidth) / 2);
    const maxY = Math.max(0, (containerHeight * ns - containerHeight) / 2);
    const nx = clamp(x, -maxX, maxX);
    const ny = clamp(y, -maxY, maxY);
    view.scale = ns;
    view.x = nx;
    view.y = ny;
    scale.setValue(ns);
    tx.setValue(nx);
    ty.setValue(ny);
  };

  const distance = (touches: any[]) =>
    Math.hypot(touches[0].pageX - touches[1].pageX, touches[0].pageY - touches[1].pageY);

  const focal = (touches: any[]) => ({
    x: (touches[0].pageX + touches[1].pageX) / 2,
    y: (touches[0].pageY + touches[1].pageY) / 2,
  });

  // Snapshot vor jedem Phasenwechsel (1↔2 Finger) — verhindert Spruenge
  const snapshot = (touches: any[]) => {
    gesture.startScale = view.scale;
    gesture.startX = view.x;
    gesture.startY = view.y;
    if (touches.length >= 2) {
      gesture.startDist = distance(touches);
      const f = focal(touches);
      gesture.startFocalX = f.x;
      gesture.startFocalY = f.y;
    } else if (touches.length === 1) {
      gesture.startDist = 0;
      gesture.startFocalX = touches[0].pageX;
      gesture.startFocalY = touches[0].pageY;
    }
  };

  const responder = useMemo(
    () =>
      PanResponder.create({
        onStartShouldSetPanResponder: () => true,
        onStartShouldSetPanResponderCapture: () => true,
        onMoveShouldSetPanResponder: () => true,
        onMoveShouldSetPanResponderCapture: () => true,

        onPanResponderGrant: (e: GestureResponderEvent) => {
          const touches = e.nativeEvent.touches as any[];
          gesture.fingers = touches.length;
          gesture.movedSinceTouch = 0;
          gesture.touchStartedAt = Date.now();
          gesture.touchStartX = touches[0]?.pageX ?? 0;
          gesture.touchStartY = touches[0]?.pageY ?? 0;
          snapshot(touches);
        },

        onPanResponderMove: (e: GestureResponderEvent, _gs) => {
          const touches = e.nativeEvent.touches as any[];

          // Phasenwechsel? → Re-Snapshot, damit nicht gesprungen wird
          if (touches.length !== gesture.fingers) {
            gesture.fingers = touches.length;
            snapshot(touches);
            return;
          }

          gesture.movedSinceTouch += 1;

          if (touches.length >= 2) {
            // Pinch + Pan via Focal
            const d = distance(touches);
            if (gesture.startDist === 0) {
              // Sicherheitsnetz falls Snapshot gemissed wurde
              snapshot(touches);
              return;
            }
            const factor = d / gesture.startDist;
            const f = focal(touches);
            const newScale = clamp(gesture.startScale * factor, MIN_SCALE, MAX_SCALE);
            // Focal-basierter Pan: zoomt um den Mittelpunkt der zwei Finger
            const newX = gesture.startX + (f.x - gesture.startFocalX);
            const newY = gesture.startY + (f.y - gesture.startFocalY);
            applyClamped(newScale, newX, newY);
          } else if (touches.length === 1 && view.scale > 1.02) {
            const dx = touches[0].pageX - gesture.startFocalX;
            const dy = touches[0].pageY - gesture.startFocalY;
            if (Math.abs(dx) < PAN_SLOP_AT_SCALE_1 && Math.abs(dy) < PAN_SLOP_AT_SCALE_1) return;
            applyClamped(view.scale, gesture.startX + dx, gesture.startY + dy);
          }
        },

        onPanResponderRelease: (e: GestureResponderEvent) => {
          const elapsed = Date.now() - gesture.touchStartedAt;
          const dx = (e.nativeEvent.changedTouches?.[0]?.pageX ?? gesture.touchStartX) - gesture.touchStartX;
          const dy = (e.nativeEvent.changedTouches?.[0]?.pageY ?? gesture.touchStartY) - gesture.touchStartY;
          const wasTap =
            elapsed < 280 &&
            Math.abs(dx) < DOUBLE_TAP_DIST &&
            Math.abs(dy) < DOUBLE_TAP_DIST;
          if (wasTap) {
            const now = Date.now();
            if (now - lastTap.current.at < DOUBLE_TAP_MS) {
              // Doppel-Tap → Zoom-Toggle
              if (view.scale > 1.1) {
                applyClamped(1, 0, 0);
              } else {
                applyClamped(2.5, 0, 0);
              }
              lastTap.current = { at: 0, x: 0, y: 0 };
            } else {
              lastTap.current = { at: now, x: gesture.touchStartX, y: gesture.touchStartY };
            }
          }
          gesture.fingers = 0;
          gesture.startDist = 0;
        },

        onPanResponderTerminate: () => {
          gesture.fingers = 0;
          gesture.startDist = 0;
        },
      }),
    [],
  );

  return (
    <View
      style={StyleSheet.absoluteFill}
      collapsable={false}
      {...responder.panHandlers}
    >
      <Animated.View pointerEvents="none" style={StyleSheet.absoluteFill}>
        <Animated.Image
          source={{ uri }}
          style={[
            style,
            {
              transform: [{ translateX: tx }, { translateY: ty }, { scale }],
            },
          ]}
          resizeMode="contain"
        />
      </Animated.View>
    </View>
  );
};

export default ZoomableImage;
