"use client";

import { Canvas, useThree } from "@react-three/fiber";
import { OrbitControls, Grid, Box } from "@react-three/drei";
import { Suspense, useRef, useState, useEffect } from "react";
import * as THREE from "three";

interface YardPlacement {
  id: number;
  yard_layout_id?: number;
  material_code?: string | null;
  material_name?: string | null;
  quantity?: number | null;
  unit?: string | null;
  position_x: number;
  position_y: number;
  position_z: number;
  size_x: number;
  size_y: number;
  size_z: number;
  color: string;
  data_source_type?: string | null;
  data_source_config?: any;
  data_binding?: any;
}

interface Yard3DCanvasProps {
  placements: YardPlacement[];
  selectedPlacementId: number | null;
  onPlacementClick: (placement: YardPlacement | null) => void;
  onPlacementDrag?: (id: number, position: { x: number; y: number; z: number }) => void;
}

// 자재 박스 컴포넌트 (드래그 가능)
function MaterialBox({
  placement,
  isSelected,
  onClick,
  onDrag,
  onDragStart,
  onDragEnd,
}: {
  placement: YardPlacement;
  isSelected: boolean;
  onClick: () => void;
  onDrag?: (position: { x: number; y: number; z: number }) => void;
  onDragStart?: () => void;
  onDragEnd?: () => void;
}) {
  const meshRef = useRef<THREE.Mesh>(null);
  const [isDragging, setIsDragging] = useState(false);
  const dragStartPos = useRef<{ x: number; y: number; z: number }>({ x: 0, y: 0, z: 0 });
  const mouseStartPos = useRef<{ x: number; y: number }>({ x: 0, y: 0 });
  const { camera, gl } = useThree();

  // 드래그 중이 아닐 때 위치 업데이트
  useEffect(() => {
    if (!isDragging && meshRef.current) {
      meshRef.current.position.set(placement.position_x, placement.position_y, placement.position_z);
    }
  }, [placement.position_x, placement.position_y, placement.position_z, isDragging]);

  // 전역 이벤트 리스너 등록
  useEffect(() => {
    const handleGlobalMouseMove = (e: MouseEvent) => {
      if (isDragging && onDrag && meshRef.current) {
        e.preventDefault();
        e.stopPropagation();

        // 마우스 이동 거리 계산 (픽셀)
        const deltaX = e.clientX - mouseStartPos.current.x;
        const deltaY = e.clientY - mouseStartPos.current.y;

        // 카메라 거리를 고려한 스케일 팩터
        const distance = camera.position.distanceTo(meshRef.current.position);
        const scaleFactor = distance / 500; // 조정 가능한 값

        // 카메라 방향 벡터
        const cameraDirection = new THREE.Vector3();
        camera.getWorldDirection(cameraDirection);

        // 카메라의 우측 벡터 (X축 이동용)
        const right = new THREE.Vector3();
        right.crossVectors(camera.up, cameraDirection).normalize();

        // 실제 3D 공간에서의 이동량 계산
        const moveRight = right.multiplyScalar(-deltaX * scaleFactor);
        const moveForward = new THREE.Vector3(-cameraDirection.x, 0, -cameraDirection.z)
          .normalize()
          .multiplyScalar(deltaY * scaleFactor);

        // 최종 위치 계산
        const finalX = dragStartPos.current.x + moveRight.x + moveForward.x;
        const finalZ = dragStartPos.current.z + moveRight.z + moveForward.z;

        // NaN 검증
        if (isNaN(finalX) || isNaN(finalZ)) {
          return;
        }

        // 즉시 mesh 위치 업데이트 (부드러운 드래그)
        meshRef.current.position.set(finalX, dragStartPos.current.y, finalZ);

        // 상태 업데이트 (저장용)
        onDrag({
          x: finalX,
          y: dragStartPos.current.y,
          z: finalZ,
        });
      }
    };

    const handleGlobalMouseUp = () => {
      if (isDragging) {
        setIsDragging(false);
        gl.domElement.style.cursor = isSelected ? "grab" : "pointer";
        if (onDragEnd) {
          onDragEnd();
        }
      }
    };

    if (isDragging) {
      window.addEventListener("mousemove", handleGlobalMouseMove);
      window.addEventListener("mouseup", handleGlobalMouseUp);

      return () => {
        window.removeEventListener("mousemove", handleGlobalMouseMove);
        window.removeEventListener("mouseup", handleGlobalMouseUp);
      };
    }
  }, [isDragging, onDrag, onDragEnd, camera, isSelected, gl.domElement]);

  const handlePointerDown = (e: any) => {
    e.stopPropagation();

    // 뷰어 모드(onDrag 없음)에서는 클릭만 처리
    if (!onDrag) {
      return;
    }

    // 편집 모드에서 선택되었고 드래그 가능한 경우
    if (isSelected && meshRef.current) {
      // 드래그 시작 시점의 자재 위치 저장 (숫자로 변환)
      dragStartPos.current = {
        x: Number(placement.position_x),
        y: Number(placement.position_y),
        z: Number(placement.position_z),
      };

      // 마우스 시작 위치 저장
      mouseStartPos.current = {
        x: e.clientX,
        y: e.clientY,
      };

      setIsDragging(true);
      gl.domElement.style.cursor = "grabbing";
      if (onDragStart) {
        onDragStart();
      }
    }
  };

  // 요소가 설정되었는지 확인
  const isConfigured = !!(placement.material_name && placement.quantity && placement.unit);

  return (
    <Box
      ref={meshRef}
      position={[placement.position_x, placement.position_y, placement.position_z]}
      args={[placement.size_x, placement.size_y, placement.size_z]}
      onClick={(e) => {
        e.stopPropagation();
        e.nativeEvent?.stopPropagation();
        e.nativeEvent?.stopImmediatePropagation();
        onClick();
      }}
      onPointerDown={handlePointerDown}
      onPointerOver={() => {
        // 뷰어 모드(onDrag 없음)에서는 기본 커서, 편집 모드에서는 grab 커서
        if (onDrag) {
          gl.domElement.style.cursor = isSelected ? "grab" : "pointer";
        } else {
          gl.domElement.style.cursor = "pointer";
        }
      }}
      onPointerOut={() => {
        if (!isDragging) {
          gl.domElement.style.cursor = "default";
        }
      }}
    >
      <meshStandardMaterial
        color={placement.color}
        opacity={isConfigured ? (isSelected ? 1 : 0.8) : 0.5}
        transparent
        emissive={isSelected ? "#ffffff" : "#000000"}
        emissiveIntensity={isSelected ? 0.2 : 0}
        wireframe={!isConfigured}
      />
    </Box>
  );
}

