import { Canvas, useThree } from "@react-three/fiber";
import { OrbitControls, Stage, useGLTF } from "@react-three/drei";
import React, { useEffect, Suspense } from "react";
import * as THREE from "three";

const BACKGROUND_COLOR = 0x111111;

export interface ThreeViewHandle {
  zoomIn: () => void;
  zoomOut: () => void;
}

interface ThreeViewProps {
  fileUrl: string;
  width?: string;
  height?: string;
  onLoad?: () => void;
  onError?: (error: string) => void;
  onAspectRatioCalculated?: (ratio: number) => void;
  controlRef?: React.MutableRefObject<ThreeViewHandle | null>;
}

const ZOOM_FACTOR = 1.2;
const MIN_DISTANCE = 1;
const MAX_DISTANCE = 100;

const ZoomController = ({
  controlRef,
}: {
  controlRef?: React.MutableRefObject<ThreeViewHandle | null>;
}) => {
  const { camera, controls } = useThree();

  useEffect(() => {
    if (!controlRef || !controls) return;
    const orbit = controls as unknown as {
      target: THREE.Vector3;
      getDistance: () => number;
      setDistance?: (d: number) => void;
    };
    const zoomIn = () => {
      const target = orbit.target;
      const dir = new THREE.Vector3()
        .subVectors(camera.position, target)
        .normalize();
      const dist = camera.position.distanceTo(target);
      const newDist = Math.max(MIN_DISTANCE, dist / ZOOM_FACTOR);
      camera.position.copy(target).addScaledVector(dir, newDist);
      camera.updateProjectionMatrix();
    };
    const zoomOut = () => {
      const target = orbit.target;
      const dir = new THREE.Vector3()
        .subVectors(camera.position, target)
        .normalize();
      const dist = camera.position.distanceTo(target);
      const newDist = Math.min(MAX_DISTANCE, dist * ZOOM_FACTOR);
      camera.position.copy(target).addScaledVector(dir, newDist);
      camera.updateProjectionMatrix();
    };
    controlRef.current = { zoomIn, zoomOut };
    return () => {
      controlRef.current = null;
    };
  }, [camera, controls, controlRef]);

  return null;
};

const AutoResize = () => {
  const { gl, camera } = useThree();
  useEffect(() => {
    const handleResize = () => {
      const parent = gl.domElement.parentElement;
      if (!parent) return;
      gl.setSize(parent.clientWidth, parent.clientHeight);
      if (camera instanceof THREE.PerspectiveCamera) {
        camera.aspect = parent.clientWidth / parent.clientHeight;
        camera.updateProjectionMatrix();
      }
    };
    handleResize();
    window.addEventListener("resize", handleResize);
    return () => window.removeEventListener("resize", handleResize);
  }, [gl, camera]);
  return null;
};

const Model = ({
  fileUrl,
  onLoad,
}: {
  fileUrl: string;
  onLoad?: () => void;
}) => {
  const { scene } = useGLTF(fileUrl);

  useEffect(() => {
    if (scene) {
      scene.traverse((child) => {
        if ((child as THREE.Mesh).isMesh) {
          const m = (child as THREE.Mesh)
            .material as THREE.MeshStandardMaterial;
          if (m) {
            m.envMapIntensity = 1.5;
            m.needsUpdate = true;
          }
        }
      });
      onLoad?.();
    }
  }, [scene, onLoad]);

  return <primitive object={scene} />;
};

export const SightFrameThreeView: React.FC<ThreeViewProps> = ({
  fileUrl,
  width = "100%",
  height = "100%",
  onLoad,
  onError,
  controlRef,
}) => {
  return (
    <div style={{ width, height, position: "relative", overflow: "hidden" }}>
      <Canvas
        gl={{
          antialias: true,
          toneMappingExposure: 1.5,
          outputColorSpace: THREE.SRGBColorSpace,
        }}
        camera={{ position: [0, 0, 5], fov: 40 }}
        style={{ width: "100%", height: "100%" }}
        onError={() => onError?.("Canvas error")}
      >
        <AutoResize />
        {controlRef && <ZoomController controlRef={controlRef} />}
        <color attach="background" args={[BACKGROUND_COLOR]} />
        <ambientLight intensity={0.8} />
        <directionalLight position={[30, 30, 30]} intensity={1.2} />
        <directionalLight position={[-30, 30, 30]} intensity={1.2} />
        <directionalLight position={[30, -30, 30]} intensity={1} />
        <directionalLight position={[-30, -30, 30]} intensity={1} />
        <directionalLight position={[0, 0, 50]} intensity={1} />
        <directionalLight position={[0, 0, -50]} intensity={0.8} />
        <directionalLight position={[50, 0, 0]} intensity={0.8} />
        <directionalLight position={[-50, 0, 0]} intensity={0.8} />
        <directionalLight position={[0, 50, 0]} intensity={0.8} />
        <directionalLight position={[0, -50, 0]} intensity={0.6} />
        <pointLight position={[30, 30, 30]} intensity={1.2} />
        <pointLight position={[-30, -30, -30]} intensity={1.2} />
        <pointLight position={[0, 30, 0]} intensity={0.6} />

        <Suspense fallback={null}>
          <Stage
            environment={null}
            intensity={1}
            shadows={false}
            adjustCamera={true}
          >
            <Model fileUrl={fileUrl} onLoad={onLoad} />
          </Stage>
        </Suspense>

        <OrbitControls
          makeDefault
          enableZoom={true}
          enablePan={true}
          target={[50, 50, 50]}
          minDistance={1}
          maxDistance={100}
          enableDamping={true}
        />
      </Canvas>
    </div>
  );
};
