import { Environment, OrbitControls } from '@react-three/drei'
import React, { useEffect, useRef, useState } from 'react'
import { useFrame, useThree } from '@react-three/fiber'
import * as THREE from "three"
import { Space } from './Space'
import useStore from '@/lib/store'
import gsap from 'gsap'
import { Bloom, EffectComposer } from '@react-three/postprocessing'
import Chunks from './Chunk'
import starData from "./Chunk/data.json"
import { UNIT, CHUNK_SIZE } from "@/lib/constants"
import { offset } from './Chunk/chunkutils'
import usePlayerControls from '@/hooks/usePlayerControls'

export default function MyScene() {

  const {camera, scene} = useThree()

  const [raycaster] = useState(new THREE.Raycaster());
  const [pointer] = useState(new THREE.Vector2())
  const [bgAmbientMusic, setAmbientMusic] = useState<THREE.Audio | null>(null)

  const { forward, backward, left, right} = usePlayerControls()

  const focus = useStore((s)=>s.focus)
  const update = useStore((s)=>s.update)
  const choose = useStore((s) => s.choose)
  const muted = useStore((s) => s.muted)

  const chunkMap = starData.chunkMap

  const controlsRef :any = useRef(null);
  const focusRef :any = useRef(null);

  useEffect(()=>{
    camera.position.set(0, 50, 200)
    // Main audio logic init
    const listener = new THREE.AudioListener();
    camera.add(listener);
    const audioLoader = new THREE.AudioLoader();
    const bgAmbientMusic = new THREE.Audio(listener);
    const selectionSound = new THREE.Audio(listener);
    audioLoader.load('assets/audio/bg-ambient.mp3', function(buffer) {
      bgAmbientMusic.setBuffer(buffer);
      bgAmbientMusic.setVolume(1.0); // Set volume between 0 and 1
      bgAmbientMusic.loop = true;
    });
    audioLoader.load('assets/audio/selection-sound.mp3', function(buffer) {
      selectionSound.setBuffer(buffer);
      selectionSound.setVolume(0.4); 
    });
    setAmbientMusic(bgAmbientMusic)
    const controls = controlsRef.current;
    if(controls && focusRef.current)
      controls.target = focusRef.current.position
    
    const handleChange = () => {
      if(!update.flag){
        if(Number(controls.target.x.toFixed(0)) !== focus.x || Number(controls.target.y.toFixed(0)) !== focus.y || Number(controls.target.z.toFixed(0)) !== focus.z){
          useStore.setState({focus:new THREE.Vector3(Number(controls.target.x?.toFixed(0)), Number(controls.target.y?.toFixed(0)), Number(controls.target.z?.toFixed(0)))})
        }
      }
    };      
    const handleMouseMove = (event: MouseEvent) => {
      pointer.x = ( event.clientX / window.innerWidth ) * 2 - 1;
      pointer.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
    };
    const handleMouseClick = (event: MouseEvent) => {
      const px = ( event.clientX / window.innerWidth ) * 2 - 1;
      const py = - ( event.clientY / window.innerHeight ) * 2 + 1;
      raycaster.setFromCamera(new THREE.Vector2(px, py), camera);
      const network = scene.getObjectByName('network')
      if(network){
        const intersects = raycaster.intersectObjects(network.children);
        if(intersects.length>0 && intersects[ 0 ].object.type === "Mesh"){
          const data = intersects[0].object.userData
          data && useStore.setState({choose:data})
          if(selectionSound.isPlaying){
            selectionSound.stop();
          }
          selectionSound.play();
        }
      }
    };

    if(controls) {
      controls.addEventListener('end', handleChange);
    }
    window.addEventListener("mousemove", handleMouseMove);    
    window.addEventListener("click", handleMouseClick);    
    return () => {
      window.removeEventListener("mousemove", handleMouseMove);
      window.removeEventListener("click", handleMouseMove);
      if(controls)
        controls.removeEventListener('end', handleChange);
    };    
  },[])

  useEffect(()=>{
    if(bgAmbientMusic){
      if(muted){
        if(bgAmbientMusic.isPlaying){
          bgAmbientMusic.stop();
        }
      }else{
        if(!bgAmbientMusic.isPlaying){
          bgAmbientMusic.play();
        }
      }
    }
  },[muted, bgAmbientMusic])

  useFrame(({clock})=>{
    if(controlsRef.current){
      // const domElement = document.getElementById("update-flag")
      // if(domElement)
      //   domElement.innerHTML = `${String(update.flag)}, ${controlsRef.current.target.x}, ${controlsRef.current.target.y}, ${controlsRef.current.target.z}`
      // const target = controlsRef.current.target as THREE.Vector3;
      const element = scene.getObjectByName('space')
      element && element.position.set(controlsRef.current.target.x, controlsRef.current.target.y, controlsRef.current.target.z)
      const network = scene.getObjectByName('network')
      if(network){
        raycaster.setFromCamera( pointer, camera );
        
        const intersects = raycaster.intersectObjects( network.children );
        const wrapper = document.getElementById("canvas-wrapper");
        if ( intersects.length > 0 && intersects[ 0 ].object.type === "Mesh" && intersects[ 0 ].object.userData && wrapper) {
          wrapper.style.cursor = "pointer"
        } else {
          if(wrapper) wrapper.style.cursor = "default"
        }

        network.traverse((child:any)=>{
          const id = child.id
          const time = clock.getElapsedTime();
          if(child.type === "Line"){
            const flag = (choose && child.userData && choose?.constellation === child.userData.constellation)
            const v = flag?(Math.sin(time*2+id) * 0.25 + 0.7):(Math.sin(time * 2+id) * 0.1 + 0.1); // Range: 0 to 1
            child.material.color.setRGB(v, v, v)
          }else if(child.type === "Mesh"){
            const brightness = (choose && choose?.transaction_signature === child.name)
            ?(Math.sin(time*2+id) * 0.25 + 0.5):(Math.sin(time * 2 + id) * 0.1 - 0.1); // Range: 0 to 1          
            child.material?.emissive?.setScalar(brightness);
          }
        })
      }
      if(backward || forward){
        const direction = new THREE.Vector3()
        const frontVector = new THREE.Vector3(0, 0, Number(backward) - Number(forward))
        const sideVector = new THREE.Vector3(Number(left) - Number(right), 0, 0)
        direction.subVectors(frontVector, sideVector).normalize().multiplyScalar(10).applyEuler(camera.rotation)
        controlsRef.current.target.x += direction.x
        controlsRef.current.target.y += direction.y
        controlsRef.current.target.z += direction.z
        camera.position.x += direction.x
        camera.position.y += direction.y
        camera.position.z += direction.z
      }

    }
  })

  useEffect(()=>{
    if(controlsRef.current){
      // controlsRef.current.maxDistance = 70
      if(choose){
        const focusElement = scene.getObjectByName(choose.transaction_signature)
        if(focusElement){
          const pos = new THREE.Vector3()
          focusElement.getWorldPosition(pos)
          gsap.to(focusRef.current?.position, {x:pos.x, y:pos.y, z:pos.z, duration:2.0, ease: "power4.out"})
        }else{
          const transactionIndex = choose.transaction_id
          const constellationIndex = Math.floor((transactionIndex-1) / 5) 
          const chunkIndex = Math.floor((constellationIndex-1)/(CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE))+1
          let chunkKey =""
          for (const item of Object.keys(chunkMap)){
            if(chunkMap[item as keyof typeof chunkMap]===chunkIndex){
              chunkKey = item; break;
            }
          }          
          const [x, y, z] = chunkKey.split(",").map(Number);
          const cIndexInChunk = constellationIndex % 8
          const cx = Math.floor(cIndexInChunk/4), cy = Math.floor((cIndexInChunk%4)/2), cz = cIndexInChunk % 2
          const _size = UNIT/CHUNK_SIZE
          const _offset = offset(constellationIndex)
          const pos = new THREE.Vector3(
            x*UNIT + ((cx-0.5)*_size + _offset.x) + choose.x,
            y*UNIT + ((cy-0.5)*_size + _offset.y) + choose.y,
            z*UNIT + ((cz-0.5)*_size + _offset.z) + choose.z
          )//chunk     constellation
          gsap.to(focusRef.current?.position, {x:pos.x, y:pos.y, z:pos.z, duration:2.0, ease: "power4.out"})
          useStore.setState({focus:pos})
        }        
        gsap.to(controlsRef.current, {
          maxDistance: 30, // Target maxDistance value
          duration: 1, // Duration of the animation in seconds
          ease: "power2.out", // Optional easing function
          onUpdate: () => controlsRef.current?.update(), // Update the controls
        });
      }else{
        gsap.to(focusRef.current?.position, {x:0, y:0, z:0, duration:2.0})
        gsap.to(controlsRef.current, {
          maxDistance: 300, // Target maxDistance value
          duration: 1, // Duration of the animation in seconds
          ease: "power2.out", // Optional easing function
          onUpdate: () => controlsRef.current?.update(), // Update the controls
        });
      }
    }
  },[choose])

  return (
    <React.Fragment>
      <ambientLight color={"#aaaaaa"} intensity={5}/>
      <Chunks/>
      <Environment files={['assets/textures/starry.hdr']} background blur={0.35}/>
      <Space name='space'/>
      <mesh name='focus' ref={focusRef} visible={false}>
        <boxGeometry args={[2, 2, 2]}/>
        <meshBasicMaterial transparent opacity={0.1}/>
      </mesh>
      <OrbitControls ref={controlsRef}
        makeDefault
        target0={new THREE.Vector3(3, 0, 0)}
        enablePan = {true}
        enableRotate = {true}
        enableZoom = {true}
        panSpeed = {0.5}
        minDistance={1}
        maxDistance={300}
        autoRotate={false}
        autoRotateSpeed={-0.5}
        // keys = {{
        //   LEFT: 'ArrowLeft', //left arrow
        //   UP: 'ArrowUp', // up arrow
        //   RIGHT: 'ArrowRight', // right arrow
        //   BOTTOM: 'ArrowDown' // down arrow
        // }}
        // keyPanSpeed={100}
      />
      {/* <axesHelper args={[100]} position={[-200, 0, 0]}/>
      <axesHelper args={[100]} position={[-100, 0, 0]}/>
      <axesHelper args={[100]} position={[0, 0, 0]}/>
      <axesHelper args={[100]} position={[100, 0, 0]}/>
      <axesHelper args={[100]} position={[200, 0, 0]}/>
      <axesHelper args={[100]} position={[0, -200, 0]}/>
      <axesHelper args={[100]} position={[0, -100, 0]}/>
      <axesHelper args={[100]} position={[0, 0, 0]}/>
      <axesHelper args={[100]} position={[0, 100, 0]}/>
      <axesHelper args={[100]} position={[0, 200, 0]}/> */}
      <EffectComposer>
        <Bloom
          luminanceThreshold={0.05}
          luminanceSmoothing={0.2}
          intensity={5}
          radius={0.01}
        />
      </EffectComposer>
    </React.Fragment>
  )
}