"use client";

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

interface YardPlacement {
  id: number;
  yard_layout_id?: number;
  material_code?: string | null;
  material_name?: string | null;
  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;
  material_count?: number; // Location의 자재 개수
  material_preview_height?: number; // 자재 스택 높이 (시각적)
}

interface Yard3DCanvasProps {
  placements: YardPlacement[];
  selectedPlacementId: number | null;
  onPlacementClick: (placement: YardPlacement | null) => void;
  onPlacementDrag?: (id: number, position: { x: number; y: number; z: number }) => void;
  gridSize?: number; // 그리드 크기 (기본값: 5)
  onCollisionDetected?: () => void; // 충돌 감지 시 콜백
  focusOnPlacementId?: number | null; // 카메라가 포커스할 요소 ID
  previewTool?: string | null; // 드래그 중인 도구 타입
  previewPosition?: { x: number; z: number } | null; // 프리뷰 위치
  onPreviewPositionUpdate?: (position: { x: number; z: number } | null) => void;
}

// 좌표를 그리드 칸의 중심에 스냅 (마인크래프트 스타일)
// Three.js Box의 position은 중심점이므로, 그리드 칸의 중심에 배치해야 칸에 딱 맞음
function snapToGrid(value: number, gridSize: number): number {
  // 가장 가까운 그리드 교차점으로 스냅 (오프셋 없음)
  // DigitalTwinEditor에서 오프셋 처리하므로 여기서는 순수 스냅만
  return Math.round(value / gridSize) * gridSize;
}

