import React, { useState, useRef, useMemo, FC } from 'react';
import { Canvas } from '@react-three/fiber';
// Environment, Cylinder, Sphere 임포트 추가
import { OrbitControls, Box, Plane, Environment, MeshReflectorMaterial, Cylinder, Sphere } from '@react-three/drei';
import * as THREE from 'three';

// --- 타입 정의 ---

// 건물 데이터 구조 정의
interface BuildingData {
  id: number;
  position: number[];
  size: number[];
  color: string;
  hasRooftop: boolean; // 옥탑 구조물 유무 추가
}

// 나무 데이터 구조 정의
interface TreeData {
    id: number;
    position: number[];
}

// Building 컴포넌트 Props 타입 정의
interface BuildingProps extends BuildingData {
  onClick: () => void;
}

// Tree 컴포넌트 Props 타입 정의
interface TreeProps extends TreeData {}

// Road 컴포넌트 Props 타입 정의
interface RoadProps {
  position: number[];
  size: number[];
}

// InfoBox 컴포넌트 Props 타입 정의
interface InfoBoxProps {
  buildingInfo: BuildingData | null;
}

// --- 컴포넌트 정의 ---

/**
 * 건물 컴포넌트 (옥탑 구조물 추가)
 */
const Building: FC<BuildingProps> = ({ position, size, color, onClick, id, hasRooftop }) => {
  const [hovered, setHovered] = useState<boolean>(false);
  const meshRef = useRef<THREE.Mesh>(null!);

  const effectiveColor = hovered ? '#007bff' : color;
  const [width, height, depth] = size;

  // 옥탑 구조물 크기 및 위치 계산
  const rooftopHeight = height * 0.075;
  const rooftopWidth = width * 0.7;
  const rooftopDepth = depth * 0.7;
  const rooftopYPosition = height / 2 + rooftopHeight / 2 - 0.01; // 본체 위에 살짝 겹치게

  return (
    <group position={position as [number, number, number]}>
        {/* 본체 건물 */}
        <Box
          ref={meshRef}
          args={[width, height, depth]}
          // position 은 group 에서 설정하므로 여기선 [0, 0, 0] 기준
          onClick={(event) => {
            event.stopPropagation();
            onClick();
          }}
          onPointerOver={(event) => {
            event.stopPropagation();
            setHovered(true);
          }}
          onPointerOut={(event) => setHovered(false)}
          castShadow
          receiveShadow
        >
          <meshStandardMaterial
              color={effectiveColor}
              metalness={0.1}
              roughness={0.6}
          />
        </Box>
        {/* 옥탑 구조물 (선택적 렌더링) */}
        {hasRooftop && (
            <Box
                args={[rooftopWidth, rooftopHeight, rooftopDepth]}
                position={[0, rooftopYPosition, 0]} // 본체 상단 중앙
                castShadow
                receiveShadow
            >
                <meshStandardMaterial
                    color={effectiveColor} // 본체와 같은 색 또는 약간 다른 색
                    metalness={0.1}
                    roughness={0.6}
                    emissive={hovered ? '#555555' : '#000000'} // 호버 시 약간 밝게
                />
            </Box>
        )}
    </group>
  );
}

/**
 * 나무 컴포넌트
 */
const Tree: FC<TreeProps> = ({ position }) => {
    return (
        <group position={position as [number, number, number]}>
          {/* 바닥 */}
          <Plane
            args={[10, 10]} // 바닥 크기
            rotation={[-Math.PI / 2, 0, 0]} // 바닥에 눕히기 위해 x축으로 -90도 회전
            receiveShadow
          >
            <meshStandardMaterial color="#138b31" roughness={0.8} metalness={0.1} />
          </Plane>
        </group>
    );
}


/**
 * 도로 컴포넌트
 */
