import { Canvas } from '@react-three/fiber';
import { OrbitControls, Edges, Grid, Html } from '@react-three/drei';

/* =========================================================
   双良节能大楼 · 3D 建筑模型（React Three Fiber）
   按「详细的布局.png」像素级重建 —— 60×42 网格
   --------------------------------------------------------
   设计原则：
   · 颜色仅供理解，渲染统一：所有地面 = 同一浅灰色
   · 靠资产区分功能：办公室=办公桌 · 会议室=会议长桌+讲台
     · 休息区=沙发 · 共享工位=工位桌 · 楼道=真实楼梯
   · 一楼大厅（玻璃顶下方挑空）外墙 + 窗户
   · 二楼楼板避开玻璃顶中庭（两块拼接，无覆盖/无悬空）
   · 相邻办公室共享一堵墙；过道两侧不设墙，门开向过道
   ========================================================= */

const U = 0.5; // 网格单位 → 世界单位（60×42 = 30×21）
const FLOOR2 = 1.9; // 二楼楼板高度
const ROOM_H = 1.5; // 二楼墙高
const HALL_H = 2.4; // 一楼大厅净高（至玻璃顶）
const T = 0.14; // 墙厚

// 统一地面色（所有地面同一个颜色）
const FLOOR_COLOR = '#e7ecf3';
const WALL_COLOR = '#eef3fb';
const WALL_EDGE = '#9db1cf';

/* ---- 房间矩形（60×42 坐标 [x0,x1,z0,z1,type,no]）----
   编号规则：成长区 G01-G08 · 国际区 I01-I07 · 加速区 A01-A12
           会议室 M · 楼道 S · 共享工位区 W · 休息区 L · 过道 H
   注意：编号用于逐间修正定位，保持稳定 */
const ROOMS = [
  // 玻璃顶（二楼中庭挑空）
  { x: [1, 44], z: [1, 27], type: 'glass', no: 'T' },
  // 右上：成长区(左 8 间) | 过道 | 国际区(右 7 间)
  // 编号从底部开始：G1 在最下（行23-24），G8 在最上（行1-3）
  // 企业按 Excel 场地安排：成长1=Facebook 越南跨境电商 → 成长8=综合直播私域平台
  { x: [45, 51], z: [1, 3], type: 'office', zone: '成长区', no: 'G8', company: '综合性云南直播私域平台' },
  { x: [53, 54], z: [1, 3], type: 'hallway', no: 'H01' },
  { x: [55, 58], z: [1, 3], type: 'stair', no: 'W01' },
  { x: [45, 48], z: [5, 6], type: 'office', zone: '成长区', no: 'G7', company: '云南星瑞航空' },
  { x: [45, 48], z: [8, 9], type: 'office', zone: '成长区', no: 'G6', company: '五华区丽裳文化' },
  { x: [45, 48], z: [11, 12], type: 'office', zone: '成长区', no: 'G5', company: '南菌优培食用菌' },
  { x: [45, 48], z: [14, 15], type: 'office', zone: '成长区', no: 'G4', company: '昆明云韵体育' },
  { x: [45, 48], z: [17, 18], type: 'office', zone: '成长区', no: 'G3', company: '朵哈·玫瑰特色产业链' },
  { x: [45, 48], z: [20, 21], type: 'office', zone: '成长区', no: 'G2', company: '研X同行者网络' },
  { x: [45, 48], z: [23, 24], type: 'office', zone: '成长区', no: 'G1', company: 'Facebook越南跨境电商' },
  { x: [50, 53], z: [5, 25], type: 'hallway', no: 'H02' },
  // 国际区编号从底部开始：I1 在最下（行23-24），I7 在最上（行5-6）
  // 企业按 Excel：国际1=仰光客厅 → 国际7=达岸教育管理
  { x: [55, 57], z: [5, 6], type: 'office', zone: '国际区', no: 'I7', company: '达岸教育管理' },
  { x: [55, 57], z: [8, 9], type: 'office', zone: '国际区', no: 'I6', company: '昆明舒诺生物科技' },
  { x: [55, 57], z: [11, 12], type: 'office', zone: '国际区', no: 'I5', company: '滇缅国际设计' },
  { x: [55, 57], z: [14, 15], type: 'office', zone: '国际区', no: 'I4', company: '酷享野农AI农业' },
  { x: [55, 57], z: [17, 18], type: 'office', zone: '国际区', no: 'I3', company: '中越生物医疗' },
  { x: [55, 57], z: [20, 21], type: 'office', zone: '国际区', no: 'I2', company: '云南上古绝学文化' },
  { x: [55, 57], z: [23, 24], type: 'office', zone: '国际区', no: 'I1', company: '仰光客厅' },
  { x: [45, 58], z: [26, 27], type: 'hallway', no: 'H03' },
  // 加速区：11 间办公室（下排6间 + 上排5间），编号从右下角开始
  // 下排从右到左：A1-A5 + 左下角园区管理办公室
  // 上排从右到左：A6-A10
  // 企业按 Excel 场地安排：加速1=云南派音 → 加速10=云南大学AI平台
  // ===== 上排（5 间 + 1 待入驻）=====
  { x: [1, 4], z: [28, 32], type: 'office', zone: '加速区', no: 'A11', company: '待入驻' },
  { x: [6, 12], z: [28, 32], type: 'office', zone: '加速区', no: 'A10', company: '待入驻' },
  { x: [14, 16], z: [28, 32], type: 'office', zone: '加速区', no: 'A9', company: '云南大学AI+创业平台' },
  { x: [18, 21], z: [28, 32], type: 'office', zone: '加速区', no: 'A8', company: '瀚颖AI+教育信息咨询' },
  { x: [23, 26], z: [28, 32], type: 'office', zone: '加速区', no: 'A7', company: '云南廷秀文旅康养' },
  { x: [6, 12], z: [36, 40], type: 'office', zone: '加速区', no: 'A6', company: '人工智能机器人大模型训练' }, // x: [28, 31], z: [28, 32]
  { x: [32, 43], z: [28, 32], type: 'desk', no: 'W02' },
  { x: [45, 48], z: [28, 32], type: 'lounge', no: 'L01' },
  { x: [50, 58], z: [28, 32], type: 'hallway', no: 'H04' },
  // 横向过道（行33-35）
  { x: [1, 58], z: [33, 35], type: 'hallway', no: 'H05' },
  // ===== 下排（6 间：A1-A5 + 园区管理办公室）=====
  { x: [1, 4], z: [36, 40], type: 'office', zone: '管理', no: 'M', company: '园区管理办公室' }, // x: [1, 4], z: [36, 40],
  { x: [28, 31], z: [28, 32], type: 'office', zone: '加速区', no: '', company: '' }, // x: [6, 12], z: [36, 40]
  { x: [14, 16], z: [36, 40], type: 'office', zone: '加速区', no: 'A5', company: '昆明智海银高文化科技' },
  { x: [18, 21], z: [36, 40], type: 'office', zone: '加速区', no: 'A4', company: '云南宸中低空经济' },
  { x: [23, 26], z: [36, 40], type: 'office', zone: '加速区', no: 'A3', company: '中泰研学合作' },
  { x: [28, 30], z: [36, 40], type: 'meeting', no: 'M02' }, // 小会议室（A12 位置）
  { x: [32, 33], z: [36, 40], type: 'stair', no: 'S01' }, // 楼道（真实楼梯通向一楼）
  { x: [35, 45], z: [36, 40], type: 'meeting', no: 'M01' }, // 大会议室（长桌+讲台）
  { x: [47, 49], z: [36, 40], type: 'office', zone: '加速区', no: 'A2', company: '米勒克尔蓝宝石珠宝' },
  { x: [51, 53.5], z: [36, 40], type: 'office', zone: '加速区', no: 'A1', company: '云南派音人工智能科技' },
];

