// ============================================================
// FloorPlan3D2 — alternative isometric "digital twin" view
// SVG-based 2.5D rendering of cfg.groups[].racks as a server room
// matching the polished reference dashboard layout.
// ============================================================
const { useState: useStateF2, useMemo: useMemoF2, useRef: useRefF2, useEffect: useEffectF2 } = React;

// ── Iso projection ─────────────────────────────────────────
const F2_DX = 0.46;
const F2_DY = -0.84;
function p3v2(x, y, z) { return [x + y * F2_DX, -z + y * F2_DY]; }

// rack dimensions in world units
const F2_RACK_W = 68;
const F2_RACK_D = 56;
const F2_RACK_H = 280;
const F2_GAP_X  = 12;
const F2_GAP_Y_ROWS = 150;

// ── Inline icons (only the ones we need inside the dashboard) ──
const F2Icon = {
  Search: ({ size = 14 }) => (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <circle cx="11" cy="11" r="7"/><path d="M21 21l-4.3-4.3"/>
    </svg>
  ),
  Chevron: ({ size = 12 }) => (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <polyline points="6 9 12 15 18 9"/>
    </svg>
  ),
  Close: ({ size = 14 }) => (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
      <path d="M5 5l14 14M19 5L5 19"/>
    </svg>
  ),
  Menu: ({ size = 14 }) => (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="currentColor">
      <circle cx="5" cy="12" r="1.4"/><circle cx="12" cy="12" r="1.4"/><circle cx="19" cy="12" r="1.4"/>
    </svg>
  ),
  Cube: ({ size = 14 }) => (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round">
      <path d="M12 3l8 4.5v9L12 21l-8-4.5v-9L12 3z"/>
      <path d="M12 12l8-4.5M12 12l-8-4.5M12 12v9"/>
    </svg>
  ),
  Temp: ({ size = 14 }) => (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
      <path d="M14 4a2 2 0 10-4 0v9.5a4 4 0 104 0V4z"/>
      <circle cx="12" cy="17" r="1.8" fill="currentColor"/>
    </svg>
  ),
  Humidity: ({ size = 14 }) => (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round">
      <path d="M12 3.5c-3.5 4.5-6 7.5-6 11a6 6 0 0012 0c0-3.5-2.5-6.5-6-11z"/>
    </svg>
  ),
  Power: ({ size = 14 }) => (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
      <path d="M13 3l-7 11h5l-1 7 7-11h-5l1-7z"/>
    </svg>
  ),
  Map: ({ size = 14 }) => (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round">
      <path d="M9 4L3 6v14l6-2 6 2 6-2V4l-6 2-6-2z"/>
      <path d="M9 4v14M15 6v14"/>
    </svg>
  ),
  Warn: ({ size = 12, color = '#F5B942' }) => (
    <svg width={size} height={size} viewBox="0 0 24 24" fill={color}>
      <path d="M12 3l10 18H2L12 3z"/>
      <path d="M12 10v5M12 17.5v.5" stroke="#0B121F" strokeWidth="2" strokeLinecap="round"/>
    </svg>
  ),
};

// ── Derive a flat rack list from cfg.groups ────────────────
function f2DeriveRacks(cfg) {
  const out = [];
  for (const g of cfg.groups) {
    for (const r of g.racks) {
      const rh = (window.rackHealth || (() => ({ level: 'empty', total: 0, susp: 0 })))(r);
      const devices = rh.total || 0;
      let status = 'empty';
      if (rh.level === 'bad') status = 'warn';
      else if (rh.level === 'warn') status = 'warn';
      else if (rh.level === 'ok') status = 'normal';
      else if (devices > 0) status = 'normal';
      out.push({
        id: r.id,
        label: (window.aliasOr ? window.aliasOr(r, r.label) : r.label),
        groupId: g.id,
        groupTag: g.tag,
        rackRef: r,
        groupRef: g,
        devices,
        status,
        absent: !!r.absent,
      });
    }
  }
  return out;
}

// ── Derive port states (top/bot of 24) from rack's first switch slot ──
function f2DerivePorts(rack) {
  if (!rack || rack.absent) return { top: Array(24).fill('empty'), bot: Array(24).fill('empty') };
  const sw = rack.slots.find(s => s.kind === 'switch' && s.ports && s.ports.length > 0);
  if (!sw) return { top: Array(24).fill('empty'), bot: Array(24).fill('empty') };
  const top = new Array(24).fill('empty');
  const bot = new Array(24).fill('empty');
  const norm = window.normPort || ((p) => p && typeof p === 'object' ? p : { plug: 'empty', led: 'off' });
  for (let i = 0; i < sw.ports.length; i++) {
    const p = norm(sw.ports[i]);
    const isOdd = i % 2 === 0;
    const col = Math.floor(i / 2);
    if (col >= 24) break;
    let state = 'empty';
    if (p.led === 'orange' || p.led === 'orange-blink') state = 'warning';
    else if (p.plug === 'rj45' && p.led === 'green') state = 'connected';
    else if (p.plug === 'sfp') state = 'module';
    else if (p.plug === 'rj45') state = 'connected';
    if (isOdd) top[col] = state; else bot[col] = state;
  }
  return { top, bot };
}