const Road: FC<RoadProps> = ({ position, size }) => {
  return (
    <Plane
      args={size as [number, number]} // Plane args 타입에 맞게 캐스팅
      position={position as [number, number, number]} // position 타입 캐스팅
      rotation={[-Math.PI / 2, 0, 0]} // 바닥에 눕히기 위해 x축으로 -90도 회전
    >
      {/* 도로 색상을 중간 회색으로 변경 */}
      <meshStandardMaterial color="#666666" side={THREE.DoubleSide} roughness={0.8} metalness={0.1} />
    </Plane>
  );
}

/**
 * 정보 표시 박스 컴포넌트 (Tailwind CSS 적용)
 */
const InfoBox: FC<InfoBoxProps> = ({ buildingInfo }) => {
  if (!buildingInfo) return null;

  // 위치와 크기 정보를 소수점 아래 두 자리까지만 표시
  const formatArray = (arr: number[]) => arr.map(n => n.toFixed(2)).join(', ');

  return (
    // Tailwind CSS 클래스 적용
    <div className="absolute top-5 left-5 p-3 px-4 bg-black/70 rounded-lg text-white font-sans text-xs shadow-lg pointer-events-none z-50">
      <h3 className="text-sm font-semibold mb-2">Selected Building</h3>
      <p className="my-1">ID: {buildingInfo.id}</p>
      {/* position[1]은 그룹 기준이므로 실제 바닥부터의 높이 표시 위해 size[1]/2 추가 */}
      <p className="my-1">Position: [{formatArray([buildingInfo.position[0], buildingInfo.position[1] - buildingInfo.size[1]/2, buildingInfo.position[2]])}]</p>
      <p className="my-1">Size: [{formatArray(buildingInfo.size)}]</p>
      {/* 색상 정보는 hex 코드로 표시 */}
      <p className="my-1">Color: {buildingInfo.color}</p>
    </div>
  );
}