/* ---- 水平墙段 [z, x0, x1]（像素精确提取）----
   说明：行1-3 顶部 G01 南墙在行4；行4/7/10/13/16/19/22/25 为成长区/国际区段间墙；
   过道（列50-53）处无墙；行27 玻璃顶南墙；行28-40 加速区内部墙（单格垂直墙点） */
const HWALLS = [
  [0, 0.5, 57.5], // 北外墙
  [4, 44.5, 48.5], 
  [4, 51, 51], 
  [4, 54, 57.5], // 顶部 G01 南墙（含列51 隔墙；列50 为过道口）
  [7, 44.5, 48.5], 
  [7, 54, 57.5],
  [10, 44.5, 48.5], 
  [10, 54, 57.5],
  [13, 44.5, 48.5], 
  [13, 54, 57.5],
  [16, 44.5, 48.5], 
  [16, 54, 57.5],
  [19, 44.5, 48.5], 
  [19, 54, 57.5],
  [22, 44.5, 48.5], 
  [22, 54, 57.5],
  [25, 44.5, 48.5], 
  [25, 54, 57.5],
  [27, 0, 44], // 玻璃顶南墙（全高）
  // 过道上侧
  [32.5, 0.5, 2.5], 
  [32.5, 5, 10], 
  [32.5, 13, 15], 
  [32.5, 17.3, 19],
  [32.5, 21, 25],
  // z=36 横墙已去除；补相邻垂直墙(z36.6-40.5)间的横向短墙段（z=36.6，x 从第一堵起到第二堵-1）
  [36, 0.5, 2.5], 
  [36, 5, 10], 
  [36, 13, 14.5], 
  [36, 17, 19], 
  [36, 22, 24], 
  [36, 27, 29], 
  [36, 33.5, 34], // 过道
  [36, 37, 44], 
  [36, 46.3, 48.5], 
  [36, 51, 53.1], // 派音右
  [41, 0, 53], // 南外墙
];
/* ---- 垂直墙段 [x, z0, z1]（像素精确提取）---- */
const VWALLS = [
  [0, 0.5, 41], // 西外墙
  [5, 27, 32], [5, 36.6, 40.5],
  [13, 27, 32], [13, 36.6, 40.5],
  [17, 27, 32], [17, 36.6, 40.5],
  [22, 27, 32], [22, 36.6, 40.5],
  [27, 27, 32], [27, 36.6, 40.5],
  [31, 36.6, 40.5], // 小会议室与楼道之间
  [34, 36.6, 40.5], // 楼道与大会议室之间
  [44, 0.5, 32], [44, 36.6, 40.5], // 玻璃顶东墙 / 大会议室西墙
  [49, 4, 5], [49, 7, 8], [49, 10, 11], [49, 13, 14], [49, 16, 17], [49, 19, 20], [49, 22, 23], // 成长区段间西侧墙
  [53.5, 4, 5], [53.5, 7, 8], [53.5, 10, 11], [53.5, 13, 14], [53.5, 16, 17], [53.5, 19, 20], [53.5, 22, 23], // 成长区段间西侧墙
  [48.5, 36.6, 40.5], // 底部 A12/A13 之间
  [53.5, 36.6, 40.5], // 底部 A1 右墙（X-0.5=53.5，Z-0.5=[35.5,40.5]）
  [51, 0.5, 3.4], // 顶部共享工位西缘（G01 东墙）
  [58, 0.5, 24.7], // 东外墙（原 [58,0,25]，延伸覆盖底部 A1 东侧）
];