// ── Build LED arrays (System + Ports) from a switch slot ───
function f2DeriveLeds(rack) {
  if (!rack || rack.absent) return { system: Array(48).fill('off'), ports: Array(48).fill('off') };
  const sw = rack.slots.find(s => s.kind === 'switch' && s.ports && s.ports.length > 0);
  const system = Array(48).fill('on');
  const ports = Array(48).fill('on');
  if (sw) {
    const norm = window.normPort || ((p) => p);
    sw.ports.slice(0, 48).forEach((p, i) => {
      const n = norm(p);
      if (n.led === 'orange' || n.led === 'orange-blink') ports[i] = 'amber';
      else if (n.led === 'off' && n.plug === 'empty') ports[i] = 'off';
      else ports[i] = 'on';
    });
    // sprinkle 1-2 amber in system based on susp count
    const susp = sw.ports.filter(p => norm(p).led === 'orange').length;
    if (susp > 0) {
      system[27] = 'amber';
      system[28] = 'amber';
    }
  }
  return { system, ports };
}

// ============================================================
// Single rack (SVG group) at given world (worldX, worldY)
// ============================================================
function F2RackGroup({ rackInfo, selected, onSelect, worldX, worldY }) {
  const id = rackInfo.label;
  const tx = (x) => x + worldX;
  const ty = (y) => y + worldY;

  const FBL = p3v2(tx(0),         ty(0),         0);
  const FBR = p3v2(tx(F2_RACK_W), ty(0),         0);
  const FTL = p3v2(tx(0),         ty(0),         F2_RACK_H);
  const FTR = p3v2(tx(F2_RACK_W), ty(0),         F2_RACK_H);
  const BBR = p3v2(tx(F2_RACK_W), ty(F2_RACK_D), 0);
  const BTR = p3v2(tx(F2_RACK_W), ty(F2_RACK_D), F2_RACK_H);
  const BTL = p3v2(tx(0),         ty(F2_RACK_D), F2_RACK_H);

  const f2p = (...pts) => pts.map(pt => pt.join(',')).join(' ');

  // Device rows derived from rack.slots (in U-order: top of rack = U_max → bottom = U1)
  const rack = rackInfo.rackRef;
  const norm = window.normPort || ((p) => p);
  const devices = [];
  if (rack && !rack.absent) {
    rack.slots.forEach((s, i) => {
      if (s.kind === 'empty') {
        devices.push({ kind: 'empty', name: '', power: 'off' });
        return;
      }
      let kind = 'device';
      if (s.kind === 'switch') kind = (rackInfo.label === 'RG-01-B' && i === 0) ? 'switch1' : 'switch';
      else if (s.kind === 'server') kind = 'server';
      const leds = s.ports && s.ports.length > 0
        ? s.ports.slice(0, 8).map(p => {
            const n = norm(p);
            return n.led === 'orange' || n.led === 'orange-blink' ? 'amber'
                 : n.led === 'green' ? 'on'
                 : n.plug === 'rj45' ? 'on' : 'off';
          })
        : ['on', 'on', s.power === 'on' ? 'on' : 'off', 'on'];
      devices.push({ kind, name: s.name || '', power: s.power || 'off', leds });
    });
    while (devices.length < 8) devices.push({ kind: 'empty', name: '', power: 'off' });
  } else {
    for (let i = 0; i < 8; i++) devices.push({ kind: 'empty', name: '', power: 'off' });
  }

  const FRONT_PAD_X = 5;
  const FRONT_PAD_TOP = 20;
  const FRONT_PAD_BOTTOM = 22;
  const slotsHeight = F2_RACK_H - FRONT_PAD_TOP - FRONT_PAD_BOTTOM;
  const slotH = slotsHeight / 8;

  const stroke = selected ? '#28D8FF' : 'rgba(110, 140, 180, 0.22)';
  const strokeW = selected ? 1.4 : 0.5;

  const slot3 = (i) => {
    const zBot = F2_RACK_H - FRONT_PAD_TOP - i * slotH;
    const zTop = zBot - slotH;
    const xL = FRONT_PAD_X + 2;
    const xR = F2_RACK_W - FRONT_PAD_X - 2;
    return [
      p3v2(tx(xL), ty(0), zBot),
      p3v2(tx(xR), ty(0), zBot),
      p3v2(tx(xR), ty(0), zTop),
      p3v2(tx(xL), ty(0), zTop),
    ];
  };

  const innerArea = [
    p3v2(tx(FRONT_PAD_X),              ty(0), F2_RACK_H - FRONT_PAD_TOP),
    p3v2(tx(F2_RACK_W - FRONT_PAD_X),  ty(0), F2_RACK_H - FRONT_PAD_TOP),
    p3v2(tx(F2_RACK_W - FRONT_PAD_X),  ty(0), FRONT_PAD_BOTTOM),
    p3v2(tx(FRONT_PAD_X),              ty(0), FRONT_PAD_BOTTOM),
  ];

  const ventArea = [
    p3v2(tx(FRONT_PAD_X),              ty(0), FRONT_PAD_BOTTOM - 4),
    p3v2(tx(F2_RACK_W - FRONT_PAD_X),  ty(0), FRONT_PAD_BOTTOM - 4),
    p3v2(tx(F2_RACK_W - FRONT_PAD_X),  ty(0), 8),
    p3v2(tx(FRONT_PAD_X),              ty(0), 8),
  ];

  const isWarn = rackInfo.status === 'warn';

  return (
    <g onClick={onSelect} style={{ cursor: 'pointer' }} data-rack-id={rackInfo.id}>
      {/* Right side */}
      <polygon
        points={f2p(FBR, BBR, BTR, FTR)}
        fill={selected ? 'url(#f2-sideGradSel)' : 'url(#f2-sideGrad)'}
        stroke={stroke} strokeWidth={strokeW}
      />

      {/* Top face */}
      <polygon
        points={f2p(FTL, FTR, BTR, BTL)}
        fill={selected ? 'url(#f2-topGradSel)' : 'url(#f2-topGrad)'}
        stroke={stroke} strokeWidth={strokeW}
      />
      {/* Top vent stripes */}
      {Array.from({ length: 11 }).map((_, i) => {
        const fx = (i + 1) / 12;
        const a = p3v2(tx(F2_RACK_W * fx), ty(0),         F2_RACK_H);
        const b = p3v2(tx(F2_RACK_W * fx), ty(F2_RACK_D), F2_RACK_H);
        return <line key={`tv${i}`} x1={a[0]} y1={a[1]} x2={b[0]} y2={b[1]} stroke="rgba(0,0,0,0.35)" strokeWidth="0.4"/>;
      })}
      {/* Top cyan accent LED */}
      {(() => {
        const a = p3v2(tx(F2_RACK_W * 0.18), ty(F2_RACK_D * 0.25), F2_RACK_H);
        const b = p3v2(tx(F2_RACK_W * 0.36), ty(F2_RACK_D * 0.25), F2_RACK_H);
        return (
          <line x1={a[0]} y1={a[1]} x2={b[0]} y2={b[1]}
                stroke={selected ? '#28D8FF' : 'rgba(40, 216, 255, 0.5)'}
                strokeWidth="1.4" strokeLinecap="round"
                style={{ filter: 'drop-shadow(0 0 2.5px #28D8FF)' }}/>
        );
      })()}

      {/* Front face */}
      <polygon
        points={f2p(FBL, FBR, FTR, FTL)}
        fill={selected ? 'url(#f2-frontGradSel)' : 'url(#f2-frontGrad)'}
        stroke={stroke} strokeWidth={strokeW}
      />

      {/* Front recessed area */}
      <polygon
        points={f2p(...innerArea)}
        fill="#04080F"
        stroke="rgba(110, 140, 180, 0.18)" strokeWidth="0.4"
      />

      {/* Device slots */}
      {devices.map((d, i) => {
        const corners = slot3(i);
        if (d.kind === 'empty') {
          return <polygon key={i} points={f2p(...corners)} fill="#05080F"
                          stroke="rgba(110, 140, 180, 0.08)" strokeWidth="0.25"/>;
        }
        const isSwitch = d.kind === 'switch' || d.kind === 'switch1';
        const isSwitch1 = d.kind === 'switch1';
        const isServer = d.kind === 'server';
        const isOff = d.power === 'off';

        // Visual differentiation: switch=cyan band, server=green band
        const bandCol = isSwitch ? '#28D8FF' : isServer ? '#00E68A' : '#9AAEC8';
        const bandAlpha = isOff ? 0.30 : 0.90;
        const slotFill = isSwitch1 ? '#0E1828' : isOff ? '#08101B' : '#10182A';
        const slotStroke = isSwitch1
          ? 'rgba(40, 216, 255, 0.55)'
          : isOff ? 'rgba(120, 150, 200, 0.10)' : 'rgba(120, 150, 200, 0.22)';

        // Slot interior left edge (where the type band goes)
        const zCenter = F2_RACK_H - FRONT_PAD_TOP - (i + 0.5) * slotH;
        const zBot = F2_RACK_H - FRONT_PAD_TOP - i * slotH;
        const zTop = zBot - slotH;
        const bandLeft = FRONT_PAD_X + 2;
        const bandRight = bandLeft + 1.6;
        const bandCorners = [
          p3v2(tx(bandLeft),  ty(0), zBot - 1),
          p3v2(tx(bandRight), ty(0), zBot - 1),
          p3v2(tx(bandRight), ty(0), zTop + 1),
          p3v2(tx(bandLeft),  ty(0), zTop + 1),
        ];

        // Text label position (right side of LEDs, on the slot)
        const labelP = p3v2(tx(F2_RACK_W - FRONT_PAD_X - 4), ty(0), zCenter);

        return (
          <g key={i}>
            <polygon
              points={f2p(...corners)}
              fill={slotFill}
              stroke={slotStroke}
              strokeWidth="0.4"
            />
            {/* Type band (left edge accent) */}
            <polygon
              points={f2p(...bandCorners)}
              fill={bandCol}
              opacity={bandAlpha}
              style={isOff ? {} : { filter: `drop-shadow(0 0 1.6px ${bandCol})` }}
            />
            {/* LEDs */}
            {d.leds && d.leds.map((l, j) => {
              const xLed = FRONT_PAD_X + 5.5 + j * 1.9;
              const ledP = p3v2(tx(xLed), ty(0), zCenter);
              const ledOff = l === 'off' || isOff;
              const color = l === 'amber' ? '#F5B942' : ledOff ? '#1A2238' : '#00E68A';
              return (
                <circle key={j} cx={ledP[0]} cy={ledP[1]} r="0.7" fill={color}
                  style={ledOff ? {} : { filter: `drop-shadow(0 0 1.2px ${color})` }}/>
              );
            })}
            {/* Device name text (only visible at high zoom; tiny but readable) */}
            {d.name && (
              <text
                x={labelP[0]} y={labelP[1] + 1.2}
                textAnchor="end"
                fontFamily="'JetBrains Mono', monospace"
                fontSize="2.8"
                fontWeight="500"
                letterSpacing="0.05em"
                fill={isOff ? 'rgba(154, 174, 200, 0.35)' : 'rgba(232, 241, 255, 0.85)'}
              >
                {d.name}
              </text>
            )}
          </g>
        );
      })}

      {/* Bottom vent */}
      <polygon
        points={f2p(...ventArea)}
        fill="#08101C"
        stroke="rgba(110, 140, 180, 0.18)" strokeWidth="0.35"
      />
      {Array.from({ length: 9 }).map((_, i) => {
        const xv = FRONT_PAD_X + 2 + (i + 1) * ((F2_RACK_W - FRONT_PAD_X * 2 - 4) / 10);
        const a = p3v2(tx(xv), ty(0), FRONT_PAD_BOTTOM - 6);
        const b = p3v2(tx(xv), ty(0), 10);
        return <line key={`bv${i}`} x1={a[0]} y1={a[1]} x2={b[0]} y2={b[1]} stroke="rgba(120, 150, 200, 0.22)" strokeWidth="0.35"/>;
      })}

      {/* Selected corner brackets */}
      {selected && (
        <g stroke="#28D8FF" strokeWidth="1.6" fill="none" style={{ filter: 'drop-shadow(0 0 3px #28D8FF)' }}>
          <path d={`M ${FTL[0]-3},${FTL[1]+10} L ${FTL[0]-3},${FTL[1]-3} L ${FTL[0]+10},${FTL[1]-3}`}/>
          <path d={`M ${FTR[0]-10},${FTR[1]-3} L ${FTR[0]+3},${FTR[1]-3} L ${FTR[0]+3},${FTR[1]+10}`}/>
          <path d={`M ${FBL[0]-3},${FBL[1]-10} L ${FBL[0]-3},${FBL[1]+3} L ${FBL[0]+10},${FBL[1]+3}`}/>
          <path d={`M ${FBR[0]-10},${FBR[1]+3} L ${FBR[0]+3},${FBR[1]+3} L ${FBR[0]+3},${FBR[1]-10}`}/>
        </g>
      )}

      {/* Rack label */}
      <text
        x={(FTL[0] + FTR[0]) / 2 + 4}
        y={FTL[1] - 8}
        textAnchor="middle"
        fontFamily="'JetBrains Mono', monospace"
        fontSize="9"
        fontWeight="600"
        letterSpacing="1"
        fill={selected ? '#28D8FF' : isWarn ? '#F5B942' : '#9AAEC8'}
        style={selected ? { filter: 'drop-shadow(0 0 3px #28D8FF)' } : {}}
      >
        {id}
      </text>
    </g>
  );
}