// 자재 박스 컴포넌트 (드래그 가능)
function MaterialBox({
  placement,
  isSelected,
  onClick,
  onDrag,
  onDragStart,
  onDragEnd,
  gridSize = 5,
  allPlacements = [],
}: {
  placement: YardPlacement;
  isSelected: boolean;
  onClick: () => void;
  onDrag?: (position: { x: number; y: number; z: number }) => void;
  onDragStart?: () => void;
  onDragEnd?: () => void;
  gridSize?: number;
  allPlacements?: YardPlacement[];
  onCollisionDetected?: () => 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 dragOffset = useRef<{ x: number; z: number }>({ x: 0, z: 0 }); // 마우스와 객체 중심 간 오프셋
  const { camera, gl } = useThree();
  const [glowIntensity, setGlowIntensity] = useState(1);

  // 선택 시 빛나는 애니메이션
  useEffect(() => {
    if (!isSelected) {
      setGlowIntensity(1);
      return;
    }

    let animationId: number;
    const startTime = Date.now();

    const animate = () => {
      const elapsed = Date.now() - startTime;
      const intensity = 1 + Math.sin(elapsed * 0.003) * 0.5; // 0.5 ~ 1.5 사이 진동
      setGlowIntensity(intensity);
      animationId = requestAnimationFrame(animate);
    };

    animate();

    return () => {
      if (animationId) {
        cancelAnimationFrame(animationId);
      }
    };
  }, [isSelected]);

  // 특정 좌표에 요소를 배치할 수 있는지 확인하고, 필요하면 Y 위치를 조정
  const checkCollisionAndAdjustY = (x: number, y: number, z: number): { hasCollision: boolean; adjustedY: number } => {
    if (!allPlacements || allPlacements.length === 0) {
      // 다른 객체가 없으면 기본 높이
      const objectType = placement.data_source_type as string | null;
      const defaultY = objectType === "area" ? 0.05 : (placement.size_y || gridSize) / 2;
      return {
        hasCollision: false,
        adjustedY: defaultY,
      };
    }

    // 내 크기 정보
    const mySizeX = placement.size_x || gridSize;
    const mySizeZ = placement.size_z || gridSize;
    const mySizeY = placement.size_y || gridSize;

    // 내 바운딩 박스 (좌측 하단 모서리 기준)
    const myMinX = x - mySizeX / 2;
    const myMaxX = x + mySizeX / 2;
    const myMinZ = z - mySizeZ / 2;
    const myMaxZ = z + mySizeZ / 2;

    const objectType = placement.data_source_type as string | null;
    const defaultY = objectType === "area" ? 0.05 : mySizeY / 2;
    let maxYBelow = defaultY;

    // Area는 스택되지 않음 (항상 바닥에 배치)
    if (objectType === "area") {
      return {
        hasCollision: false,
        adjustedY: defaultY,
      };
    }

    for (const p of allPlacements) {
      // 자기 자신은 제외
      if (Number(p.id) === Number(placement.id)) {
        continue;
      }

      // 상대방 크기 정보
      const pSizeX = p.size_x || gridSize;
      const pSizeZ = p.size_z || gridSize;
      const pSizeY = p.size_y || gridSize;

      // 상대방 바운딩 박스
      const pMinX = p.position_x - pSizeX / 2;
      const pMaxX = p.position_x + pSizeX / 2;
      const pMinZ = p.position_z - pSizeZ / 2;
      const pMaxZ = p.position_z + pSizeZ / 2;

      // AABB 충돌 감지 (2D 평면에서)
      const isOverlapping = myMinX < pMaxX && myMaxX > pMinX && myMinZ < pMaxZ && myMaxZ > pMinZ;

      if (isOverlapping) {
        // 겹침: 상대방 위에 배치
        const topOfOtherElement = p.position_y + pSizeY / 2;
        const myYOnTop = topOfOtherElement + mySizeY / 2;

        if (myYOnTop > maxYBelow) {
          maxYBelow = myYOnTop;
        }
      }
    }

    const needsAdjustment = Math.abs(y - maxYBelow) > 0.1;

    return {
      hasCollision: needsAdjustment,
      adjustedY: maxYBelow,
    };
  };

  // 드래그 중이 아닐 때만 위치 동기화
  useEffect(() => {
    if (!isDragging && meshRef.current) {
      const currentPos = meshRef.current.position;
      const targetX = placement.position_x;
      const targetY = placement.position_y;
      const targetZ = placement.position_z;

      // 현재 위치와 목표 위치가 다를 때만 업데이트 (0.01 이상 차이)
      const threshold = 0.01;
      const needsUpdate =
        Math.abs(currentPos.x - targetX) > threshold ||
        Math.abs(currentPos.y - targetY) > threshold ||
        Math.abs(currentPos.z - targetZ) > threshold;

      if (needsUpdate) {
        meshRef.current.position.set(targetX, targetY, targetZ);
      }
    }
  }, [placement.position_x, placement.position_y, placement.position_z, isDragging]);

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

        // 마우스 좌표를 정규화 (-1 ~ 1)
        const rect = gl.domElement.getBoundingClientRect();
        const mouseX = ((e.clientX - rect.left) / rect.width) * 2 - 1;
        const mouseY = -((e.clientY - rect.top) / rect.height) * 2 + 1;

        // Raycaster로 바닥 평면과의 교차점 계산
        const raycaster = new THREE.Raycaster();
        raycaster.setFromCamera(new THREE.Vector2(mouseX, mouseY), camera);

        // 바닥 평면 (y = 0)
        const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
        const intersectPoint = new THREE.Vector3();
        const hasIntersection = raycaster.ray.intersectPlane(plane, intersectPoint);

        if (!hasIntersection) {
          return;
        }

        // 마우스 위치에 드래그 시작 시 저장한 오프셋 적용
        const finalX = intersectPoint.x + dragOffset.current.x;
        const finalZ = intersectPoint.z + dragOffset.current.z;

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

        // 객체의 좌측 하단 모서리 좌표 계산 (크기 / 2를 빼서)
        const sizeX = placement.size_x || 5;
        const sizeZ = placement.size_z || 5;

        const cornerX = finalX - sizeX / 2;
        const cornerZ = finalZ - sizeZ / 2;

        // 좌측 하단 모서리를 그리드에 스냅
        const snappedCornerX = snapToGrid(cornerX, gridSize);
        const snappedCornerZ = snapToGrid(cornerZ, gridSize);

        // 스냅된 모서리로부터 중심 위치 계산
        const finalSnappedX = snappedCornerX + sizeX / 2;
        const finalSnappedZ = snappedCornerZ + sizeZ / 2;

        console.log("🐛 드래그 중:", {
          마우스_화면: { x: e.clientX, y: e.clientY },
          정규화_마우스: { x: mouseX, y: mouseY },
          교차점: { x: finalX, z: finalZ },
          스냅후: { x: finalSnappedX, z: finalSnappedZ },
        });

        // 충돌 체크 및 Y 위치 조정
        const { adjustedY } = checkCollisionAndAdjustY(finalSnappedX, dragStartPos.current.y, finalSnappedZ);

        // 즉시 mesh 위치 업데이트 (스냅된 위치로)
        meshRef.current.position.set(finalSnappedX, adjustedY, finalSnappedZ);

        // ⚠️ 드래그 중에는 상태 업데이트 안 함 (미리보기만)
        // 실제 저장은 handleGlobalMouseUp에서만 수행
      }
    };

    const handleGlobalMouseUp = () => {
      if (isDragging && meshRef.current) {
        const currentPos = meshRef.current.position;

        // 실제로 이동했는지 확인 (최소 이동 거리: 0.1)
        const minMovement = 0.1;
        const deltaX = Math.abs(currentPos.x - dragStartPos.current.x);
        const deltaZ = Math.abs(currentPos.z - dragStartPos.current.z);
        const hasMoved = deltaX > minMovement || deltaZ > minMovement;

        if (hasMoved) {
          // 실제로 드래그한 경우: 이미 handleGlobalMouseMove에서 스냅됨
          // currentPos는 이미 스냅+오프셋이 적용된 값이므로 그대로 사용
          const finalX = currentPos.x;
          const finalY = currentPos.y;
          const finalZ = currentPos.z;

          // ✅ 항상 배치 가능 (위로 올라가므로)
          console.log("✅ 배치 완료! 저장:", { x: finalX, y: finalY, z: finalZ });

          // 최종 위치 저장
          if (onDrag) {
            onDrag({
              x: finalX,
              y: finalY,
              z: finalZ,
            });
          }
        } else {
          // 클릭만 한 경우: 원래 위치 유지 (아무것도 안 함)
          meshRef.current.position.set(dragStartPos.current.x, dragStartPos.current.y, dragStartPos.current.z);
        }

        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) {
      // 드래그 시작 시점의 mesh 실제 위치 저장 (현재 렌더링된 위치)
      const currentPos = meshRef.current.position;
      dragStartPos.current = {
        x: currentPos.x,
        y: currentPos.y,
        z: currentPos.z,
      };

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

      // 마우스 클릭 위치를 3D 좌표로 변환
      const rect = gl.domElement.getBoundingClientRect();
      const mouseX = ((e.clientX - rect.left) / rect.width) * 2 - 1;
      const mouseY = -((e.clientY - rect.top) / rect.height) * 2 + 1;

      const raycaster = new THREE.Raycaster();
      raycaster.setFromCamera(new THREE.Vector2(mouseX, mouseY), camera);

      // 바닥 평면과의 교차점 계산
      const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
      const intersectPoint = new THREE.Vector3();
      const hasIntersection = raycaster.ray.intersectPlane(plane, intersectPoint);

      if (hasIntersection) {
        // 마우스 클릭 위치와 객체 중심 간의 오프셋 저장
        dragOffset.current = {
          x: currentPos.x - intersectPoint.x,
          z: currentPos.z - intersectPoint.z,
        };
      } else {
        dragOffset.current = { x: 0, z: 0 };
      }

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

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

  const boxHeight = placement.size_y || gridSize;
  const boxWidth = placement.size_x || gridSize;
  const boxDepth = placement.size_z || gridSize;
  const palletHeight = 0.3; // 팔레트 높이
  const palletGap = 0.05; // 팔레트와 박스 사이 간격 (매우 작게)

  // 팔레트 위치 계산: 박스 하단부터 시작
  const palletYOffset = -(boxHeight / 2) - palletHeight / 2 - palletGap;

  // 객체 타입 (data_source_type에 저장됨)
  const objectType = placement.data_source_type as string | null;

  // 타입별 렌더링
  const renderObjectByType = () => {
    switch (objectType) {
      case "area":
        // Area: 투명한 바닥 + 두꺼운 외곽선 (mesh) + 이름 텍스트
        const borderThickness = 0.3; // 외곽선 두께
        return (
          <>
            {/* 투명한 메쉬 (클릭 영역) */}
            <mesh>
              <boxGeometry args={[boxWidth, 0.1, boxDepth]} />
              <meshBasicMaterial transparent opacity={0} />
            </mesh>

            {/* 두꺼운 외곽선 - 4개의 막대로 구현 */}
            {/* 상단 */}
            <mesh position={[0, 0.05, -boxDepth / 2]}>
              <boxGeometry args={[boxWidth + borderThickness, 0.2, borderThickness]} />
              <meshBasicMaterial color={isSelected ? "#ffffff" : placement.color} transparent opacity={0.9} />
            </mesh>
            {/* 하단 */}
            <mesh position={[0, 0.05, boxDepth / 2]}>
              <boxGeometry args={[boxWidth + borderThickness, 0.2, borderThickness]} />
              <meshBasicMaterial color={isSelected ? "#ffffff" : placement.color} transparent opacity={0.9} />
            </mesh>
            {/* 좌측 */}
            <mesh position={[-boxWidth / 2, 0.05, 0]}>
              <boxGeometry args={[borderThickness, 0.2, boxDepth - borderThickness]} />
              <meshBasicMaterial color={isSelected ? "#ffffff" : placement.color} transparent opacity={0.9} />
            </mesh>
            {/* 우측 */}
            <mesh position={[boxWidth / 2, 0.05, 0]}>
              <boxGeometry args={[borderThickness, 0.2, boxDepth - borderThickness]} />
              <meshBasicMaterial color={isSelected ? "#ffffff" : placement.color} transparent opacity={0.9} />
            </mesh>

            {/* 선택 시 빛나는 효과 */}
            {isSelected && (
              <>
                <mesh position={[0, 0.08, -boxDepth / 2]}>
                  <boxGeometry args={[boxWidth + borderThickness * 2, 0.25, borderThickness * 1.5]} />
                  <meshBasicMaterial color={placement.color} transparent opacity={0.5} />
                </mesh>
                <mesh position={[0, 0.08, boxDepth / 2]}>
                  <boxGeometry args={[boxWidth + borderThickness * 2, 0.25, borderThickness * 1.5]} />
                  <meshBasicMaterial color={placement.color} transparent opacity={0.5} />
                </mesh>
                <mesh position={[-boxWidth / 2, 0.08, 0]}>
                  <boxGeometry args={[borderThickness * 1.5, 0.25, boxDepth]} />
                  <meshBasicMaterial color={placement.color} transparent opacity={0.5} />
                </mesh>
                <mesh position={[boxWidth / 2, 0.08, 0]}>
                  <boxGeometry args={[borderThickness * 1.5, 0.25, boxDepth]} />
                  <meshBasicMaterial color={placement.color} transparent opacity={0.5} />
                </mesh>
              </>
            )}

            {/* Area 이름 텍스트 - 위쪽 (바닥) */}
            {placement.name && (
              <>
                <Text
                  position={[0, 0.15, 0]}
                  rotation={[-Math.PI / 2, 0, 0]}
                  fontSize={Math.min(boxWidth, boxDepth) * 0.2}
                  color={placement.color}
                  anchorX="center"
                  anchorY="middle"
                  outlineWidth={0.05}
                  outlineColor="#000000"
                >
                  {placement.name}
                </Text>

                {/* 4면에 텍스트 표시 */}
                {/* 앞면 (+Z) */}
                <Text
                  position={[0, boxHeight / 2, boxDepth / 2 + 0.01]}
                  rotation={[0, 0, 0]}
                  fontSize={Math.min(boxWidth, boxHeight) * 0.3}
                  color="#ffffff"
                  anchorX="center"
                  anchorY="middle"
                  outlineWidth={0.08}
                  outlineColor="#000000"
                >
                  {placement.name}
                </Text>

                {/* 뒷면 (-Z) */}
                <Text
                  position={[0, boxHeight / 2, -boxDepth / 2 - 0.01]}
                  rotation={[0, Math.PI, 0]}
                  fontSize={Math.min(boxWidth, boxHeight) * 0.3}
                  color="#ffffff"
                  anchorX="center"
                  anchorY="middle"
                  outlineWidth={0.08}
                  outlineColor="#000000"
                >
                  {placement.name}
                </Text>

                {/* 왼쪽면 (-X) */}
                <Text
                  position={[-boxWidth / 2 - 0.01, boxHeight / 2, 0]}
                  rotation={[0, -Math.PI / 2, 0]}
                  fontSize={Math.min(boxDepth, boxHeight) * 0.3}
                  color="#ffffff"
                  anchorX="center"
                  anchorY="middle"
                  outlineWidth={0.08}
                  outlineColor="#000000"
                >
                  {placement.name}
                </Text>

                {/* 오른쪽면 (+X) */}
                <Text
                  position={[boxWidth / 2 + 0.01, boxHeight / 2, 0]}
                  rotation={[0, Math.PI / 2, 0]}
                  fontSize={Math.min(boxDepth, boxHeight) * 0.3}
                  color="#ffffff"
                  anchorX="center"
                  anchorY="middle"
                  outlineWidth={0.08}
                  outlineColor="#000000"
                >
                  {placement.name}
                </Text>
              </>
            )}
          </>
        );

      case "location-bed":
      case "location-temp":
      case "location-dest":
        // 베드 타입 Location: 회색 철판들이 데이터 개수만큼 쌓이는 형태
        // 자재가 없으면 0, 있으면 해당 개수 표시 (기본값 5 제거)
        const locPlateCount = placement.material_count ?? placement.quantity ?? 0;
        const locVisiblePlateCount = locPlateCount; // 데이터 개수만큼 모두 렌더링
        const locPlateThickness = 0.15; // 각 철판 두께
        const locPlateGap = 0.03; // 철판 사이 미세한 간격
        // 실제 렌더링되는 폴리곤 기준으로 높이 계산
        const locVisibleStackHeight = locVisiblePlateCount * (locPlateThickness + locPlateGap);
        // 그룹의 position_y를 상쇄해서 바닥(y=0)부터 시작하도록
        const locYOffset = -placement.position_y;
        const locPlateBaseY = locYOffset + locPlateThickness / 2;
        
        return (
          <>
            {/* 자재가 없을 때: 흰색 실선 테두리 바닥판 */}
            {locPlateCount === 0 && (
              <>
                {/* 얇은 흰색 바닥판 */}
                <Box
                  args={[boxWidth, 0.05, boxDepth]}
                  position={[0, locYOffset + 0.025, 0]}
                >
                  <meshStandardMaterial
                    color="#f5f5f5"
                    roughness={0.6}
                    metalness={0.1}
                    emissive={isSelected ? "#e5e5e5" : "#000000"}
                    emissiveIntensity={isSelected ? glowIntensity * 0.2 : 0}
                  />
                </Box>
                {/* 흰색 실선 테두리 */}
                <lineSegments position={[0, locYOffset + 0.06, 0]}>
                  <edgesGeometry args={[new THREE.BoxGeometry(boxWidth, 0.05, boxDepth)]} />
                  <lineBasicMaterial color="#9ca3af" linewidth={2} />
                </lineSegments>
              </>
            )}

            {/* 철판 스택 - 데이터 개수만큼 회색 판 쌓기 */}
            {locPlateCount > 0 && Array.from({ length: locVisiblePlateCount }).map((_, idx) => {
              const yPos = locPlateBaseY + idx * (locPlateThickness + locPlateGap);
              // 약간의 랜덤 오프셋으로 자연스러움 추가
              const xOffset = (Math.sin(idx * 0.5) * 0.02);
              const zOffset = (Math.cos(idx * 0.7) * 0.02);
              
              return (
                <Box
                  key={`loc-plate-${idx}`}
                  args={[boxWidth, locPlateThickness, boxDepth]}
                  position={[xOffset, yPos, zOffset]}
                >
                  <meshStandardMaterial
                    color="#6b7280" // 회색 (고정)
                    roughness={0.4}
                    metalness={0.7}
                    emissive={isSelected ? "#9ca3af" : "#000000"}
                    emissiveIntensity={isSelected ? glowIntensity * 0.3 : 0}
                  />
                  {/* 각 철판 외곽선 */}
                  <lineSegments>
                    <edgesGeometry args={[new THREE.BoxGeometry(boxWidth, locPlateThickness, boxDepth)]} />
                    <lineBasicMaterial color="#374151" opacity={0.8} transparent />
                  </lineSegments>
                </Box>
              );
            })}
            
            {/* Location 이름 - 실제 폴리곤 높이 기준, 뒤쪽(+Z)에 배치 */}
            {placement.name && (
              <Text
                position={[0, locYOffset + (locPlateCount > 0 ? locVisibleStackHeight : 0.1) + 0.3, boxDepth * 0.3]}
                rotation={[-Math.PI / 2, 0, 0]}
                fontSize={Math.min(boxWidth, boxDepth) * 0.18}
                color="#374151"
                anchorX="center"
                anchorY="middle"
                outlineWidth={0.03}
                outlineColor="#ffffff"
              >
                {placement.name}
              </Text>
            )}

            {/* 수량 표시 텍스트 - 실제 폴리곤 높이 기준, 앞쪽(-Z)에 배치 */}
            {locPlateCount > 0 && (
              <Text
                position={[0, locYOffset + locVisibleStackHeight + 0.3, -boxDepth * 0.3]}
                rotation={[-Math.PI / 2, 0, 0]}
                fontSize={Math.min(boxWidth, boxDepth) * 0.15}
                color="#1f2937"
                anchorX="center"
                anchorY="middle"
                outlineWidth={0.02}
                outlineColor="#ffffff"
              >
                {`${locPlateCount}장`}
              </Text>
            )}
          </>
        );

      case "location-stp":
        // 정차포인트(STP): 회색 타원형 플랫폼 + 'P' 마크 (자재 미적재 영역)
        {
          const baseRadius = 0.5; // 스케일로 실제 W/D를 반영 (타원형)
          const labelFontSize = Math.min(boxWidth, boxDepth) * 0.15;
          const iconFontSize = Math.min(boxWidth, boxDepth) * 0.3;

          return (
            <>
              {/* 타원형 플랫폼: 단위 실린더를 W/D로 스케일 */}
              <mesh scale={[boxWidth, 1, boxDepth]}>
                <cylinderGeometry args={[baseRadius, baseRadius, boxHeight, 32]} />
                <meshStandardMaterial
                  color={placement.color}
                  roughness={0.6}
                  metalness={0.2}
                  emissive={isSelected ? placement.color : "#000000"}
                  emissiveIntensity={isSelected ? glowIntensity * 0.8 : 0}
                />
              </mesh>

              {/* 상단 'P' 마크 (주차 아이콘 역할) */}
              <Text
                position={[0, boxHeight / 2 + 0.05, 0]}
                rotation={[-Math.PI / 2, 0, 0]}
                fontSize={iconFontSize}
                color="#ffffff"
                anchorX="center"
                anchorY="middle"
                outlineWidth={0.08}
                outlineColor="#000000"
              >
                P
              </Text>

              {/* Location 이름 */}
              {placement.name && (
                <Text
                  position={[0, boxHeight / 2 + 0.4, 0]}
                  rotation={[-Math.PI / 2, 0, 0]}
                  fontSize={labelFontSize}
                  color="#ffffff"
                  anchorX="center"
                  anchorY="middle"
                  outlineWidth={0.03}
                  outlineColor="#000000"
                >
                  {placement.name}
                </Text>
              )}
            </>
          );
        }

      // case "gantry-crane":
      //   // 겐트리 크레인: 기둥 2개 + 상단 빔
      //   return (
      //     <group>
      //       {/* 왼쪽 기둥 */}
      //       <Box args={[boxWidth * 0.1, boxHeight, boxDepth * 0.1]} position={[-boxWidth * 0.4, 0, 0]}>
      //         <meshStandardMaterial
      //           color={placement.color}
      //           roughness={0.3}
      //           metalness={0.7}
      //           emissive={isSelected ? placement.color : "#000000"}
      //           emissiveIntensity={isSelected ? glowIntensity : 0}
      //         />
      //       </Box>
      //       {/* 오른쪽 기둥 */}
      //       <Box args={[boxWidth * 0.1, boxHeight, boxDepth * 0.1]} position={[boxWidth * 0.4, 0, 0]}>
      //         <meshStandardMaterial
      //           color={placement.color}
      //           roughness={0.3}
      //           metalness={0.7}
      //           emissive={isSelected ? placement.color : "#000000"}
      //           emissiveIntensity={isSelected ? glowIntensity : 0}
      //         />
      //       </Box>
      //       {/* 상단 빔 */}
      //       <Box args={[boxWidth, boxHeight * 0.15, boxDepth * 0.15]} position={[0, boxHeight * 0.42, 0]}>
      //         <meshStandardMaterial
      //           color={placement.color}
      //           roughness={0.3}
      //           metalness={0.7}
      //           emissive={isSelected ? placement.color : "#000000"}
      //           emissiveIntensity={isSelected ? glowIntensity : 0}
      //         />
      //       </Box>
      //       {/* 호이스트 (크레인 훅) */}
      //       <Box args={[boxWidth * 0.08, boxHeight * 0.3, boxDepth * 0.08]} position={[0, boxHeight * 0.1, 0]}>
      //         <meshStandardMaterial
      //           color="#fbbf24"
      //           roughness={0.4}
      //           metalness={0.6}
      //           emissive={isSelected ? "#fbbf24" : "#000000"}
      //           emissiveIntensity={isSelected ? glowIntensity * 0.6 : 0}
      //         />
      //       </Box>
      //     </group>
      //   );

      case "crane-mobile":
        // 이동식 크레인: 하부(트랙) + 회전대 + 캐빈 + 붐대 + 카운터웨이트 + 후크
        return (
          <group>
            {/* 하부 - 크롤러 트랙 (좌측) */}
            <Box
              args={[boxWidth * 0.3, boxHeight * 0.15, boxDepth * 0.95]}
              position={[-boxWidth * 0.3, -boxHeight * 0.42, 0]}
            >
              <meshStandardMaterial
                color="#1f2937"
                roughness={0.8}
                metalness={0.3}
                emissive={isSelected ? "#1f2937" : "#000000"}
                emissiveIntensity={isSelected ? glowIntensity * 0.3 : 0}
              />
            </Box>
            {/* 하부 - 크롤러 트랙 (우측) */}
            <Box
              args={[boxWidth * 0.3, boxHeight * 0.15, boxDepth * 0.95]}
              position={[boxWidth * 0.3, -boxHeight * 0.42, 0]}
            >
              <meshStandardMaterial
                color="#1f2937"
                roughness={0.8}
                metalness={0.3}
                emissive={isSelected ? "#1f2937" : "#000000"}
                emissiveIntensity={isSelected ? glowIntensity * 0.3 : 0}
              />
            </Box>

            {/* 회전 플랫폼 */}
            <Box args={[boxWidth * 0.85, boxHeight * 0.12, boxDepth * 0.85]} position={[0, -boxHeight * 0.3, 0]}>
              <meshStandardMaterial
                color={placement.color}
                roughness={0.3}
                metalness={0.7}
                emissive={isSelected ? placement.color : "#000000"}
                emissiveIntensity={isSelected ? glowIntensity : 0}
              />
            </Box>

            {/* 엔진룸 (뒤쪽) */}
            <Box
              args={[boxWidth * 0.6, boxHeight * 0.25, boxDepth * 0.3]}
              position={[0, -boxHeight * 0.15, boxDepth * 0.25]}
            >
              <meshStandardMaterial
                color={placement.color}
                roughness={0.4}
                metalness={0.6}
                emissive={isSelected ? placement.color : "#000000"}
                emissiveIntensity={isSelected ? glowIntensity * 0.8 : 0}
              />
            </Box>

            {/* 캐빈 (운전실) - 앞쪽 */}
            <Box
              args={[boxWidth * 0.35, boxHeight * 0.3, boxDepth * 0.35]}
              position={[0, -boxHeight * 0.1, -boxDepth * 0.2]}
            >
              <meshStandardMaterial
                color="#374151"
                roughness={0.2}
                metalness={0.8}
                emissive={isSelected ? "#60a5fa" : "#000000"}
                emissiveIntensity={isSelected ? glowIntensity * 0.5 : 0}
              />
            </Box>

            {/* 붐대 베이스 (회전 지점) */}
            <Box args={[boxWidth * 0.2, boxHeight * 0.2, boxDepth * 0.2]} position={[0, -boxHeight * 0.05, 0]}>
              <meshStandardMaterial
                color="#4b5563"
                roughness={0.3}
                metalness={0.8}
                emissive={isSelected ? "#4b5563" : "#000000"}
                emissiveIntensity={isSelected ? glowIntensity * 0.4 : 0}
              />
            </Box>

            {/* 메인 붐대 (하단 섹션) */}
            <Box
              args={[boxWidth * 0.15, boxHeight * 0.5, boxDepth * 0.15]}
              position={[0, boxHeight * 0.1, -boxDepth * 0.15]}
              rotation={[Math.PI / 4.5, 0, 0]}
            >
              <meshStandardMaterial
                color="#fbbf24"
                roughness={0.3}
                metalness={0.7}
                emissive={isSelected ? "#fbbf24" : "#000000"}
                emissiveIntensity={isSelected ? glowIntensity * 0.7 : 0}
              />
            </Box>

            {/* 메인 붐대 (상단 섹션 - 연장) */}
            <Box
              args={[boxWidth * 0.12, boxHeight * 0.4, boxDepth * 0.12]}
              position={[0, boxHeight * 0.3, -boxDepth * 0.35]}
              rotation={[Math.PI / 5, 0, 0]}
            >
              <meshStandardMaterial
                color="#fbbf24"
                roughness={0.3}
                metalness={0.7}
                emissive={isSelected ? "#fbbf24" : "#000000"}
                emissiveIntensity={isSelected ? glowIntensity * 0.7 : 0}
              />
            </Box>

            {/* 카운터웨이트 (뒤쪽 균형추) */}
            <Box
              args={[boxWidth * 0.5, boxHeight * 0.2, boxDepth * 0.25]}
              position={[0, -boxHeight * 0.05, boxDepth * 0.3]}
            >
              <meshStandardMaterial
                color="#6b7280"
                roughness={0.6}
                metalness={0.4}
                emissive={isSelected ? "#6b7280" : "#000000"}
                emissiveIntensity={isSelected ? glowIntensity * 0.3 : 0}
              />
            </Box>

            {/* 후크 케이블 */}
            <Box
              args={[boxWidth * 0.02, boxHeight * 0.3, boxDepth * 0.02]}
              position={[0, boxHeight * 0.15, -boxDepth * 0.4]}
            >
              <meshStandardMaterial color="#1f2937" roughness={0.4} metalness={0.6} />
            </Box>

            {/* 후크 */}
            <Box args={[boxWidth * 0.08, boxHeight * 0.08, boxDepth * 0.08]} position={[0, 0, -boxDepth * 0.4]}>
              <meshStandardMaterial
                color="#ef4444"
                roughness={0.3}
                metalness={0.8}
                emissive={isSelected ? "#ef4444" : "#000000"}
                emissiveIntensity={isSelected ? glowIntensity * 0.8 : 0}
              />
            </Box>

            {/* 지브 와이어 (지지 케이블) */}
            <Box
              args={[boxWidth * 0.015, boxHeight * 0.35, boxDepth * 0.015]}
              position={[0, boxHeight * 0.25, -boxDepth * 0.05]}
              rotation={[Math.PI / 6, 0, 0]}
            >
              <meshStandardMaterial color="#1f2937" roughness={0.3} metalness={0.7} />
            </Box>
          </group>
        );

      case "rack":
        // 랙: 프레임 구조
        return (
          <group>
            {/* 4개 기둥 */}
            {[
              [-boxWidth * 0.4, -boxDepth * 0.4],
              [boxWidth * 0.4, -boxDepth * 0.4],
              [-boxWidth * 0.4, boxDepth * 0.4],
              [boxWidth * 0.4, boxDepth * 0.4],
            ].map(([x, z], idx) => (
              <Box key={`pillar-${idx}`} args={[boxWidth * 0.08, boxHeight, boxDepth * 0.08]} position={[x, 0, z]}>
                <meshStandardMaterial
                  color={placement.color}
                  roughness={0.3}
                  metalness={0.7}
                  emissive={isSelected ? placement.color : "#000000"}
                  emissiveIntensity={isSelected ? 0.5 : 0}
                />
              </Box>
            ))}
            {/* 선반 (3단) */}
            {[-boxHeight * 0.3, 0, boxHeight * 0.3].map((y, idx) => (
              <Box key={`shelf-${idx}`} args={[boxWidth * 0.9, boxHeight * 0.05, boxDepth * 0.9]} position={[0, y, 0]}>
                <meshStandardMaterial
                  color="#6b7280"
                  roughness={0.5}
                  metalness={0.5}
                  emissive={isSelected ? "#6b7280" : "#000000"}
                  emissiveIntensity={isSelected ? glowIntensity * 0.6 : 0}
                />
              </Box>
            ))}
          </group>
        );

      case "plate-stack":
      default:
        // 후판 스택: 회색 철판들이 데이터 개수만큼 쌓이는 형태
        const plateCount = placement.material_count || placement.quantity || 5; // 데이터 개수 (기본 5장)
        const visiblePlateCount = plateCount; // 데이터 개수만큼 모두 렌더링
        const plateThickness = 0.15; // 각 철판 두께
        const plateGap = 0.03; // 철판 사이 미세한 간격
        // 실제 렌더링되는 폴리곤 기준으로 높이 계산
        const visibleStackHeight = visiblePlateCount * (plateThickness + plateGap);
        // 그룹의 position_y를 상쇄해서 바닥(y=0)부터 시작하도록
        const yOffset = -placement.position_y;
        const plateBaseY = yOffset + plateThickness / 2;
        
        return (
          <>
            {/* 철판 스택 - 데이터 개수만큼 회색 판 쌓기 (최대 20개) */}
            {Array.from({ length: visiblePlateCount }).map((_, idx) => {
              const yPos = plateBaseY + idx * (plateThickness + plateGap);
              // 약간의 랜덤 오프셋으로 자연스러움 추가 (실제 철판처럼)
              const xOffset = (Math.sin(idx * 0.5) * 0.02);
              const zOffset = (Math.cos(idx * 0.7) * 0.02);
              
              return (
                <Box
                  key={`plate-${idx}`}
                  args={[boxWidth, plateThickness, boxDepth]}
                  position={[xOffset, yPos, zOffset]}
                >
                  <meshStandardMaterial
                    color="#6b7280" // 회색 (고정)
                    roughness={0.4}
                    metalness={0.7}
                    emissive={isSelected ? "#9ca3af" : "#000000"}
                    emissiveIntensity={isSelected ? glowIntensity * 0.3 : 0}
                  />
                  {/* 각 철판 외곽선 */}
                  <lineSegments>
                    <edgesGeometry args={[new THREE.BoxGeometry(boxWidth, plateThickness, boxDepth)]} />
                    <lineBasicMaterial color="#374151" opacity={0.8} transparent />
                  </lineSegments>
                </Box>
              );
            })}
            
            {/* 수량 표시 텍스트 (상단) - 앞쪽(-Z)에 배치 */}
            {plateCount > 0 && (
              <Text
                position={[0, yOffset + visibleStackHeight + 0.3, -boxDepth * 0.3]}
                rotation={[-Math.PI / 2, 0, 0]}
                fontSize={Math.min(boxWidth, boxDepth) * 0.18}
                color="#374151"
                anchorX="center"
                anchorY="middle"
                outlineWidth={0.03}
                outlineColor="#ffffff"
              >
                {`${plateCount}장`}
              </Text>
            )}
            
            {/* 자재명 표시 (있는 경우) - 뒤쪽(+Z)에 배치 */}
            {placement.material_name && (
              <Text
                position={[0, yOffset + visibleStackHeight + 0.3, boxDepth * 0.3]}
                rotation={[-Math.PI / 2, 0, 0]}
                fontSize={Math.min(boxWidth, boxDepth) * 0.15}
                color="#1f2937"
                anchorX="center"
                anchorY="middle"
                outlineWidth={0.02}
                outlineColor="#ffffff"
              >
                {placement.material_name}
              </Text>
            )}
          </>
        );
    }
  };

  return (
    <group
      ref={meshRef}
      position={[placement.position_x, placement.position_y, placement.position_z]}
      onClick={(e) => {
        e.stopPropagation();
        e.nativeEvent?.stopPropagation();
        e.nativeEvent?.stopImmediatePropagation();
        onClick();
      }}
      onPointerDown={handlePointerDown}
      onPointerOver={() => {
        if (onDrag) {
          gl.domElement.style.cursor = isSelected ? "grab" : "pointer";
        } else {
          gl.domElement.style.cursor = "pointer";
        }
      }}
      onPointerOut={() => {
        if (!isDragging) {
          gl.domElement.style.cursor = "default";
        }
      }}
    >
      {renderObjectByType()}
    </group>
  );
}

// 3D 씬 컴포넌트
// 카메라 포커스 컨트롤러
function CameraFocusController({
  focusOnPlacementId,
  placements,
  orbitControlsRef,
}: {
  focusOnPlacementId?: number | null;
  placements: YardPlacement[];
  orbitControlsRef: React.RefObject<any>;
}) {
  const { camera } = useThree();

  useEffect(() => {
    console.log("🎥 CameraFocusController triggered");
    console.log("  - focusOnPlacementId:", focusOnPlacementId);
    console.log("  - orbitControlsRef.current:", orbitControlsRef.current);
    console.log("  - placements count:", placements.length);

    if (focusOnPlacementId && orbitControlsRef.current) {
      const targetPlacement = placements.find((p) => p.id === focusOnPlacementId);
      console.log("  - targetPlacement:", targetPlacement);

      if (targetPlacement) {
        console.log("✅ Starting camera animation to:", targetPlacement.material_name || targetPlacement.id);

        const controls = orbitControlsRef.current;
        const targetPosition = new THREE.Vector3(
          targetPlacement.position_x,
          targetPlacement.position_y,
          targetPlacement.position_z,
        );

        // 카메라 위치 계산 (요소 위에서 약간 비스듬히)
        const cameraOffset = new THREE.Vector3(15, 15, 15);
        const newCameraPosition = targetPosition.clone().add(cameraOffset);

        // 부드러운 애니메이션으로 카메라 이동
        const startPos = camera.position.clone();
        const startTarget = controls.target.clone();
        const duration = 1000; // 1초
        const startTime = Date.now();

        const animate = () => {
          const elapsed = Date.now() - startTime;
          const progress = Math.min(elapsed / duration, 1);

          // easeInOutCubic 이징 함수
          const eased = progress < 0.5 ? 4 * progress * progress * progress : 1 - Math.pow(-2 * progress + 2, 3) / 2;

          // 카메라 위치 보간
          camera.position.lerpVectors(startPos, newCameraPosition, eased);

          // 카메라 타겟 보간
          controls.target.lerpVectors(startTarget, targetPosition, eased);
          controls.update();

          if (progress < 1) {
            requestAnimationFrame(animate);
          }
        };

        animate();
      }
    }
  }, [focusOnPlacementId, placements, camera, orbitControlsRef]);

  return null;
}

function Scene({
  placements,
  selectedPlacementId,
  onPlacementClick,
  onPlacementDrag,
  gridSize = 5,
  onCollisionDetected,
  focusOnPlacementId,
  previewTool,
  previewPosition,
}: Yard3DCanvasProps) {
  const [isDraggingAny, setIsDraggingAny] = useState(false);
  const orbitControlsRef = useRef<any>(null);

  // 프리뷰 박스 크기 계산
  const getPreviewSize = (tool: string) => {
    if (tool === "area") return { x: 20, y: 0.1, z: 20 };
    return { x: 5, y: 5, z: 5 };
  };

  // 프리뷰 박스 색상
  const getPreviewColor = (tool: string) => {
    if (tool === "area") return "#3b82f6";
    if (tool === "location-bed") return "#10b981";
    if (tool === "location-stp") return "#f59e0b";
    return "#9ca3af";
  };

  return (
    <>
      {/* 카메라 포커스 컨트롤러 */}
      <CameraFocusController
        focusOnPlacementId={focusOnPlacementId}
        placements={placements}
        orbitControlsRef={orbitControlsRef}
      />

      {/* 조명 - 전체적으로 밝게 조정 */}
      <ambientLight intensity={0.9} />
      <directionalLight position={[10, 20, 10]} intensity={1.2} />
      <directionalLight position={[-10, 20, -10]} intensity={0.8} />
      <directionalLight position={[0, 20, 0]} intensity={0.5} />
      <hemisphereLight args={["#ffffff", "#bbbbbb", 0.8]} />

      {/* 배경색 */}
      <color attach="background" args={["#f3f4f6"]} />

      {/* 바닥 - 단색 평면 (그리드 제거) */}
      <mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, -0.01, 0]}>
        <planeGeometry args={[200, 200]} />
        <meshStandardMaterial color="#e5e7eb" roughness={0.9} metalness={0.1} />
      </mesh>

      {/* 자재 박스들 */}
      {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;
            }
          }}
          gridSize={gridSize}
          allPlacements={placements}
          onCollisionDetected={onCollisionDetected}
        />
      ))}

      {/* 드래그 프리뷰 박스 */}
      {previewTool && previewPosition && (
        <Box
          args={[
            getPreviewSize(previewTool).x,
            getPreviewSize(previewTool).y,
            getPreviewSize(previewTool).z,
          ]}
          position={[
            previewPosition.x,
            previewTool === "area" ? 0.05 : getPreviewSize(previewTool).y / 2,
            previewPosition.z,
          ]}
        >
          <meshStandardMaterial
            color={getPreviewColor(previewTool)}
            transparent
            opacity={0.4}
            roughness={0.5}
            metalness={0.1}
          />
        </Box>
      )}

      {/* 카메라 컨트롤 */}
      <OrbitControls
        ref={orbitControlsRef}
        enablePan={true}
        enableZoom={true}
        enableRotate={true}
        minDistance={8}
        maxDistance={200}
        maxPolarAngle={Math.PI / 2}
        enabled={!isDraggingAny}
        screenSpacePanning={true} // 화면 공간 패닝
        panSpeed={0.8} // 패닝 속도 (기본값 1.0, 낮을수록 느림)
        rotateSpeed={0.5} // 회전 속도
      />
    </>
  );
}

export default function Yard3DCanvas({
  placements,
  selectedPlacementId,
  onPlacementClick,
  onPlacementDrag,
  gridSize = 5,
  onCollisionDetected,
  focusOnPlacementId,
  previewTool,
  previewPosition,
  onPreviewPositionUpdate,
}: 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-muted" onClick={handleCanvasClick}>
      <Canvas
        camera={{
          position: [50, 30, 50],
          fov: 50,
        }}
        shadows
        gl={{ preserveDrawingBuffer: true }}
      >
        <Suspense fallback={null}>
          <Scene
            placements={placements}
            selectedPlacementId={selectedPlacementId}
            onPlacementClick={onPlacementClick}
            onPlacementDrag={onPlacementDrag}
            gridSize={gridSize}
            onCollisionDetected={onCollisionDetected}
            focusOnPlacementId={focusOnPlacementId}
            previewTool={previewTool}
            previewPosition={previewPosition}
          />
        </Suspense>
      </Canvas>
    </div>
  );
}