// --- 메인 앱 컴포넌트 ---
const App: FC = () => {
  // useState 타입 명시: BuildingData 또는 null
  const [selectedBuilding, setSelectedBuilding] = useState<BuildingData | null>(null);

  // 건물 클릭 시 호출될 함수
  const handleBuildingClick = (buildingData: BuildingData) => {
    setSelectedBuilding(buildingData);
  };

  // 도시 데이터 정의 (건물, 도로, 나무) - 모던 스타일 적용
  const cityData = useMemo<{ buildings: BuildingData[]; roads: { position: number[]; size: number[] }[]; trees: TreeData[] }>(() => {
    const buildings: BuildingData[] = [];
    const roads: { position: number[]; size: number[] }[] = [];
    const trees: TreeData[] = [];
    const gridSize = 7;
    const spacing = 5;
    const roadWidth = 1.5;
    const buildingBaseSize = 4;
    const parkProbability = 0.15; // 공원(나무)이 될 확률

    // 건물 또는 나무 생성
    for (let i = -gridSize; i <= gridSize; i++) {
      for (let j = -gridSize; j <= gridSize; j++) {
        // 격자 교차점이 아닌 곳
        if (Math.abs(i % 2) === 1 && Math.abs(j % 2) === 1) {
          // 공원 확률 체크
          if (Math.random() < parkProbability) {
            // 나무 생성
             trees.push({
                id: trees.length + 1,
                position: [i * spacing, 0, j * spacing] // 나무는 바닥(y=0)에서 시작
             });
          } else {
            // 건물 생성
            const height = Math.random() * 20 + 6;
            const width = buildingBaseSize;
            const depth = buildingBaseSize;
            // 건물의 position.y는 건물의 중앙 높이
            const position = [i * spacing, height / 2, j * spacing];
            const size = [width, height, depth];
            const hasRooftop = Math.random() > 0.6; // 40% 확률로 옥탑 구조물 생성

            let color: string;
            if (Math.random() < 0.15) {
              const darkShade = Math.random() * 0.2 + 0.1;
              color = new THREE.Color(darkShade, darkShade, darkShade).getHexString();
              color = `#${color}`;
            } else {
              const lightShade = Math.random() * 0.3 + 0.7;
              color = new THREE.Color(lightShade, lightShade, lightShade).getHexString();
              color = `#${color}`;
            }

            buildings.push({
              id: buildings.length + 1,
              position: position,
              size: size,
              color: color,
              hasRooftop: hasRooftop
            });
          }
        }
      }
    }

    // 도로 생성
    const roadYPosition = 0.01;
    const totalGridWidth = (gridSize * 2 + 1) * spacing;
    for (let j = -gridSize; j <= gridSize; j++) {
       if (Math.abs(j % 2) === 0) {
         roads.push({ position: [0, roadYPosition, j * spacing], size: [totalGridWidth, roadWidth] });
       }
    }
    for (let i = -gridSize; i <= gridSize; i++) {
       if (Math.abs(i % 2) === 0) {
         roads.push({ position: [i * spacing, roadYPosition, 0], size: [roadWidth, totalGridWidth] });
       }
    }

    return { buildings, roads, trees };
  }, []);


  const groundColor = "#e0e0e0"; // 밝은 회색 바닥

  return (
    <div className="w-screen h-screen relative">
      <InfoBox buildingInfo={selectedBuilding} />
      <Canvas
        camera={{ position: [0, 50, 50], fov: 50 }}
        onPointerMissed={() => setSelectedBuilding(null)}
        shadows
      >
        <color attach="background" args={[groundColor]} />
        <fog attach="fog" args={[groundColor, 60, 150]} />

        {/* 조명: Environment 사용 시 강도 조절 */}
        <ambientLight intensity={0.1} />
        {/* Environment preset과 어울리도록 DirectionalLight 강도/색상 조절 가능 */}
        <directionalLight
            position={[20, 30, 15]}
            intensity={2} // 강도 약간 줄임
            castShadow
            shadow-mapSize-width={1024}
            shadow-mapSize-height={1024}
            shadow-camera-far={70}
            shadow-camera-left={-40}
            shadow-camera-right={40}
            shadow-camera-top={40}
            shadow-camera-bottom={-40}
            shadow-bias={-0.0005}
        />

        <OrbitControls makeDefault maxPolarAngle={Math.PI / 2.1} />

        {/* --- 환경 맵 추가 --- */}
        {/* preset="city" 또는 "sunset", "dawn", "apartment" 등 시도 */}
        <Environment preset="city" />

        {/* 건물 렌더링 */}
        {cityData.buildings.map((building) => (
          <Building
            key={building.id}
            {...building} // 모든 속성 전달
            onClick={() => handleBuildingClick(building)}
          />
        ))}

        {/* 도로 렌더링 */}
        {cityData.roads.map((road, index) => (
          <Road key={`road-${index}`} position={road.position} size={road.size} />
        ))}

        {/* 나무 렌더링 */}
        {cityData.trees.map((tree) => (
            <Tree key={tree.id} {...tree} />
        ))}


        {/* --- 바닥 평면에 MeshReflectorMaterial 적용 --- */}
        <Plane
            args={[300, 300]}
            rotation={[-Math.PI / 2, 0, 0]}
            position={[0, -0.01, 0]}
            receiveShadow
        >
            <MeshReflectorMaterial
                resolution={1024}
                mirror={0.8} // 반사 선명도 약간 낮춤
                mixBlur={0.2} // 블러 약간 추가
                mixStrength={0.8}
                roughness={0.4}
                metalness={0.6}
                color={groundColor}
                blur={[100, 100]} // 블러 값 조정 (성능 영향 주의)
            />
        </Plane>
      </Canvas>
    </div>
  );
}

export default App;

// --- TypeScript 및 Tailwind CSS 설정 참고 ---
// 이전과 동일

// --- 필요 라이브러리 설치 ---
// 이전과 동일

// --- 실행 방법 ---
// 이전과 동일