// ============================================================
// The 3D scene
// ============================================================
function F2Scene({ racksList, selectedRackId, onSelectRack }) {
  const wrapRef = useRefF2(null);
  const transformRef = useRefF2(null);
  const [overlay, setOverlay] = useStateF2(null);
  const [view, setView] = useStateF2({ scale: 1, tx: 0, ty: 0 });
  const dragRef = useRefF2(null);

  // Recompute the connection-line overlay whenever selection, list size, or view changes
  useEffectF2(() => {
    function update() {
      if (!wrapRef.current) return;
      const sel = wrapRef.current.querySelector(`[data-rack-id="${selectedRackId}"]`);
      if (!sel) { setOverlay(null); return; }
      const r = sel.getBoundingClientRect();
      const w = wrapRef.current.getBoundingClientRect();
      setOverlay({
        startX: r.right - w.left,
        startY: r.top  - w.top  + r.height * 0.45,
        endX:   w.width,
        endY:   r.top  - w.top  + r.height * 0.45,
      });
    }
    const t = setTimeout(update, 50);
    window.addEventListener('resize', update);
    return () => { clearTimeout(t); window.removeEventListener('resize', update); };
  }, [selectedRackId, racksList.length, view]);

  // ---- Wheel zoom (around cursor) ----
  const onWheel = (e) => {
    e.preventDefault();
    if (!wrapRef.current) return;
    const w = wrapRef.current.getBoundingClientRect();
    const cx = w.width / 2, cy = w.height / 2;
    const mx = e.clientX - w.left, my = e.clientY - w.top;
    const delta = -e.deltaY * 0.0015;
    setView(v => {
      const sNew = Math.max(0.5, Math.min(4, v.scale * (1 + delta)));
      // Keep the point under cursor fixed (transform-origin = center cx,cy)
      const tx = v.tx + (mx - cx - v.tx) * (1 - sNew / v.scale);
      const ty = v.ty + (my - cy - v.ty) * (1 - sNew / v.scale);
      return { scale: sNew, tx, ty };
    });
  };

  // ---- Drag pan ----
  const onMouseDown = (e) => {
    if (e.button !== 0) return;
    // Don't pan if the click is on a rack (let select fire first)
    if (e.target.closest && e.target.closest('[data-rack-id]')) return;
    dragRef.current = {
      startX: e.clientX, startY: e.clientY,
      tx: view.tx, ty: view.ty, moved: false,
    };
    e.preventDefault();
  };
  const onMouseMove = (e) => {
    if (!dragRef.current) return;
    const dx = e.clientX - dragRef.current.startX;
    const dy = e.clientY - dragRef.current.startY;
    if (Math.abs(dx) > 2 || Math.abs(dy) > 2) dragRef.current.moved = true;
    setView(v => ({ ...v, tx: dragRef.current.tx + dx, ty: dragRef.current.ty + dy }));
  };
  const onMouseUp = () => { dragRef.current = null; };

  // Wheel listener must be non-passive (so preventDefault works)
  useEffectF2(() => {
    const el = wrapRef.current;
    if (!el) return;
    const handler = (e) => onWheel(e);
    el.addEventListener('wheel', handler, { passive: false });
    return () => el.removeEventListener('wheel', handler);
  }, []);
  useEffectF2(() => {
    window.addEventListener('mousemove', onMouseMove);
    window.addEventListener('mouseup', onMouseUp);
    return () => {
      window.removeEventListener('mousemove', onMouseMove);
      window.removeEventListener('mouseup', onMouseUp);
    };
  }, []);

  const resetView = () => setView({ scale: 1, tx: 0, ty: 0 });

  // Layout: 2 rows × 8 cols
  const positions = racksList.slice(0, 16).map((r, idx) => {
    const row = Math.floor(idx / 8);
    const col = idx % 8;
    const worldX = col * (F2_RACK_W + F2_GAP_X);
    const worldY = (1 - row) * (F2_RACK_D + F2_GAP_Y_ROWS);
    return { rackInfo: r, worldX, worldY, row, col };
  });

  if (positions.length === 0) {
    return (
      <div className="scene-wrap-f2" ref={wrapRef}>
        <div style={{ color: '#5D7290', fontFamily: 'var(--font-mono)', fontSize: 12, letterSpacing: '0.1em' }}>
          NO RACK DATA
        </div>
      </div>
    );
  }

  // Compute world bounds for floor
  const allWX = positions.flatMap(p => [p.worldX, p.worldX + F2_RACK_W]);
  const allWY = positions.flatMap(p => [p.worldY, p.worldY + F2_RACK_D]);
  const wxMin = Math.min(...allWX), wxMax = Math.max(...allWX);
  const wyMin = Math.min(...allWY), wyMax = Math.max(...allWY);
  const floorPad = 70;
  const fX1 = wxMin - floorPad, fX2 = wxMax + floorPad;
  const fY1 = wyMin - floorPad, fY2 = wyMax + floorPad;

  // Project bounds
  const projected = [];
  positions.forEach(p => {
    projected.push(p3v2(p.worldX, p.worldY, 0));
    projected.push(p3v2(p.worldX + F2_RACK_W, p.worldY, 0));
    projected.push(p3v2(p.worldX, p.worldY, F2_RACK_H));
    projected.push(p3v2(p.worldX + F2_RACK_W, p.worldY, F2_RACK_H));
    projected.push(p3v2(p.worldX, p.worldY + F2_RACK_D, F2_RACK_H));
    projected.push(p3v2(p.worldX + F2_RACK_W, p.worldY + F2_RACK_D, F2_RACK_H));
  });
  projected.push(p3v2(fX1, fY1, 0));
  projected.push(p3v2(fX2, fY1, 0));
  projected.push(p3v2(fX2, fY2, 0));
  projected.push(p3v2(fX1, fY2, 0));
  const xs = projected.map(p => p[0]);
  const ys = projected.map(p => p[1]);
  const minX = Math.min(...xs) - 20;
  const maxX = Math.max(...xs) + 20;
  const minY = Math.min(...ys) - 24;
  const maxY = Math.max(...ys) + 12;
  const sceneW = maxX - minX;
  const sceneH = maxY - minY;

  // Floor corners
  const F1 = p3v2(fX1, fY1, 0);
  const F2 = p3v2(fX2, fY1, 0);
  const F3 = p3v2(fX2, fY2, 0);
  const F4 = p3v2(fX1, fY2, 0);

  const sel = positions.find(p => p.rackInfo.id === selectedRackId);

  // Back-to-front sort
  const sorted = [...positions].sort((a, b) => b.worldY - a.worldY || a.worldX - b.worldX);

  return (
    <div
      className="scene-wrap-f2"
      ref={wrapRef}
      onMouseDown={onMouseDown}
      style={{ cursor: dragRef.current ? 'grabbing' : 'grab' }}
    >
      <div
        ref={transformRef}
        className="scene-transform-f2"
        style={{
          width: '100%', height: '100%',
          transform: `translate(${view.tx}px, ${view.ty}px) scale(${view.scale})`,
          transformOrigin: 'center center',
          transition: dragRef.current ? 'none' : 'transform 0.06s linear',
        }}
      >
      <svg
        className="scene-svg-f2"
        viewBox={`${minX} ${minY} ${sceneW} ${sceneH}`}
        preserveAspectRatio="xMidYMax meet"
        style={{ width: '100%', height: '100%' }}
      >
        <defs>
          <linearGradient id="f2-topGrad" x1="0" y1="0" x2="0.5" y2="1">
            <stop offset="0%"  stopColor="#262F45" />
            <stop offset="55%" stopColor="#161E33" />
            <stop offset="100%" stopColor="#0E1424" />
          </linearGradient>
          <linearGradient id="f2-topGradSel" x1="0" y1="0" x2="0.5" y2="1">
            <stop offset="0%"  stopColor="#2A3650" />
            <stop offset="55%" stopColor="#192340" />
            <stop offset="100%" stopColor="#10182E" />
          </linearGradient>
          <linearGradient id="f2-frontGrad" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%"  stopColor="#0F1525" />
            <stop offset="100%" stopColor="#070C18" />
          </linearGradient>
          <linearGradient id="f2-frontGradSel" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%"  stopColor="#102035" />
            <stop offset="100%" stopColor="#081020" />
          </linearGradient>
          <linearGradient id="f2-sideGrad" x1="0" y1="0" x2="1" y2="0">
            <stop offset="0%" stopColor="#0A1020" />
            <stop offset="100%" stopColor="#04080F" />
          </linearGradient>
          <linearGradient id="f2-sideGradSel" x1="0" y1="0" x2="1" y2="0">
            <stop offset="0%" stopColor="#0D1628" />
            <stop offset="100%" stopColor="#050A14" />
          </linearGradient>
          <linearGradient id="f2-floorBase" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor="#06091A"/>
            <stop offset="100%" stopColor="#02040A"/>
          </linearGradient>
          <radialGradient id="f2-floorVignette" cx="0.5" cy="0.5" r="0.7">
            <stop offset="0%" stopColor="rgba(0,0,0,0)"/>
            <stop offset="100%" stopColor="rgba(2, 4, 8, 1)"/>
          </radialGradient>
        </defs>

        {/* Floor */}
        <polygon
          points={`${F1.join(',')} ${F2.join(',')} ${F3.join(',')} ${F4.join(',')}`}
          fill="url(#f2-floorBase)"
        />

        {/* Floor grid */}
        {(() => {
          const lines = [];
          const step = 40;
          for (let x = fX1; x <= fX2 + 0.01; x += step) {
            const a = p3v2(x, fY1, 0);
            const b = p3v2(x, fY2, 0);
            lines.push(<line key={`gx${x}`} x1={a[0]} y1={a[1]} x2={b[0]} y2={b[1]}
                              stroke="rgba(170, 200, 255, 0.04)" strokeWidth="0.4"/>);
          }
          for (let y = fY1; y <= fY2 + 0.01; y += step) {
            const a = p3v2(fX1, y, 0);
            const b = p3v2(fX2, y, 0);
            lines.push(<line key={`gy${y}`} x1={a[0]} y1={a[1]} x2={b[0]} y2={b[1]}
                              stroke="rgba(170, 200, 255, 0.04)" strokeWidth="0.4"/>);
          }
          return lines;
        })()}

        {/* Selected floor tile */}
        {sel && (() => {
          const A = p3v2(sel.worldX - 4,           sel.worldY - 4,           0);
          const B = p3v2(sel.worldX + F2_RACK_W + 4,  sel.worldY - 4,        0);
          const C = p3v2(sel.worldX + F2_RACK_W + 4,  sel.worldY + F2_RACK_D + 4, 0);
          const D = p3v2(sel.worldX - 4,           sel.worldY + F2_RACK_D + 4, 0);
          return (
            <polygon
              points={`${A.join(',')} ${B.join(',')} ${C.join(',')} ${D.join(',')}`}
              fill="rgba(40, 216, 255, 0.05)"
              stroke="rgba(40, 216, 255, 0.45)"
              strokeWidth="0.5"
              strokeDasharray="2.5 2.5"
            />
          );
        })()}

        <rect x={minX} y={minY} width={sceneW} height={sceneH} fill="url(#f2-floorVignette)" pointerEvents="none"/>

        {/* Racks back-to-front */}
        {sorted.map(p => (
          <F2RackGroup
            key={p.rackInfo.id}
            rackInfo={p.rackInfo}
            selected={selectedRackId === p.rackInfo.id}
            onSelect={() => onSelectRack(p.rackInfo.id)}
            worldX={p.worldX}
            worldY={p.worldY}
          />
        ))}

        {/* Floor beacons */}
        {(() => {
          const beacons = [
            p3v2(fX1 + 30, fY2 - 20, 8),
            p3v2(fX2 - 40, fY1 + 30, 8),
            p3v2(fX1 + 80, fY1 + 20, 8),
            p3v2(fX2 - 80, fY2 - 30, 8),
          ];
          return beacons.map((b, i) => (
            <g key={i}>
              <circle cx={b[0]} cy={b[1]} r="2.4" fill="#28D8FF" style={{ filter: 'drop-shadow(0 0 4px #28D8FF)' }}/>
              <circle cx={b[0]} cy={b[1]} r="5" fill="none" stroke="rgba(40, 216, 255, 0.3)" strokeWidth="0.5"/>
            </g>
          ));
        })()}
      </svg>
      </div>

      {/* Zoom / Pan controls */}
      <div className="scene-controls-f2">
        <button className="sc-btn"
                onClick={() => setView(v => ({ ...v, scale: Math.min(4, v.scale * 1.2) }))}
                title="Zoom in">+</button>
        <button className="sc-btn"
                onClick={() => setView(v => ({ ...v, scale: Math.max(0.5, v.scale / 1.2) }))}
                title="Zoom out">−</button>
        <button className="sc-btn reset" onClick={resetView} title="Reset view">⌂</button>
        <div className="sc-zoom">{Math.round(view.scale * 100)}%</div>
      </div>

      {/* Connection line overlay */}
      {overlay && (
        <svg className="connection-overlay-f2"
             style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', pointerEvents: 'none', overflow: 'visible' }}>
          <g style={{ filter: 'drop-shadow(0 0 4px #28D8FF)' }}>
            <line x1={overlay.startX} y1={overlay.startY}
                  x2={overlay.endX + 60} y2={overlay.endY}
                  stroke="#28D8FF" strokeWidth="1" opacity="0.85"/>
            <circle cx={overlay.startX} cy={overlay.startY} r="3" fill="#28D8FF"/>
            <circle cx={overlay.startX} cy={overlay.startY} r="6" fill="none" stroke="rgba(40, 216, 255, 0.4)" strokeWidth="1"/>
          </g>
        </svg>
      )}
    </div>
  );
}