const W = 60, D = 42;
const cx = (x) => (x - W / 2) * U;
const cz = (z) => (z - D / 2) * U;

/* ---------- 基础组件 ---------- */

function Wall({ x, z, w, h, axis = 'z', color = WALL_COLOR, edge = WALL_EDGE, y = 0 }) {
  const size = axis === 'z' ? [w, h, T] : [T, h, w];
  return (
    <mesh position={[x, y + h / 2, z]} castShadow receiveShadow>
      <boxGeometry args={size} />
      <meshStandardMaterial color={color} roughness={0.82} metalness={0.03} />
      <Edges color={edge} threshold={20} />
    </mesh>
  );
}

/* 窗（一楼外墙上的窗格：玻璃面板 + 窗框） */
function Window({ x, z, w, h = 1.0, axis = 'z', y = 0.85 }) {
  const size = axis === 'z' ? [w, h, 0.04] : [0.04, h, w];
  return (
    <mesh position={[x, y + h / 2, z]} castShadow>
      <boxGeometry args={size} />
      <meshStandardMaterial color="#bfe0ff" transparent opacity={0.5} roughness={0.08} metalness={0.2} />
    </mesh>
  );
}


/* 办公桌（办公桌 + 座椅） */
function Desk({ x, z, y = 0, rotY = 0 }) {
  return (
    <group position={[x, y, z]} rotation={[0, rotY, 0]}>
      <mesh position={[0, 0.4, 0]} castShadow>
        <boxGeometry args={[1.1, 0.05, 0.55]} />
        <meshStandardMaterial color="#f0e6d2" roughness={0.6} metalness={0.05} />
      </mesh>
      <mesh position={[0, 0.2, 0]} castShadow>
        <boxGeometry args={[0.06, 0.4, 0.5]} />
        <meshStandardMaterial color="#d8cbb2" roughness={0.7} />
      </mesh>
      {/* 显示器 */}
      <mesh position={[0, 0.62, -0.1]} castShadow>
        <boxGeometry args={[0.4, 0.3, 0.04]} />
        <meshStandardMaterial color="#2e3a4e" roughness={0.3} metalness={0.2} />
      </mesh>
      {/* 座椅 */}
      <mesh position={[0.5, 0.24, 0]} castShadow>
        <boxGeometry args={[0.45, 0.08, 0.45]} />
        <meshStandardMaterial color="#5c6b85" roughness={0.7} />
      </mesh>
      <mesh position={[0.5, 0.45, 0]} castShadow>
        <boxGeometry args={[0.45, 0.4, 0.06]} />
        <meshStandardMaterial color="#5c6b85" roughness={0.7} />
      </mesh>
    </group>
  );
}

/* 圆桌（茶几） */
function Table({ x, z, y = 0, s = 1 }) {
  return (
    <group position={[x, y, z]}>
      <mesh position={[0, 0.18 * s, 0]} castShadow receiveShadow>
        <cylinderGeometry args={[0.22 * s, 0.26 * s, 0.34 * s, 24]} />
        <meshStandardMaterial color="#e9eef7" roughness={0.6} metalness={0.05} />
      </mesh>
      <mesh position={[0, 0.38 * s, 0]} castShadow>
        <cylinderGeometry args={[0.32 * s, 0.32 * s, 0.05 * s, 28]} />
        <meshStandardMaterial color="#ffffff" roughness={0.45} metalness={0.06} />
      </mesh>
    </group>
  );
}

/* 会议长桌 + 座椅 */
function MeetingTable({ x, z, y = 0, rotY = 0, len = 2.6 }) {
  return (
    <group position={[x, y, z]} rotation={[0, rotY, 0]}>
      <mesh position={[0, 0.42, 0]} castShadow>
        <boxGeometry args={[len, 0.05, 1.0]} />
        <meshStandardMaterial color="#e8dcc4" roughness={0.55} metalness={0.05} />
      </mesh>
      {[0, 1].map((i) => (
        <mesh key={`leg${i}`} position={[len / 2 - 0.15 - i * (len - 0.3), 0.2, 0]} castShadow>
          <boxGeometry args={[0.07, 0.4, 0.9]} />
          <meshStandardMaterial color="#d8cbb2" roughness={0.7} />
        </mesh>
      ))}
      {[-0.5, 0, 0.5].map((dx) => (
        <mesh key={`chair${dx}`} position={[dx * (len / 2 - 0.4), 0.24, 0.75]} castShadow>
          <boxGeometry args={[0.5, 0.08, 0.5]} />
          <meshStandardMaterial color="#4f5f7a" roughness={0.7} />
        </mesh>
      ))}
    </group>
  );
}