// 3D 씬 컴포넌트
function Scene({ placements, selectedPlacementId, onPlacementClick, onPlacementDrag }: Yard3DCanvasProps) {
  const [isDraggingAny, setIsDraggingAny] = useState(false);
  const orbitControlsRef = useRef<any>(null);

  return (
    <>
      {/* 조명 */}
      <ambientLight intensity={0.5} />
      <directionalLight position={[10, 10, 5]} intensity={1} />
      <directionalLight position={[-10, -10, -5]} intensity={0.3} />

      {/* 바닥 그리드 */}
      <Grid
        args={[100, 100]}
        cellSize={5}
        cellThickness={0.5}
        cellColor="#6b7280"
        sectionSize={10}
        sectionThickness={1}
        sectionColor="#374151"
        fadeDistance={200}
        fadeStrength={1}
        followCamera={false}
        infiniteGrid={true}
      />

      {/* 자재 박스들 */}
      {placements.map((placement) => (
        <MaterialBox
          key={placement.id}
          placement={placement}
          isSelected={selectedPlacementId === placement.id}
          onClick={() => onPlacementClick(placement)}
          onDrag={onPlacementDrag ? (position) => onPlacementDrag(placement.id, position) : undefined}
          onDragStart={() => {
            setIsDraggingAny(true);
            if (orbitControlsRef.current) {
              orbitControlsRef.current.enabled = false;
            }
          }}
          onDragEnd={() => {
            setIsDraggingAny(false);
            if (orbitControlsRef.current) {
              orbitControlsRef.current.enabled = true;
            }
          }}
        />
      ))}

      {/* 카메라 컨트롤 */}
      <OrbitControls
        ref={orbitControlsRef}
        enablePan={true}
        enableZoom={true}
        enableRotate={true}
        minDistance={10}
        maxDistance={200}
        maxPolarAngle={Math.PI / 2}
        enabled={!isDraggingAny}
      />
    </>
  );
}

export default function Yard3DCanvas({
  placements,
  selectedPlacementId,
  onPlacementClick,
  onPlacementDrag,
}: Yard3DCanvasProps) {
  const handleCanvasClick = (e: any) => {
    // Canvas의 빈 공간을 클릭했을 때만 선택 해제
    // e.target이 canvas 엘리먼트인 경우
    if (e.target.tagName === "CANVAS") {
      onPlacementClick(null as any);
    }
  };

  return (
    <div className="h-full w-full bg-gray-900" onClick={handleCanvasClick}>
      <Canvas
        camera={{
          position: [50, 30, 50],
          fov: 50,
        }}
        shadows
      >
        <Suspense fallback={null}>
          <Scene
            placements={placements}
            selectedPlacementId={selectedPlacementId}
            onPlacementClick={onPlacementClick}
            onPlacementDrag={onPlacementDrag}
          />
        </Suspense>
      </Canvas>
    </div>
  );
}