// ============================================================
// Sidebar (DC header + search + rack list)
// ============================================================
function F2Sidebar({ cfg, racksList, selectedRackId, onSelectRack }) {
  return (
    <aside className="sidebar-f2">
      <div className="sidebar-panel-f2">
        <div className="dc-header-f2">
          <div className="dc-title-f2">
            DATA CENTER 01
            <F2Icon.Chevron size={12} />
          </div>
          <div className="dc-sub-f2">PDU-01 · 208V / 30A · BUSBAR</div>
        </div>

        <div className="search-f2">
          <div className="search-input-f2">
            <F2Icon.Search size={14} />
            <input placeholder="Search racks or devices..." />
          </div>
        </div>

        <div className="racks-header-f2">
          <span>RACKS</span>
          <span className="count">{racksList.length} Total</span>
        </div>

        <div className="racks-list-f2">
          {racksList.map((r) => (
            <div
              key={r.id}
              className={`rack-row-f2 ${selectedRackId === r.id ? 'selected' : ''}`}
              onClick={() => onSelectRack(r.id)}
            >
              <span className={`status-dot-f2 ${r.status}`}></span>
              <span className="name">{r.label}</span>
              <span className="meta">{r.devices} Devices</span>
            </div>
          ))}
        </div>
      </div>
    </aside>
  );
}