/* 会议室小办公桌（小型方桌 + 座椅，多张布置，不穿墙） */
function MeetingDesk({ x, z, y = 0, rotY = 0 }) {
  return (
    <group position={[x, y, z]} rotation={[0, rotY, 0]}>
      {/* 桌面 */}
      <mesh position={[0, 0.42, 0]} castShadow>
        <boxGeometry args={[0.9, 0.05, 0.6]} />
        <meshStandardMaterial color="#e8dcc4" roughness={0.55} metalness={0.05} />
      </mesh>
      {/* 桌腿 */}
      <mesh position={[0, 0.2, 0]} castShadow>
        <boxGeometry args={[0.06, 0.46, 0.5]} />
        <meshStandardMaterial color="#d8cbb2" roughness={0.7} />
      </mesh>
      {/* 座椅（桌前） */}
      <mesh position={[0, 0.24, 0.45]} castShadow>
        <boxGeometry args={[0.45, 0.08, 0.45]} />
        <meshStandardMaterial color="#4f5f7a" roughness={0.7} />
      </mesh>
      <mesh position={[0, 0.45, 0.45]} castShadow>
        <boxGeometry args={[0.45, 0.4, 0.06]} />
        <meshStandardMaterial color="#4f5f7a" roughness={0.7} />
      </mesh>
    </group>
  );
}

/* 讲台（会议室讲台） */
function Podium({ x, z, y = 0 }) {  return (
    <group position={[x, y, z]}>
      <mesh position={[0, 0.5, 0]} castShadow>
        <boxGeometry args={[0.7, 1.0, 0.5]} />
        <meshStandardMaterial color="#b9986a" roughness={0.6} metalness={0.1} />
      </mesh>
      <mesh position={[0, 1.02, 0]}>
        <boxGeometry args={[0.76, 0.05, 0.56]} />
        <meshStandardMaterial color="#a8875c" roughness={0.5} metalness={0.1} />
      </mesh>
    </group>
  );
}

/* 工位桌（共享工位区） */
function Workstation({ x, z, y = 0, rotY = 0 }) {
  return (
    <group position={[x, y, z]} rotation={[0, rotY, 0]}>
      <mesh position={[0, 0.4, 0]} castShadow>
        <boxGeometry args={[1.0, 0.05, 0.6]} />
        <meshStandardMaterial color="#e7dcc8" roughness={0.6} metalness={0.05} />
      </mesh>
      <mesh position={[0, 0.2, 0]} castShadow>
        <boxGeometry args={[0.05, 0.4, 0.55]} />
        <meshStandardMaterial color="#d0c3ab" roughness={0.7} />
      </mesh>
      <mesh position={[0, 0.6, 0]} castShadow>
        <boxGeometry args={[0.5, 0.34, 0.04]} />
        <meshStandardMaterial color="#33415c" roughness={0.3} metalness={0.2} />
      </mesh>
      <mesh position={[0.48, 0.24, 0]} castShadow>
        <boxGeometry args={[0.4, 0.08, 0.4]} />
        <meshStandardMaterial color="#6b7a94" roughness={0.7} />
      </mesh>
    </group>
  );
}

/* 沙发 */
function Sofa({ x, z, y = 0, rotY = 0 }) {
  return (
    <group position={[x, y, z]} rotation={[0, rotY, 0]}>
      <mesh position={[0, 0.2, 0]} castShadow>
        <boxGeometry args={[0.9, 0.18, 0.4]} />
        <meshStandardMaterial color="#8fbfa8" roughness={0.75} />
      </mesh>
      <mesh position={[0, 0.4, -0.15]} castShadow>
        <boxGeometry args={[0.9, 0.42, 0.15]} />
        <meshStandardMaterial color="#7fb39c" roughness={0.75} />
      </mesh>
      <mesh position={[-0.38, 0.33, 0.02]} castShadow>
        <boxGeometry args={[0.14, 0.28, 0.38]} />
        <meshStandardMaterial color="#7fb39c" roughness={0.75} />
      </mesh>
      <mesh position={[0.38, 0.33, 0.02]} castShadow>
        <boxGeometry args={[0.14, 0.28, 0.38]} />
        <meshStandardMaterial color="#7fb39c" roughness={0.75} />
      </mesh>
      <mesh position={[-0.3, 0.28, 0.22]} castShadow>
        <boxGeometry args={[0.18, 0.14, 0.12]} />
        <meshStandardMaterial color="#e8d9a8" roughness={0.8} />
      </mesh>
    </group>
  );
}

/* 绿植 */
function Plant({ x, z, y = 0, s = 1 }) {
  return (
    <group position={[x, y, z]}>
      <mesh position={[0, 0.2 * s, 0]} castShadow>
        <cylinderGeometry args={[0.08 * s, 0.11 * s, 0.4 * s, 16]} />
        <meshStandardMaterial color="#c9b28a" roughness={0.8} />
      </mesh>
      <mesh position={[0, 0.56 * s, 0]} castShadow>
        <sphereGeometry args={[0.3 * s, 20, 20]} />
        <meshStandardMaterial color="#3fae7f" roughness={0.7} />
      </mesh>
    </group>
  );
}

/* 楼梯（楼梯间：从二楼通向一楼 —— 踏步自二楼逐级向下延伸至地面，居中铺满房间） */
function Stairs({ x, z, y = 0, rotY = 0, steps = 9, stepW = 0.9, stepD = 0.24, rise }) {
  // 默认向下：最高一级落在 y（二楼楼面），最低一级落在 y - rise*(steps-1) = 0（一楼地面）
  const r = rise ?? (steps > 1 ? y / (steps - 1) : y);
  const len = steps * stepD;
  const els = [];
  for (let i = 0; i < steps; i++) {
    const dz = i * stepD - len / 2;      // 居中铺满房间（避免穿墙）
    const h = y - r * i;                 // 从二楼向下逐级降低
    els.push(
      <mesh key={`st${i}`} position={[0, h - r / 2, dz]} castShadow receiveShadow>
        <boxGeometry args={[stepW, r, stepD]} />
        <meshStandardMaterial color="#e8eef5" roughness={0.6} metalness={0.02} />
      </mesh>
    );
  }
  return (
    <group position={[x, y, z]} rotation={[0, rotY, 0]}>
      {els}
    </group>
  );
}

