import { useEffect, useRef } from "react";
import * as THREE from 'three';
import { useInteraction, type InteractionMouseEventArgs } from "../helpers/hooks/useInteration";
import { db } from "../backend/db";
import { HitTestFactory, type HitTest } from "../helpers/hitTest";
import { model } from "../model/model";
import { SceneHelper } from "../helpers/sceneHelper";

export type ThreeViewEventArgs = {
    camera: THREE.Camera,
    scene: THREE.Scene,
}

export type ThreeViewTickEventArgs = ThreeViewEventArgs & {
    deltaTime: number,
    absoluteTime: number,
}

export type ThreeViewMouseEventArgs = ThreeViewEventArgs & {
    hitTest: HitTest,
}

export type ThreeViewProps = {
    sceneHelper: SceneHelper,
    onTick?: (event: ThreeViewTickEventArgs) => void,
    onClick?: (event: ThreeViewMouseEventArgs) => void,
    onMouseMove?: (event: ThreeViewMouseEventArgs) => void,
    onDispose: (event: ThreeViewEventArgs) => void,
}

function setupScene(size: { w: number, h: number }): { scene: THREE.Scene, camera: THREE.PerspectiveCamera } {
    // --- Scene & Camera ---
    const scene = new THREE.Scene();
    scene.background = new THREE.Color(0x0a0a12);

    const camera = new THREE.PerspectiveCamera(55, size.w / size.h, 0.1, 100);
    camera.position.set(4, 3, 6);

    // --- Lights ---
    scene.add(new THREE.AmbientLight(0xffffff, 0.3));

    const dir = new THREE.DirectionalLight(0xffffff, 1.2);
    dir.position.set(5, 8, 5);
    dir.castShadow = true;
    scene.add(dir);

    const pt = new THREE.PointLight(0x5588ff, 1.5, 20);
    pt.position.set(-3, 2, -3);
    scene.add(pt);

    // --- Floor & Grid ---
    const plane = new THREE.Mesh(
        new THREE.PlaneGeometry(14, 14),
        new THREE.MeshStandardMaterial({ color: 0x080810 })
    );
    plane.rotation.x = -Math.PI / 2;
    plane.receiveShadow = true;
    // scene.add(plane);
    // scene.add(new THREE.GridHelper(14, 14, 0x222233, 0x111122));

    return { scene, camera };
}

export const ThreeView = function (props: ThreeViewProps) {

    const viewportRef = useRef<HTMLDivElement | null>(null);
    const canvasRef = useRef<HTMLCanvasElement | null>(null);
    const cameraRef = useRef<THREE.PerspectiveCamera | null>(null);

    let handleClick: (e: InteractionMouseEventArgs) => void;
    let handleHover: (e: InteractionMouseEventArgs) => void;

    useEffect(() => {

        model.initFromBlob(db.saveToBlob());

        const container = viewportRef.current!;
        const W = container.clientWidth;
        const H = container.clientHeight;

        // --- Renderer ---
        const renderer = new THREE.WebGLRenderer({ antialias: true });
        renderer.setPixelRatio(window.devicePixelRatio);
        renderer.setSize(W, H);
        renderer.shadowMap.enabled = true;
        container.appendChild(renderer.domElement);

        canvasRef.current = renderer.domElement;

        const { scene, camera } = setupScene({ w: W, h: H });
        cameraRef.current = camera;

        props.sceneHelper.initialize(scene, camera);

        const handleWindowResize = () => {
            const w = container.clientWidth;
            const h = container.clientHeight;
            camera.aspect = w / h;
            camera.updateProjectionMatrix();
            renderer.setSize(w, h);
        };
        window.addEventListener("resize", handleWindowResize);

        handleHover = (e: InteractionMouseEventArgs) => {

            const ht = props.sceneHelper.hitTest(
                e.position,
                e.screenSize,
            );
            console.log(JSON.stringify(ht.map((h) => h.object.userData)));

            const hitTest = HitTestFactory.hitTest(
                props.sceneHelper,
                new THREE.Vector2(e.position.x, e.position.y),
                camera,
                { tolerancePixels: 3, cameraPixelSize: e.pixelSize }
            );
            props.onMouseMove?.({
                camera,
                scene,
                hitTest,
            });
        };

        handleClick = (e: InteractionMouseEventArgs) => {
            const hitTest = HitTestFactory.hitTest(
                props.sceneHelper,
                new THREE.Vector2(e.position.x, e.position.y),
                camera,
                { tolerancePixels: 3, cameraPixelSize: e.pixelSize }
            );
            props.onClick?.({
                camera,
                scene,
                hitTest,
            })
        };

        // --- Animation loop ---
        let lastTime = performance.now();
        let animId: number;
        function animate(time: DOMHighResTimeStamp) {
            animId = requestAnimationFrame(animate);
            const deltaTime = lastTime ? time - lastTime : 0;
            lastTime = time;
            props.onTick?.({
                camera,
                scene,
                deltaTime,
                absoluteTime: time,
            });
            renderer.render(scene, camera);
        }
        animId = requestAnimationFrame(animate);

        // --- Cleanup ---
        return () => {
            if (animId)
                cancelAnimationFrame(animId);

            container.removeChild(renderer.domElement);

            window.removeEventListener("resize", handleWindowResize);

            renderer.dispose();

            props.onDispose({
                camera,
                scene,
            });

            props.sceneHelper.dispose();
        };
    }, []);

    useInteraction(canvasRef, cameraRef, {
        onMouseMove: (e) => handleHover?.(e),
        onMouseClick: (e) => handleClick?.(e),
    });

    return (
        <div id="viewport" ref={viewportRef}>

        </div>
    )
}