// ============================================================
// Detail panel (right side)
// ============================================================
function F2DetailPanel({ rackInfo, onClose }) {
  const [selectedU, setSelectedU] = useStateF2(null);

  if (!rackInfo) return null;
  const rack = rackInfo.rackRef;
  const slots = rack && !rack.absent ? rack.slots : [];
  const K = slots.length;
  // Render in U-order top to bottom (Uxx descending)
  const units = slots.map((s, i) => ({
    u: `U${String(K - i).padStart(2, '0')}`,
    idx: i,
    type: s.kind === 'switch' ? 'SW' : s.kind === 'server' ? 'SRV' : null,
    name: s.kind === 'empty' ? 'EMPTY' : (s.name || s.kind),
    slot: s,
  }));

  const defaultSelU = units.find(u => u.type)?.u || units[0]?.u;
  const curU = selectedU || defaultSelU;
  const selUnit = units.find(u => u.u === curU);
  const selSlot = selUnit?.slot;

  // Ports + LEDs based on selected unit (if it's a switch) else first switch
  const portSource = selSlot?.kind === 'switch' ? rack
    : rack;
  const PORTS = useMemoF2(() => f2DerivePorts(rack), [rack]);
  const LEDS = useMemoF2(() => f2DeriveLeds(rack), [rack]);

  // Tick labels
  const topTickLabels = { 0: '1', 2: '5', 4: '10', 7: '15', 9: '20', 12: '25', 14: '30', 17: '35', 19: '40', 22: '45', 23: '48' };
  const botTickLabels = { 0: '2', 2: '6', 4: '11', 7: '16', 9: '21', 12: '26', 14: '31', 17: '36', 19: '41', 22: '46', 23: '48' };

  const status = rackInfo.status === 'warn' ? 'Warning' : 'Normal';
  const statusColor = rackInfo.status === 'warn' ? '#F5B942' : '#00E68A';

  return (
    <aside className="detail-panel-f2">
      <div className="dp-header-f2">
        <div>
          <div className="dp-title-f2">{rackInfo.label}</div>
          <div className="dp-sub-f2">{rackInfo.groupTag === 'RG-01' ? 'Rack Group 01' : rackInfo.groupRef.alias || rackInfo.groupTag}</div>
        </div>
        <div style={{ display: 'flex', alignItems: 'flex-start' }}>
          <div className="dp-status-f2">
            <span className="label">Status</span>
            <span className="value">
              <span className="dot" style={{ background: statusColor, boxShadow: `0 0 8px ${statusColor}` }}></span>
              {status}
            </span>
          </div>
          <button className="dp-close-f2" onClick={onClose}><F2Icon.Close /></button>
        </div>
      </div>

      <div className="dp-body-f2">
        <div className="rack-overview-f2">
          <div className="dp-section-title-f2">RACK OVERVIEW</div>
          {units.map((u) => (
            <div
              key={u.u}
              className={`ru-row-f2 ${curU === u.u ? 'selected' : ''} ${u.name === 'EMPTY' ? 'empty' : ''}`}
              onClick={() => setSelectedU(u.u)}
            >
              <span className="u">{u.u}</span>
              <span className="type">{u.type || ''}</span>
              <span className="device-name">{u.name}</span>
            </div>
          ))}
          {units.length === 0 && (
            <div className="ru-row-f2 empty"><span className="u">—</span><span className="type"></span><span className="device-name">ABSENT</span></div>
          )}
        </div>

        <div className="device-details-f2">
          <div className="dp-section-title-f2">DEVICE DETAILS</div>
          {selUnit && selUnit.type ? (
            <>
              <div className="dd-card-f2">
                <span className="type">{selUnit.type}</span>
                <div>
                  <div className="name">{selUnit.name}</div>
                  <div className="desc">{selUnit.type === 'SW' ? 'Network Switch' : 'Server'}</div>
                </div>
                <button className="menu"><F2Icon.Menu /></button>
              </div>

              {selUnit.type === 'SW' && (
                <div className="ports-wrap-f2">
                  <div className="dp-section-title-f2">PORT STATUS</div>
                  <div className="ports-legend-f2">
                    <div className="item"><span className="dot connected"></span>Connected (LAN)</div>
                    <div className="item"><span className="dot module"></span>Module Inserted</div>
                    <div className="item"><span className="dot empty"></span>Empty</div>
                  </div>
                  <div className="port-grid-f2">
                    <div className="port-row-f2">
                      {PORTS.top.map((s, i) => (
                        <div key={`t${i}`} className={`port-cell-f2 ${s !== 'empty' ? s : ''}`}></div>
                      ))}
                    </div>
                    <div className="port-ticks-f2">
                      {Array.from({ length: 24 }).map((_, i) => (
                        <span key={`tt${i}`} className={topTickLabels[i] ? 'show' : ''}>
                          {topTickLabels[i] || '·'}
                        </span>
                      ))}
                    </div>
                    <div className="port-row-f2" style={{ marginTop: 6 }}>
                      {PORTS.bot.map((s, i) => (
                        <div key={`b${i}`} className={`port-cell-f2 ${s !== 'empty' ? s : ''}`}></div>
                      ))}
                    </div>
                    <div className="port-ticks-f2">
                      {Array.from({ length: 24 }).map((_, i) => (
                        <span key={`bt${i}`} className={botTickLabels[i] ? 'show' : ''}>
                          {botTickLabels[i] || '·'}
                        </span>
                      ))}
                    </div>
                  </div>
                </div>
              )}

              <div className="dp-section-title-f2" style={{ marginTop: 14 }}>LED STATUS</div>
              <div className="led-block-f2">
                <div className="heading">System</div>
                <div className="led-row-f2">
                  {LEDS.system.map((s, i) => <span key={i} className={`led-f2 ${s}`}></span>)}
                </div>
                <div className="led-ticks-f2">
                  <span>1</span><span>12</span><span>24</span><span>36</span><span>48</span>
                </div>
              </div>
              <div className="led-block-f2">
                <div className="heading">Ports</div>
                <div className="led-row-f2">
                  {LEDS.ports.map((s, i) => <span key={i} className={`led-f2 ${s}`}></span>)}
                </div>
                <div className="led-ticks-f2">
                  <span>1</span><span>12</span><span>24</span><span>36</span><span>48</span>
                </div>
              </div>
            </>
          ) : (
            <div style={{
              color: 'var(--fg-3)', fontFamily: 'var(--font-mono)', fontSize: 11, padding: '20px 0',
              textAlign: 'center', letterSpacing: '0.1em',
            }}>
              {rack?.absent ? 'RACK ABSENT' : 'EMPTY SLOT — NO DEVICE DETAILS'}
            </div>
          )}
        </div>
      </div>
    </aside>
  );
}