function ZoneTag({ x, y, z, cls, label, sub, no }) {
  return (
    <Html position={[x, y, z]} center distanceFactor={13} zIndexRange={[40, 0]} style={{ pointerEvents: 'none' }}>
      <div className={`bd3d-tag${cls ? ` ${cls}` : ''}`}>
        {sub && <span className={`bd3d-bubble ${sub.cls}`}>{sub.text}</span>}
        <span className="bd3d-roomname">
          {no && <b className="bd3d-no">{no}</b>}
          {label}
        </span>
      </div>
    </Html>
  );
}

/* ---------- 建筑主体 ---------- */
function Building() {
  const floors = [];
  const walls = [];
  const roofs = [];
  const furniture = [];
  const tags = [];

  /* ===== 1. 一楼大厅（玻璃顶下方挑空） ===== */
  const hx0 = cx(0), hx1 = cx(44);
  const hz0 = cz(0), hz1 = cz(27);
  const hcX = (hx0 + hx1) / 2, hcZ = (hz0 + hz1) / 2;
  const hW = hx1 - hx0, hD = hz1 - hz0;

  // 一楼地面（统一色）
  floors.push(
    <mesh key="hf" position={[hcX, 0.02, hcZ]} receiveShadow>
      <boxGeometry args={[hW - 0.3, 0.04, hD - 0.3]} />
      <meshStandardMaterial color={FLOOR_COLOR} roughness={0.9} metalness={0} />
    </mesh>
  );

  // 一楼大厅外墙（高度 = FLOOR2，不穿过二楼楼板）+ 窗户
  const hallWalls = [
    { axis: 'z', pos: 0, from: 0, to: 44 },
    { axis: 'z', pos: 41.1, from: 0, to: 44 },
    { axis: 'x', pos: 0, from: 0, to: 41 },
    { axis: 'x', pos: 44, from: 0, to: 41 },
  ];
  hallWalls.forEach((wl, i) => {
    const posW = wl.axis === 'z' ? cz(wl.pos) : cx(wl.pos);
    const fromW = wl.axis === 'z' ? cx(wl.from) : cz(wl.from);
    const toW = wl.axis === 'z' ? cx(wl.to) : cz(wl.to);
    walls.push(
      <Wall key={`hw${i}`} x={wl.axis === 'z' ? (fromW + toW) / 2 : posW} z={wl.axis === 'z' ? posW : (fromW + toW) / 2}
        w={Math.abs(toW - fromW)} h={FLOOR2} axis={wl.axis} color="#e9f0f8" edge="#9db1cf" y={0} />
    );
    // 窗（沿墙均匀分布）
    const len = Math.abs(toW - fromW);
    const winN = Math.max(2, Math.floor(len / 2.4));
    for (let wi = 0; wi < winN; wi++) {
      const t = (wi + 0.5) / winN;
      const pos = fromW + len * t;
      if (wl.axis === 'z') {
        walls.push(<Window key={`win${i}_${wi}`} x={pos} z={posW + (wl.pos === 27 ? -0.03 : 0.03)} w={len / winN - 0.4} axis="z" />);
      } else {
        walls.push(<Window key={`win${i}_${wi}`} x={posW + (wl.pos === 44 ? -0.03 : 0.03)} z={pos} w={len / winN - 0.4} axis="x" />);
      }
    }
  });

  // 大厅立柱
  for (let i = 0; i <= 8; i++) {
    const t = i / 8;
    const px = hx0 + 0.8 + (hW - 1.6) * t;
    furniture.push(<mesh key={`c1${i}`} position={[px, FLOOR2 / 2, hz0 + 1.5]} castShadow>
      <cylinderGeometry args={[0.09, 0.12, FLOOR2, 12]} />
      <meshStandardMaterial color="#eef2f9" roughness={0.5} metalness={0.1} />
    </mesh>);
    furniture.push(<mesh key={`c2${i}`} position={[px, FLOOR2 / 2, hz1 - 1.5]} castShadow>
      <cylinderGeometry args={[0.09, 0.12, FLOOR2, 12]} />
      <meshStandardMaterial color="#eef2f9" roughness={0.5} metalness={0.1} />
    </mesh>);
  }
  // 吊灯（二楼楼板下方）
  [hcX - 4, hcX, hcX + 4].forEach((lx, li) => {
    furniture.push(
      <group key={`hl${li}`} position={[lx, 0, hcZ]}>
        <mesh position={[0, FLOOR2 - 0.4, 0]}><cylinderGeometry args={[0.04, 0.04, 0.35, 10]} /><meshStandardMaterial color="#b9c8de" roughness={0.3} metalness={0.5} /></mesh>
        <mesh position={[0, FLOOR2 - 0.75, 0]}><sphereGeometry args={[0.18, 24, 24]} /><meshStandardMaterial color="#fff3cf" emissive="#ffd97a" emissiveIntensity={1.1} roughness={0.15} /></mesh>
      </group>
    );
  });
  // 大厅家具
  furniture.push(<Sofa key="hs1" x={hcX - 2} z={hcZ + 1.5} />);
  furniture.push(<Sofa key="hs2" x={hcX + 2} z={hcZ - 1.5} />);
  furniture.push(<Table key="ht1" x={hcX} z={hcZ} s={1.2} />);
  furniture.push(<Plant key="hp1" x={hx0 + 2.4} z={hcZ} s={1.3} />);
  furniture.push(<Plant key="hp2" x={hx1 - 2.4} z={hcZ} s={1.3} />);
  tags.push(<ZoneTag key="hall" x={hcX} y={FLOOR2 + 0.4} z={hcZ} cls="glass" label="一楼大厅" />);

  /* ===== 2. 二楼整体地面（两块拼接，避开玻璃顶中庭） ===== */
  // 右上块：x[45,58] z[1,28]
  floors.push(
    <mesh key="f2r" position={[(cx(45) + cx(58)) / 2, FLOOR2, (cz(1) + cz(28)) / 2]} receiveShadow castShadow>
      <boxGeometry args={[cx(58) - cx(45) + 0.2, 0.05, cz(28) - cz(1) + 0.2]} />
      <meshStandardMaterial color={FLOOR_COLOR} roughness={0.88} metalness={0} />
    </mesh>
  );
  // 加速块：x[1,58] z[28,41]
  floors.push(
    <mesh key="f2a" position={[(cx(1) + cx(58)) / 2, FLOOR2, (cz(28) + cz(41)) / 2]} receiveShadow castShadow>
      <boxGeometry args={[cx(58) - cx(1) + 0.2, 0.05, cz(41) - cz(28) + 0.2]} />
      <meshStandardMaterial color={FLOOR_COLOR} roughness={0.88} metalness={0} />
    </mesh>
  );

  /* ===== 3. 房间家具 + 标签（地面统一色，不铺色块） ===== */
  ROOMS.forEach((r, i) => {
    const x0 = cx(r.x[0]), x1 = cx(r.x[1]);
    const z0 = cz(r.z[0]), z1 = cz(r.z[1]);
    const cX = (x0 + x1) / 2, cZ = (z0 + z1) / 2;
    const w = x1 - x0, d = z1 - z0;

    if (r.type === 'glass') {
      // 玻璃顶：二楼中庭挑空；绿植放中庭平台
      furniture.push(<Plant key={`g1${i}`} x={x0 + 1.8} z={z0 + 1.8} y={FLOOR2} s={1} />);
      furniture.push(<Plant key={`g2${i}`} x={x1 - 1.8} z={z1 - 1.8} y={FLOOR2} s={1} />);
      // 玻璃天窗（青绿半透明 + 框架梁）
      const gw = w + 0.4, gd = d + 0.4;
      roofs.push(
        <mesh key="growf" position={[cX, FLOOR2 + ROOM_H + 0.25, cZ]} castShadow>
          <boxGeometry args={[gw, 0.05, gd]} />
          <meshStandardMaterial color="#8fd8c0" transparent opacity={0.28} roughness={0.05} metalness={0.25} />
        </mesh>
      );
      for (let k = 0; k <= 8; k++) {
        const t = k / 8;
        const px = x0 - 0.2 + gw * t;
        roofs.push(<mesh key={`gx${i}_${k}`} position={[px, FLOOR2 + ROOM_H + 0.28, cZ]}><boxGeometry args={[0.06, 0.09, gd + 0.4]} /><meshStandardMaterial color="#dbe7f5" roughness={0.4} metalness={0.2} /></mesh>);
      }
      for (let k = 0; k <= 4; k++) {
        const t = k / 4;
        const pz = z0 - 0.2 + gd * t;
        roofs.push(<mesh key={`gz${i}_${k}`} position={[cX, FLOOR2 + ROOM_H + 0.28, pz]}><boxGeometry args={[gw + 0.4, 0.09, 0.06]} /><meshStandardMaterial color="#dbe7f5" roughness={0.4} metalness={0.2} /></mesh>);
      }
      tags.push(<ZoneTag key={`zt${i}`} x={cX} y={FLOOR2 + ROOM_H + 0.7} z={cZ} cls="glass" label="玻璃顶" />);
      return;
    }

    // 过道不渲染顶棚（hallway：无顶棚，避免半透明色块干扰地面观感）

    // 家具按功能区分（地面统一色，靠资产区分）
    if (r.type === 'office') {
      // 办公室：办公桌按房间面积排布，留边距不穿墙
      const roomW = r.x[1] - r.x[0];
      const roomD = r.z[1] - r.z[0];
      // 窄房间（宽 < 2.6）：单桌且旋转 90°（桌+椅沿深度方向排布，避免横向穿墙）
      const narrow = roomW < 2.6;
      if (narrow) {
        furniture.push(
          <Desk key={`d${i}_0`} x={cX} z={cZ} y={FLOOR2} rotY={Math.PI / 2} />
        );
      } else {
        const nX = Math.max(1, Math.min(3, Math.floor(roomW / 2.4)));
        const nZ = Math.max(1, Math.min(2, Math.floor(roomD / 2.4)));
        const total = Math.min(4, nX * nZ);
        let idx = 0;
        for (let ix = 0; ix < nX && idx < total; ix++) {
          for (let iz = 0; iz < nZ && idx < total; iz++, idx++) {
            // 留边距居中，确保桌面/椅不穿墙
            const dx = (ix + 0.5) / nX - 0.5;
            const dz = (iz + 0.5) / nZ - 0.5;
            furniture.push(
              <Desk key={`d${i}_${idx}`} x={cX + dx * (w - 1.6)} z={cZ + dz * (d - 1.6)} y={FLOOR2} />
            );
          }
        }
      }
    } else if (r.type === 'desk') {
      // 共享工位区：工位桌多张
      const nX = Math.max(2, Math.floor((r.x[1] - r.x[0]) / 3));
      const nZ = Math.max(1, Math.floor((r.z[1] - r.z[0]) / 3));
      let idx = 0;
      for (let ix = 0; ix < nX; ix++) {
        for (let iz = 0; iz < nZ; iz++, idx++) {
          const dx = (ix + 0.5) / nX - 0.5;
          const dz = (iz + 0.5) / nZ - 0.5;
          furniture.push(<Workstation key={`ws${i}_${idx}`} x={cX + dx * w} z={cZ + dz * d} y={FLOOR2} />);
        }
      }
    } else if (r.type === 'lounge') {
      // 休息区：多张沙发 + 茶几
      furniture.push(<Sofa key={`l1${i}`} x={cX - 0.6} z={cZ - 0.5} y={FLOOR2} rotY={0.5} />);
      furniture.push(<Sofa key={`l2${i}`} x={cX + 0.6} z={cZ - 0.5} y={FLOOR2} rotY={-0.5} />);
      furniture.push(<Sofa key={`l3${i}`} x={cX} z={cZ + 0.7} y={FLOOR2} rotY={Math.PI} />);
      furniture.push(<Table key={`l4${i}`} x={cX} z={cZ} y={FLOOR2} s={0.8} />);
    } else if (r.type === 'meeting') {
      // 会议室：多张小办公桌（MeetingDesk）按房间网格排布，不穿墙且满布
      const roomW = r.x[1] - r.x[0];
      const roomD = r.z[1] - r.z[0];
      const nD = Math.max(1, Math.min(6, Math.floor((roomW - 0.8) / 1.2))); // 沿 X 列（留边距，桌宽0.9+隙）
      const nR = Math.max(1, Math.min(4, Math.floor((roomD - 0.8) / 1.2))); // 沿 Z 行
      const total = Math.min(12, nD * nR);              // 上限 12 张小桌
      let idx = 0;
      for (let ix = 0; ix < nD && idx < total; ix++) {
        for (let iz = 0; iz < nR && idx < total; iz++, idx++) {
          // 留边距居中（每侧 0.5 单位），避免穿墙
          const dx = (ix + 0.5) / nD - 0.5;
          const dz = (iz + 0.5) / nR - 0.5;
          furniture.push(
            <MeetingDesk key={`md${i}_${idx}`}
              x={cX + dx * (w - 1.0)} z={cZ + dz * (d - 1.0)} y={FLOOR2} />
          );
        }
      }
      // 大会议室额外加讲台
      const aBig = roomW * roomD >= 40;
      if (aBig) furniture.push(<Podium key={`m3${i}`} x={cX - (w - 1.0) / 2 + 0.6} z={cZ} y={FLOOR2} />);
    } else if (r.type === 'stair') {
      // 楼梯间：真实楼梯（二楼 → 一楼），自动按房间宽/深选择梯段方向与级数
      const alongX = (x1 - x0) >= (z1 - z0);   // 房间宽≥深 → 梯段沿 X 方向
      const sX = (x0 + x1) / 2;
      const sZ = (z0 + z1) / 2;
      const sLen = alongX ? (x1 - x0) : (z1 - z0);
      const steps = Math.max(4, Math.min(11, Math.round(sLen / 0.45)));
      furniture.push(
        <Stairs key={`st${i}`} x={sX} z={sZ} y={FLOOR2} rotY={alongX ? Math.PI / 2 : Math.PI}
          steps={steps} stepW={Math.min(0.9, (alongX ? d : w) - 0.1)} stepD={sLen / steps} />
      );
    }
    // 标签：每个房间显示编号（编号 + 名称），办公室显示入驻企业名；过道不显示标签
    if (r.type === 'hallway') return;
    const noLabel = r.no || '';
    const typeLabel =
      r.type === 'desk' ? '共享工位区' :
      r.type === 'lounge' ? '休息区' :
      r.type === 'meeting' ? '会议室' :
      r.type === 'stair' ? '楼梯间' :
      r.type === 'office' ? (r.company || r.zone || '办公室') :
      '过道';
    tags.push(
      <ZoneTag key={`zt${i}`} x={cX} y={FLOOR2 + ROOM_H + 0.55} z={cZ}
        cls={r.type === 'office' ? 'office' : r.type}
        no={noLabel}
        label={typeLabel}
      />
    );
  });

  /* ===== 4. 墙体（像素墙段精确生成，无交叉穿出） ===== */
  HWALLS.forEach(([z, x0, x1], i) => {
    walls.push(
      <Wall key={`h_${i}`} x={cx((x0 + x1) / 2)} z={cz(z)} w={(x1 - x0 + 1) * U} h={ROOM_H} axis="z" y={FLOOR2} />
    );
  });
  VWALLS.forEach(([x, z0, z1], i) => {
    walls.push(
      <Wall key={`v_${i}`} x={cx(x)} z={cz((z0 + z1) / 2)} w={(z1 - z0 + 1) * U} h={ROOM_H} axis="x" y={FLOOR2} />
    );
  });

  // 玻璃顶中庭四周矮围栏（二楼，0.65 高）
  const gl = ROOMS.find((r) => r.type === 'glass');
  const gx0 = cx(gl.x[0]), gx1 = cx(gl.x[1]);
  const gz0 = cz(gl.z[0]), gz1 = cz(gl.z[1]);
  const railH = 0.65;
  walls.push(<Wall key="railN" x={(gx0 + gx1) / 2} z={gz0} w={gx1 - gx0} h={railH} axis="z" y={FLOOR2} />);
  walls.push(<Wall key="railS" x={(gx0 + gx1) / 2} z={gz1} w={gx1 - gx0} h={railH} axis="z" y={FLOOR2} />);
  walls.push(<Wall key="railW" x={gx0} z={(gz0 + gz1) / 2} w={gz1 - gz0} h={railH} axis="x" y={FLOOR2} />);
  walls.push(<Wall key="railE" x={gx1} z={(gz0 + gz1) / 2} w={gz1 - gz0} h={railH} axis="x" y={FLOOR2} />);

  /* ===== 过道/大厅绿植（靠墙摆放 · 严禁穿墙 · 排列整齐） =====
     每盆绿植按【墙线 wall（x 或 z）+ 内侧方向 side（+1 向过道内侧 / -1）+ 沿墙间距】确定：
     中心离墙 = 冠半径 + 固定余量 0.15，保证完全落在过道地面内、不越墙、不挡门 */
  const plant = (s, wall, side, offset) => ({
    s,
    x: wall.kind === 'x' ? wall.v + side * (0.28 * s + 0.18) : offset,
    z: wall.kind === 'z' ? wall.v + side * (0.28 * s + 0.18) : offset,
  });
  // wall: 沿墙线；offset: 沿该墙线的间距（x 或 z 坐标）
  const corridorPlants = [
    // —— H04 竖向过道西墙（x=50，过道 x50-58，内侧 = +x 方向）——
    plant(0.9, { kind: 'x', v: 50 }, 1, 30),
    plant(0.8, { kind: 'x', v: 50 }, 1, 29.2),
    // —— H02 竖向过道（x50-53）绿植靠东内侧 x≈50.6，沿 z 等距 ——
    plant(0.85, { kind: 'x', v: 50.5 }, 1, 20),
    plant(0.85, { kind: 'x', v: 50.5 }, 1, 14),
    plant(0.85, { kind: 'x', v: 50.5 }, 1, 8),
    // —— H03 横向过道北墙（z=28，过道在 z<28，内侧 = -z）——
    // plant(0.9, { kind: 'z', v: 28 }, -1, 48),
    // plant(0.9, { kind: 'z', v: 28 }, -1, 54),
    // plant(0.9, { kind: 'z', v: 28 }, -1, 56),
    // —— 中庭围栏（z=1 北外墙内侧 +z / z=27 南缘内侧 -z）——
    // plant(0.85, { kind: 'z', v: 1 }, 1, 40),
    // plant(0.85, { kind: 'z', v: 27 }, -1, 40),
    // plant(0.85, { kind: 'z', v: 27 }, -1, 2),
    // —— W02 共享工位区南缘（z=32，内侧 = +z 回工位区）——
    plant(0.8, { kind: 'z', v: 32 }, 1, 36),
    plant(0.8, { kind: 'z', v: 32 }, 1, 40),
  ];
  corridorPlants.forEach((p, i) => {
    furniture.push(<Plant key={`cp${i}`} x={cx(p.x)} z={cz(p.z)} y={FLOOR2} s={p.s} />);
  });

  return (
    <group>
      {/* 地基 */}
      <mesh position={[0, -0.09, 0]} receiveShadow castShadow>
        <boxGeometry args={[W * U + 0.4, 0.18, D * U + 0.4]} />
        <meshStandardMaterial color="#eef2f8" roughness={0.92} metalness={0} />
        <Edges color="#eef2f8" threshold={20} />
      </mesh>
      {floors}
      {walls}
      {roofs}
      {furniture}
      {tags}
      {/* 底座平台 */}
      <mesh position={[0, -0.38, 0]} receiveShadow castShadow>
        <boxGeometry args={[W * U + 0.8, 0.44, D * U + 0.8]} />
        <meshStandardMaterial color="#eef2f8" roughness={0.75} metalness={0.02} />
        <Edges color="#eef2f8" threshold={20} />
      </mesh>
    </group>
  );
}

