import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { useSimulation } from '../useSimulation';

export function useCameraControls(camera: THREE.Camera, renderer: THREE.WebGLRenderer) {
    let controls: OrbitControls;

    // Camera Motion State
    let moveAnimating = false;
    let moveStart: THREE.Vector3 | null = null;
    let moveEnd: THREE.Vector3 | null = null;
    let contStart: THREE.Vector3 | null = null;
    let contEnd: THREE.Vector3 | null = null;
    let moveProgress = 0;
    const moveDuration = 0.2;
    let moveStartTime = 0;

    const { state } = useSimulation(); // Access global state

    const initControls = () => {
        controls = new OrbitControls(camera, renderer.domElement);
        controls.enableDamping = false;
        controls.dampingFactor = 0.05;
        controls.enableRotate = false;
        controls.enablePan = true;
        controls.enableZoom = false;
        controls.screenSpacePanning = false; // Fix: Pan on XZ plane only
        // Strictly match legacy controls.js
        controls.mouseButtons = {
            LEFT: THREE.MOUSE.PAN
        } as any; // Cast to allow partial object if TS complains, or just because we are overriding defaults
        controls.target.set(0, 0, 0);

        // Scroll Listeners
        controls.addEventListener('change', () => {
            if (moveAnimating) return;
            controls.target.y = 0;
            camera.position.y = Math.min(camera.position.y, state.value.maxZoom || 120);
            // This 'change' listener might fight with the zoom logic if not careful.
            // But let's just stick to the requested change: Zoom Out limit.
        });

        renderer.domElement.addEventListener('wheel', (event: WheelEvent) => {
            event.preventDefault();
            if (!moveAnimating) {
                const zoomAmount = state.value.zoomSensitivity || 5;
                const maxZoom = state.value.maxZoom || 120; // Default fallback

                // Calculate direction from target to camera (view vector)
                const dir = new THREE.Vector3().subVectors(camera.position, controls.target).normalize();
                let targetPos: THREE.Vector3 | null = null;

                if (event.deltaY < 0) {
                    // Zoom Out: Move away from target
                    targetPos = camera.position.clone().add(dir.multiplyScalar(zoomAmount));
                    // Check Max Limit
                    if (targetPos.y > maxZoom) {
                        // Clamp to max zoom height, but keep angle (roughly, or just stop)
                        // Simple stop:
                        targetPos = null;

                        // Or precise clamping (complex math to find point on vector where y=maxZoom):
                        // y = y0 + t * dir.y => maxZoom = y0 + t * dir.y => t = (maxZoom - y0) / dir.y
                        // But simply ignoring the input if it exceeds is safer/smoother for now.
                    }
                } else if (event.deltaY > 0) {
                    // Zoom In: Move towards target
                    targetPos = camera.position.clone().sub(dir.multiplyScalar(zoomAmount));
                    // Check Min Limit
                    if (targetPos.y < 10) {
                        targetPos = null;
                    }
                }

                if (targetPos) {
                    moveStart = camera.position.clone();
                    moveEnd = targetPos;
                    contStart = controls.target.clone();
                    contEnd = controls.target.clone(); // Target stays same
                    moveProgress = 0;
                    moveStartTime = performance.now() / 1000;
                    moveAnimating = true;
                }
            }
        }, { passive: false });
    };

    const updateControls = () => {
        // Animation
        if (moveAnimating && moveStart && moveEnd && contStart && contEnd) {
            const now = performance.now() / 1000;
            moveProgress = Math.min((now - moveStartTime) / moveDuration, 1);

            camera.position.lerpVectors(moveStart, moveEnd, moveProgress);
            controls.target.lerpVectors(contStart, contEnd, moveProgress);

            if (moveProgress >= 1) {
                moveAnimating = false;
            }
            controls.update();
        }

        if (controls) controls.update();
    };

    return {
        initControls,
        updateControls,
        getControls: () => controls
    };
}