// ============================================================
// Bottom status bar
// ============================================================
function F2StatusBar({ cfg, racksList }) {
  // Compute totals
  let normal = 0, warning = 0, critical = 0, offline = 0;
  for (const r of racksList) {
    if (r.absent) { offline++; continue; }
    if (r.status === 'warn') warning++;
    else if (r.devices > 0) normal++;
    else offline++;
  }

  return (
    <footer className="statusbar-f2">
      <div className="sb-section-f2 left">
        <span className="sb-label-f2">STATUS OVERVIEW</span>
        <div className="sb-stat-f2">
          <span className="key"><span className="dot ok"></span>Normal</span>
          <span className="val">{normal}</span>
        </div>
        <div className="sb-stat-f2">
          <span className="key"><F2Icon.Warn size={12} />Warning</span>
          <span className="val">{warning}</span>
        </div>
        <div className="sb-stat-f2">
          <span className="key"><span className="dot bad"></span>Critical</span>
          <span className="val">{critical}</span>
        </div>
        <div className="sb-stat-f2">
          <span className="key"><span className="dot off"></span>Offline</span>
          <span className="val">{offline}</span>
        </div>
      </div>

      <div className="sb-section-f2 center" style={{ position: 'relative' }}>
        <div className="compass-f2">
          <span>N</span>
          <svg width="16" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.4">
            <path d="M12 4l-4 12 4-3 4 3-4-12z" fill="rgba(40, 216, 255, 0.25)"/>
          </svg>
        </div>
        <div className="toggle-group-f2">
          <button className="toggle-btn-f2"><F2Icon.Cube /></button>
          <button className="toggle-btn-f2 active">3D</button>
          <button className="toggle-btn-f2">2D</button>
        </div>
      </div>

      <div className="sb-section-f2 right">
        <div className="env-stat-f2">
          <span className="icon"><F2Icon.Temp size={14} /></span>
          <div>
            <div className="num">23.4°C</div>
            <div className="name">Temperature</div>
          </div>
        </div>
        <div className="env-stat-f2">
          <span className="icon"><F2Icon.Humidity size={14} /></span>
          <div>
            <div className="num">45%</div>
            <div className="name">Humidity</div>
          </div>
        </div>
        <div className="env-stat-f2">
          <span className="icon"><F2Icon.Power size={14} /></span>
          <div>
            <div className="num">4.2 kW</div>
            <div className="name">Total Power</div>
          </div>
        </div>
        <button className="map-btn-f2">
          <F2Icon.Map />
          View Map
          <F2Icon.Chevron size={12} />
        </button>
      </div>
    </footer>
  );
}

