'use client'

import { useCallback, useRef, useState } from 'react';

export interface PinchZoomState {
  scale: number
  x: number
  y: number
}

export interface UsePinchZoomOptions {
  minScale?: number
  maxScale?: number
  onZoomChange?: (zoomed: boolean) => void
}

export interface UsePinchZoomReturn {
  state: PinchZoomState
  isZoomed: boolean
  handlers: {
    onTouchStart: (e: React.TouchEvent) => void
    onTouchMove: (e: React.TouchEvent) => void
    onTouchEnd: (e: React.TouchEvent) => void
  }
  reset: () => void
  zoomTo: (scale: number, x?: number, y?: number) => void
}

const INITIAL_STATE: PinchZoomState = { scale: 1, x: 0, y: 0 }

/**
 * Hook for pinch-to-zoom functionality on touch devices
 */
export function usePinchZoom(options: UsePinchZoomOptions = {}): UsePinchZoomReturn {
  const { minScale = 1, maxScale = 3, onZoomChange } = options

  const [state, setState] = useState<PinchZoomState>(INITIAL_STATE)

  // Track touch state and container size
  const touchRef = useRef<{
    initialDistance: number
    initialScale: number
    initialX: number
    initialY: number
    centerX: number
    centerY: number
    isPinching: boolean
    lastTap: number
    containerWidth: number
    containerHeight: number
  }>({
    initialDistance: 0,
    initialScale: 1,
    initialX: 0,
    initialY: 0,
    centerX: 0,
    centerY: 0,
    isPinching: false,
    lastTap: 0,
    containerWidth: 0,
    containerHeight: 0,
  })

  const isZoomed = state.scale > 1.05

  // Calculate distance between two touch points
  const getDistance = useCallback((touches: React.TouchList): number => {
    if (touches.length < 2) return 0
    const dx = touches[0].clientX - touches[1].clientX
    const dy = touches[0].clientY - touches[1].clientY
    return Math.sqrt(dx * dx + dy * dy)
  }, [])

  // Get center point between two touches
  const getCenter = useCallback((touches: React.TouchList): { x: number; y: number } => {
    if (touches.length < 2) {
      return { x: touches[0].clientX, y: touches[0].clientY }
    }
    return {
      x: (touches[0].clientX + touches[1].clientX) / 2,
      y: (touches[0].clientY + touches[1].clientY) / 2,
    }
  }, [])

  // Calculate max pan based on container size and scale
  const getMaxPan = useCallback((scale: number) => {
    const { containerWidth, containerHeight } = touchRef.current
    // When zoomed, allow panning up to half the "extra" size
    // At 2x zoom on 400px container: can pan 200px in each direction
    return {
      x: (containerWidth * (scale - 1)) / 2,
      y: (containerHeight * (scale - 1)) / 2,
    }
  }, [])

  const handleTouchStart = useCallback((e: React.TouchEvent) => {
    const touches = e.touches

    // Store container dimensions from the element
    const rect = e.currentTarget.getBoundingClientRect()
    touchRef.current.containerWidth = rect.width
    touchRef.current.containerHeight = rect.height

    // Double tap to zoom
    if (touches.length === 1) {
      const now = Date.now()
      const timeSinceLastTap = now - touchRef.current.lastTap

      if (timeSinceLastTap < 300) {
        // Double tap detected
        e.preventDefault()
        if (isZoomed) {
          setState(INITIAL_STATE)
          onZoomChange?.(false)
        } else {
          const x = touches[0].clientX - rect.left - rect.width / 2
          const y = touches[0].clientY - rect.top - rect.height / 2
          setState({ scale: 2, x: -x * 0.5, y: -y * 0.5 })
          onZoomChange?.(true)
        }
        touchRef.current.lastTap = 0
        return
      }

      touchRef.current.lastTap = now

      // Single finger - start pan (if zoomed)
      if (isZoomed) {
        touchRef.current.initialX = touches[0].clientX - state.x
        touchRef.current.initialY = touches[0].clientY - state.y
      }
    }

    // Two finger pinch
    if (touches.length === 2) {
      e.preventDefault()
      touchRef.current.isPinching = true
      touchRef.current.initialDistance = getDistance(touches)
      touchRef.current.initialScale = state.scale

      const center = getCenter(touches)
      touchRef.current.centerX = center.x
      touchRef.current.centerY = center.y
      touchRef.current.initialX = state.x
      touchRef.current.initialY = state.y
    }
  }, [isZoomed, state, getDistance, getCenter, onZoomChange])

  const handleTouchMove = useCallback((e: React.TouchEvent) => {
    const touches = e.touches

    // Pinch zoom
    if (touches.length === 2 && touchRef.current.isPinching) {
      e.preventDefault()

      const currentDistance = getDistance(touches)
      const scaleDelta = currentDistance / touchRef.current.initialDistance
      let newScale = touchRef.current.initialScale * scaleDelta

      // Clamp scale
      newScale = Math.max(minScale, Math.min(maxScale, newScale))

      // Calculate new position to zoom toward center
      const center = getCenter(touches)
      const dx = center.x - touchRef.current.centerX
      const dy = center.y - touchRef.current.centerY

      // Apply pan limits
      const maxPan = getMaxPan(newScale)
      const newX = Math.max(-maxPan.x, Math.min(maxPan.x, touchRef.current.initialX + dx))
      const newY = Math.max(-maxPan.y, Math.min(maxPan.y, touchRef.current.initialY + dy))

      setState({
        scale: newScale,
        x: newX,
        y: newY,
      })

      if (newScale > 1.05 !== isZoomed) {
        onZoomChange?.(newScale > 1.05)
      }
    }

    // Pan when zoomed (single finger)
    if (touches.length === 1 && isZoomed && !touchRef.current.isPinching) {
      e.preventDefault()

      const maxPan = getMaxPan(state.scale)
      const newX = touches[0].clientX - touchRef.current.initialX
      const newY = touches[0].clientY - touchRef.current.initialY

      setState((prev) => ({
        ...prev,
        x: Math.max(-maxPan.x, Math.min(maxPan.x, newX)),
        y: Math.max(-maxPan.y, Math.min(maxPan.y, newY)),
      }))
    }
  }, [isZoomed, state.scale, minScale, maxScale, getDistance, getCenter, getMaxPan, onZoomChange])

  const handleTouchEnd = useCallback((e: React.TouchEvent) => {
    if (e.touches.length < 2) {
      touchRef.current.isPinching = false
    }

    // Snap to 1 if close
    if (state.scale < 1.1 && state.scale > 0.9) {
      setState(INITIAL_STATE)
      onZoomChange?.(false)
    }
  }, [state.scale, onZoomChange])

  const reset = useCallback(() => {
    setState(INITIAL_STATE)
    onZoomChange?.(false)
  }, [onZoomChange])

  const zoomTo = useCallback((scale: number, x = 0, y = 0) => {
    const clampedScale = Math.max(minScale, Math.min(maxScale, scale))
    setState({ scale: clampedScale, x, y })
    onZoomChange?.(clampedScale > 1.05)
  }, [minScale, maxScale, onZoomChange])

  return {
    state,
    isZoomed,
    handlers: {
      onTouchStart: handleTouchStart,
      onTouchMove: handleTouchMove,
      onTouchEnd: handleTouchEnd,
    },
    reset,
    zoomTo,
  }
}