/* ---------- 场景 ---------- */
export default function Building3D({ interactive = true }) {
  return (
    <div className="bd3d-wrap">
      <Canvas
        shadows
        dpr={[1, 1.75]}
        camera={{ position: [22.2, 17.6, 26.7], fov: 38 }}
        gl={{ antialias: true, alpha: true }}
      >
        <ambientLight intensity={0.55} />
        <directionalLight position={[14, 22, 12]} intensity={1.1} castShadow shadow-mapSize={[2048, 2048]}
          shadow-camera-left={-26} shadow-camera-right={26} shadow-camera-top={26} shadow-camera-bottom={-26} />
        <directionalLight position={[-14, 10, -10]} intensity={0.3} />
        <hemisphereLight args={['#d3e0f0', '#d3e0f0', 0.35]} />

        <Building />

        <Grid position={[0, -0.62, 0]} args={[80, 80]} cellSize={0.8} cellThickness={0.6} cellColor="#d3e0f0"
          sectionSize={4} sectionThickness={1} sectionColor="#eef2f8" fadeDistance={40} fadeStrength={2.5} infiniteGrid />

        <OrbitControls makeDefault autoRotate={interactive} autoRotateSpeed={0.5} enablePan={false} enableZoom={interactive}
          minDistance={10} maxDistance={55} maxPolarAngle={Math.PI / 2.05} minPolarAngle={Math.PI / 7} target={[0, 1.6, 0]} />
      </Canvas>
    </div>
  );
}