// ============================================================
// Top-level FloorPlan3D2 — assembled dashboard
// ============================================================
function FloorPlan3D2({ cfg, setCfg, onPickGroup, speed, scanState, tweaks }) {
  const racksList = useMemoF2(() => f2DeriveRacks(cfg), [cfg]);

  // Default selection: prefer RG-01-B if exists, else first non-empty rack
  const defaultId = useMemoF2(() => {
    const pref = racksList.find(r => r.label === 'RG-01-B');
    if (pref) return pref.id;
    const firstFilled = racksList.find(r => r.devices > 0);
    return firstFilled?.id || racksList[0]?.id || null;
  }, [racksList]);

  const [selectedRackId, setSelectedRackId] = useStateF2(defaultId);

  // Reset selection if it disappears
  useEffectF2(() => {
    if (!selectedRackId || !racksList.find(r => r.id === selectedRackId)) {
      setSelectedRackId(defaultId);
    }
  }, [defaultId, racksList, selectedRackId]);

  const selectedRack = racksList.find(r => r.id === selectedRackId);

  return (
    <div className="floor3d2" data-screen-label="3D #2">
      <F2Sidebar
        cfg={cfg}
        racksList={racksList}
        selectedRackId={selectedRackId}
        onSelectRack={setSelectedRackId}
      />

      <F2Scene
        racksList={racksList}
        selectedRackId={selectedRackId}
        onSelectRack={setSelectedRackId}
      />

      <F2DetailPanel
        rackInfo={selectedRack}
        onClose={() => setSelectedRackId(null)}
      />

      <F2StatusBar cfg={cfg} racksList={racksList} />
    </div>
  );
}

Object.assign(window, { FloorPlan3D2 });
