'use client'

import { emitter, sceneRegistry, useScene } from '@pascal-app/core'
import { useViewer } from '@pascal-app/viewer'
import { CameraControls, CameraControlsImpl } from '@react-three/drei'
import { useCallback, useEffect, useMemo, useRef } from 'react'
import { Box3, Vector3 } from 'three'

const tempBox = new Box3()
const tempCenter = new Vector3()
const tempSize = new Vector3()

export const ViewerCameraControls = () => {
  const controls = useRef<CameraControlsImpl>(null!)
  const selection = useViewer((s) => s.selection)
  const nodes = useScene((s) => s.nodes)
  const cameraMode = useViewer((s) => s.cameraMode)
  const firstLoad = useRef(true)

  // Get the deepest selected node ID (excluding selectedIds)
  const targetNodeId = selection.zoneId ?? selection.levelId ?? selection.buildingId

  // Configure mouse buttons - same as editor
  const mouseButtons = useMemo(() => {
    const wheelAction =
      cameraMode === 'orthographic'
        ? CameraControlsImpl.ACTION.ZOOM
        : CameraControlsImpl.ACTION.DOLLY

    return {
      left: CameraControlsImpl.ACTION.SCREEN_PAN,
      middle: CameraControlsImpl.ACTION.SCREEN_PAN,
      right: CameraControlsImpl.ACTION.ROTATE,
      wheel: wheelAction,
    }
  }, [cameraMode])

  useEffect(() => {
    if (!controls.current) return

    // On first load, set a default camera position
    if (firstLoad.current) {
      firstLoad.current = false
      controls.current.setLookAt(30, 30, 30, 0, 0, 0, false)
    }


    let node = targetNodeId ? nodes[targetNodeId] : null;
    if (!targetNodeId) {
      const site = Object.values(nodes).find((n) => n.type === 'site')
      node = site || null
    }
    if (!node) return

    // Check if node has a saved camera
    if (node.camera) {
      
      const { position, target } = node.camera
      requestAnimationFrame(() => {
        controls.current.setLookAt(
          position[0],
          position[1],
          position[2],
          target[0],
          target[1],
          target[2],
          true,
        )
      })
      return
    }
    if (!targetNodeId) {
      // No selection and no site - do nothing
      return
    }

    // Calculate camera position based on the node's 3D object
    const object3D = sceneRegistry.nodes.get(targetNodeId)
    if (!object3D) return

    // Compute bounding box
    tempBox.setFromObject(object3D)
    tempBox.getCenter(tempCenter)
    tempBox.getSize(tempSize)

    // Calculate a good viewing distance based on the object size
    const maxDim = Math.max(tempSize.x, tempSize.y, tempSize.z)
    const distance = Math.max(maxDim * 2, 15)

    // Position camera at an angle looking at the center
    const cameraPos = new Vector3(
      tempCenter.x + distance * 0.7,
      tempCenter.y + distance * 0.5,
      tempCenter.z + distance * 0.7,
    )

    controls.current.setLookAt(
      cameraPos.x,
      cameraPos.y,
      cameraPos.z,
      tempCenter.x,
      tempCenter.y,
      tempCenter.z,
      true,
    )
  }, [targetNodeId, nodes])

  useEffect(() => {
    const handleTopView = () => {
      if (!controls.current) return

      const currentPolarAngle = controls.current.polarAngle

      // Toggle: if already near top view (< 0.1 radians ≈ 5.7°), go back to 45°
      // Otherwise, go to top view (0°)
      const targetAngle = currentPolarAngle < 0.1 ? Math.PI / 4 : 0

      controls.current.rotatePolarTo(targetAngle, true)
    }

    const handleOrbitCW = () => {
      if (!controls.current) return

      const currentAzimuth = controls.current.azimuthAngle
      const currentPolar = controls.current.polarAngle
      // Round to nearest 90° increment, then rotate 90° clockwise
      const rounded = Math.round(currentAzimuth / (Math.PI / 2)) * (Math.PI / 2)
      const target = rounded - Math.PI / 2

      controls.current.rotateTo(target, currentPolar, true)
    }

    const handleOrbitCCW = () => {
      if (!controls.current) return

      const currentAzimuth = controls.current.azimuthAngle
      const currentPolar = controls.current.polarAngle
      // Round to nearest 90° increment, then rotate 90° counter-clockwise
      const rounded = Math.round(currentAzimuth / (Math.PI / 2)) * (Math.PI / 2)
      const target = rounded + Math.PI / 2

      controls.current.rotateTo(target, currentPolar, true)
    }

    emitter.on('camera-controls:top-view', handleTopView)
    emitter.on('camera-controls:orbit-cw', handleOrbitCW)
    emitter.on('camera-controls:orbit-ccw', handleOrbitCCW)

    return () => {
      emitter.off('camera-controls:top-view', handleTopView)
      emitter.off('camera-controls:orbit-cw', handleOrbitCW)
      emitter.off('camera-controls:orbit-ccw', handleOrbitCCW)
    }
  }, [])

  const onTransitionStart = useCallback(() => {
    useViewer.getState().setCameraDragging(true)
  }, [])

  const onRest = useCallback(() => {
    useViewer.getState().setCameraDragging(false)
  }, [])

  return (
    <CameraControls
      ref={controls}
      maxDistance={100}
      minDistance={5}
      maxPolarAngle={Math.PI / 2 - 0.1}
      minPolarAngle={0}
      mouseButtons={mouseButtons}
      onTransitionStart={onTransitionStart}
      onRest={onRest}
      restThreshold={0.01}
      
    />
  )
}
