// ============================================================
// SAMPLE — Rack view with media buttons + popup
// ============================================================
const { useState: useStateSR, useEffect: useEffectSR, useRef: useRefSR } = React;

const POWER_CYCLE = { on: 'warn', warn: 'off', off: 'on' };

function MediaButton({ count, onClick }) {
  return (
    <button className={`media-btn ${count > 0 ? 'has-media' : ''}`}
            onClick={(e) => { e.stopPropagation(); onClick(e); }}
            title={count > 0 ? `미디어 ${count}개` : '미디어 없음'}>
      <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor"
           strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round">
        <rect x="3" y="3" width="18" height="18" rx="2"/>
        <circle cx="8.5" cy="8.5" r="1.5"/>
        <polyline points="21 15 16 10 5 21"/>
      </svg>
      {count > 0 && <span className="media-btn-count">{count}</span>}
    </button>
  );
}

function MediaPopup({ items, title, onClose, onAdd, onDelete }) {
  const [idx, setIdx] = useStateSR(0);
  const cur = items[idx];

  useEffectSR(() => { setIdx(0); }, [items.length]);

  return (
    <div className="modal-backdrop" onClick={onClose}>
      <div className="media-popup" onClick={(e) => e.stopPropagation()}>
        <div className="media-popup-head">
          <span className="media-popup-title">{title}</span>
          <div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
            {onAdd && (
              <label className="btn small" style={{ cursor: 'pointer' }} title="파일 추가">
                + 추가
                <input type="file" accept="image/*,video/*" multiple style={{ display: 'none' }}
                       onChange={(e) => { onAdd(Array.from(e.target.files)); e.target.value = ''; }} />
              </label>
            )}
            <button className="btn small" onClick={onClose}>✕ 닫기</button>
          </div>
        </div>

        {items.length === 0 ? (
          <div className="media-popup-empty">
            미디어가 없습니다.<br/>
            <span style={{ fontSize: 11, color: 'var(--fg-3)' }}>
              위 "+ 추가" 버튼으로 파일을 추가하세요.
            </span>
          </div>
        ) : (
          <>
            <div className="media-popup-viewer">
              {cur?.type === 'video' ? (
                <video key={cur.src} src={cur.src} controls className="media-popup-media" />
              ) : (
                <img key={cur.src} src={cur.src} alt={cur.name} className="media-popup-media" />
              )}
            </div>
            <div className="media-popup-footer">
              <div className="media-popup-nav">
                <button className="btn small"
                        onClick={() => setIdx(i => Math.max(0, i - 1))}
                        disabled={idx === 0}>‹</button>
                <span className="mono" style={{ fontSize: 11, color: 'var(--fg-2)' }}>
                  {idx + 1} / {items.length} · {cur?.name}
                </span>
                <button className="btn small"
                        onClick={() => setIdx(i => Math.min(items.length - 1, i + 1))}
                        disabled={idx === items.length - 1}>›</button>
              </div>
              {onDelete && cur && (
                <button className="btn small"
                        style={{ color: 'var(--bad)', borderColor: 'var(--bad)' }}
                        onClick={() => { onDelete(cur.id); setIdx(i => Math.max(0, i - 1)); }}>
                  삭제
                </button>
              )}
            </div>
            {items.length > 1 && (
              <div className="media-popup-thumbs">
                {items.map((m, i) => (
                  <div key={m.id}
                       className={`media-thumb ${i === idx ? 'active' : ''}`}
                       onClick={() => setIdx(i)}
                       title={m.name}>
                    {m.type === 'video'
                      ? <div className="media-thumb-video">▶</div>
                      : <img src={m.src} alt={m.name} />}
                  </div>
                ))}
              </div>
            )}
          </>
        )}
      </div>
    </div>
  );
}

function SampleRackColumn({ rack, group, M, K, setCfg,
  editable = false, onToggle, onSelectSlot, selSlotId,
  readOnly = false, apiKey = null,
  portMode = 'plug_led', showPortNums = false }) {
  const [popup, setPopup] = useStateSR(null); // { target:'rack'|slotId, items, title }

  const openRackMedia = () => {
    setPopup({ target: 'rack', items: rack.media || [], title: `${rack.alias || rack.label} · Rack Media` });
  };

  const openSlotMedia = (slot) => {
    setPopup({ target: slot.id, items: slot.media || [], title: `${slot.name || slot.kind} · Asset Media` });
  };

  // Upload to R2 via server — only available in CONFIG (readOnly=false)
  const handleAdd = readOnly ? null : async (files) => {
    if (!apiKey) { alert('CONFIG → File 패널에서 API Key를 먼저 입력하세요.'); return; }
    for (const file of files) {
      try {
        const { src } = await serverUploadMedia(file, apiKey);
        const item = { id: sRid('m'), name: file.name,
                       type: file.type.startsWith('video/') ? 'video' : 'image', src };
        const slotId = popup.target === 'rack' ? null : popup.target;
        setCfg(c => addMedia(c, group.id, rack.id, slotId, item));
        setPopup(p => p ? { ...p, items: [...p.items, item] } : p);
      } catch (e) { alert('업로드 실패: ' + e.message); }
    }
  };

  const handleDelete = readOnly ? null : async (mediaId) => {
    const item = popup?.items?.find(m => m.id === mediaId);
    // Delete from R2 if it's a server-stored file
    if (item?.src?.startsWith('/media/')) await serverDeleteMedia(item.src, apiKey);
    const slotId = popup.target === 'rack' ? null : popup.target;
    setCfg(c => removeMedia(c, group.id, rack.id, slotId, mediaId));
    setPopup(p => p ? { ...p, items: p.items.filter(m => m.id !== mediaId) } : p);
  };

  if (rack.absent) {
    return (
      <div style={{ position: 'relative', display: 'inline-block' }}>
        <AbsentRackPlaceholder rack={rack} K={K} />
      </div>
    );
  }

  // Height of .slot rows — used to align the media-btn column.
  // .rack-head ≈ 38px, each .slot ≈ 86px (80px height + 4px margin-bottom + 2px border)
  const RACK_HEAD_H = 38;
  const SLOT_H = 86;

  return (
    <div style={{ position: 'relative', display: 'inline-flex', alignItems: 'flex-start' }}>
      {/* Main rack column */}
      <RackColumn rack={rack} M={M} K={K}
                  onToggle={onToggle || (() => {})}
                  onSelectSlot={onSelectSlot || (() => {})}
                  selSlotId={selSlotId || null}
                  editable={editable}
                  tt={{ show: () => {}, move: () => {}, hide: () => {} }}
                  scanning={false}
                  portMode={portMode}
                  showPortNums={showPortNums} />

      {/* Right-side column: rack-level btn at top, then one btn per slot */}
      <div style={{ display: 'flex', flexDirection: 'column', paddingTop: 4, gap: 0, marginLeft: 4 }}>
        {/* Rack-level media button — aligned with rack-head */}
        <div style={{ height: RACK_HEAD_H, display: 'flex', alignItems: 'center' }}>
          <MediaButton count={(rack.media || []).length} onClick={openRackMedia} />
        </div>
        {/* Per-slot media buttons */}
        {rack.slots.map(slot => (
          <div key={slot.id} style={{ height: SLOT_H, display: 'flex', alignItems: 'center' }}>
            {slot.kind !== 'empty' ? (
              <MediaButton count={(slot.media || []).length} onClick={() => openSlotMedia(slot)} />
            ) : (
              <div style={{ width: 28 }} />
            )}
          </div>
        ))}
      </div>

      {popup && (
        <MediaPopup
          items={popup.items}
          title={popup.title}
          onClose={() => setPopup(null)}
          onAdd={handleAdd}
          onDelete={handleDelete}
        />
      )}
    </div>
  );
}

// ── Port plug SVG icon ───────────────────────────────────
function PortPlugIcon({ plug, size = 14 }) {
  if (plug === 'rj45') return (
    <svg width={size} height={size} viewBox="0 0 14 14" fill="none">
      <rect x="3" y="1.5" width="8" height="5.5" rx="1" fill="currentColor" opacity="0.9"/>
      <rect x="5" y="6.5" width="4" height="3.5" fill="currentColor" opacity="0.75"/>
      <rect x="4.5" y="9.5" width="1.5" height="2.5" rx="0.4" fill="currentColor" opacity="0.6"/>
      <rect x="8" y="9.5" width="1.5" height="2.5" rx="0.4" fill="currentColor" opacity="0.6"/>
    </svg>
  );
  if (plug === 'sfp') return (
    <svg width={size} height={size} viewBox="0 0 14 14" fill="none">
      <rect x="1" y="2.5" width="12" height="9" rx="1.5" fill="currentColor" opacity="0.85"/>
      <rect x="1" y="2.5" width="12" height="3" rx="1.5" fill="currentColor" opacity="0.4"/>
      <rect x="3.5" y="10.5" width="7" height="1" rx="0.5" fill="rgba(0,0,0,0.3)"/>
    </svg>
  );
  return null;
}

const PLUG_CYCLE = { rj45: 'sfp', sfp: 'empty', empty: 'rj45' };
const LED_CYCLE  = { green: 'green-blink', 'green-blink': 'orange', orange: 'orange-blink', 'orange-blink': 'off', off: 'green' };

// ── Per-slot port grid (port icons + LED circles) ────────
// onPortClick(portIdx, 'plug'|'led') — optional; when provided, ports become interactive
function SamplePortGrid({ ports, M, iconSize = 22, onPortClick }) {
  // ledSize formula: M×(iconSize+2) = 2M×(ledSize+2) → ledSize = iconSize/2 - 1
  const ledSize  = Math.round(iconSize / 2) - 1;
  const svgSize  = Math.round(iconSize * 0.65);
  const numFont  = Math.max(5, Math.round(iconSize * 0.45));
  const ledFont  = Math.max(4, Math.round(ledSize  * 0.65));
  const clickable = !!onPortClick;
  const iconClick = clickable ? (e, pi) => { e.stopPropagation(); onPortClick(pi, 'plug'); } : null;
  const ledClick  = clickable ? (e, k)  => { e.stopPropagation(); onPortClick(k,  'led');  } : null;
  return (
    <div className="smp-port-grid">
      <div className="smp-section-lbl">
        포트 연결 상태{clickable && <span style={{ marginLeft: 4, color: 'var(--accent)', fontSize: 8 }}>· 클릭 편집</span>}
      </div>
      {/* Odd numbers above top row */}
      <div className="smp-port-nums">
        {Array.from({ length: M }, (_, i) => (
          <div key={i} className="smp-port-num" style={{ width: iconSize, fontSize: numFont }}>{2*i+1}</div>
        ))}
      </div>
      {/* Top icon row: ports[0,2,4,...] = P1,P3,P5,... */}
      <div className="smp-port-icon-row">
        {Array.from({ length: M }, (_, i) => {
          const { plug } = normPort(ports[2*i]);
          return (
            <div key={i} className={`smp-port-icon plug-${plug}${clickable ? ' editable' : ''}`}
                 style={{ width: iconSize, height: iconSize, cursor: clickable ? 'pointer' : 'default' }}
                 title={clickable ? `P${2*i+1} · ${plug} — 클릭으로 순환` : `P${2*i+1} · ${plug}`}
                 onClick={clickable ? (e) => iconClick(e, 2*i) : undefined}>
              <PortPlugIcon plug={plug} size={svgSize} />
            </div>
          );
        })}
      </div>
      {/* Bottom icon row: ports[1,3,5,...] = P2,P4,P6,... */}
      <div className="smp-port-icon-row">
        {Array.from({ length: M }, (_, i) => {
          const { plug } = normPort(ports[2*i+1]);
          return (
            <div key={i} className={`smp-port-icon plug-${plug}${clickable ? ' editable' : ''}`}
                 style={{ width: iconSize, height: iconSize, cursor: clickable ? 'pointer' : 'default' }}
                 title={clickable ? `P${2*i+2} · ${plug} — 클릭으로 순환` : `P${2*i+2} · ${plug}`}
                 onClick={clickable ? (e) => iconClick(e, 2*i+1) : undefined}>
              <PortPlugIcon plug={plug} size={svgSize} />
            </div>
          );
        })}
      </div>
      {/* Even numbers below bottom row */}
      <div className="smp-port-nums">
        {Array.from({ length: M }, (_, i) => (
          <div key={i} className="smp-port-num" style={{ width: iconSize, fontSize: numFont }}>{2*i+2}</div>
        ))}
      </div>
      {/* LED section */}
      <div className="smp-led-hd" style={{ color: 'var(--fg-0)' }}>LED 상태</div>
      {/* 2M LED dots */}
      <div className="smp-led-row">
        {Array.from({ length: 2*M }, (_, k) => {
          const { led } = normPort(ports[k]);
          return <div key={k} className={`smp-led-dot led-${led}${clickable ? ' editable' : ''}`}
                      style={{ width: ledSize, height: ledSize, cursor: clickable ? 'pointer' : 'default' }}
                      title={clickable ? `P${k+1} · ${led} — 클릭으로 순환` : `P${k+1} · ${led}`}
                      onClick={clickable ? (e) => ledClick(e, k) : undefined} />;
        })}
      </div>
      {/* LED port numbers: 1, 2, 3, ..., 2M */}
      <div className="smp-led-nums">
        {Array.from({ length: 2*M }, (_, k) => (
          <div key={k} className="smp-led-num" style={{ width: ledSize, fontSize: ledFont }}>{k+1}</div>
        ))}
      </div>
    </div>
  );
}

// ── Full-width slot row card ──────────────────────────────
function SampleSlotRow({ slot, M, unit, iconSize = 22 }) {
  const typeLabel = { server: '서버', switch: '스위치' }[slot.kind];
  const typeBadge = { server: 'SRV', switch: 'SW' }[slot.kind];
  const typeColor = slot.kind === 'switch' ? 'var(--accent)' : 'var(--ok)';
  const on = slot.power === 'on';
  const ports = slot.ports || [];

  if (slot.kind === 'empty') {
    return (
      <div className="smp-slot-row empty">
        <div className="smp-slot-left">
          <div className="smp-unit-lbl">U{String(unit).padStart(2, '0')}</div>
        </div>
        <div className="smp-slot-mid" style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
          <div className="smp-empty-ring" />
          <div>
            <div className="mono" style={{ fontSize: 13, color: 'var(--fg-2)' }}>비어 있음</div>
            <div className="mono" style={{ fontSize: 10, color: 'var(--fg-3)', marginTop: 2 }}>
              장비가 설치되지 않았습니다.
            </div>
          </div>
        </div>
        <div className="smp-slot-right" />
      </div>
    );
  }

  const rj45   = ports.filter(p => normPort(p).plug === 'rj45').length;
  const sfp    = ports.filter(p => normPort(p).plug === 'sfp').length;
  const empty  = ports.filter(p => normPort(p).plug === 'empty').length;
  const green  = ports.filter(p => ['green', 'green-blink'].includes(normPort(p).led)).length;
  const orange = ports.filter(p => ['orange', 'orange-blink'].includes(normPort(p).led)).length;
  const off    = ports.filter(p => normPort(p).led  === 'off').length;
  const hasPorts = ports.length > 0 && slot.kind === 'switch';

  return (
    <div className={`smp-slot-row ${on ? 'powered' : ''}`} style={{ borderLeftColor: typeColor }}>
      {/* Left: unit info */}
      <div className="smp-slot-left">
        <div className="smp-unit-lbl">U{String(unit).padStart(2, '0')}</div>
        {typeBadge && (
          <div className="smp-type-chip"
               style={{ background: typeColor + '22', borderColor: typeColor, color: typeColor }}>
            {typeBadge}
          </div>
        )}
        <div className="smp-dev-name">{slot.name || '(unnamed)'}</div>
        <div className="smp-dev-kind">⊟ {typeLabel}</div>
      </div>

      {/* Center: port visualization */}
      <div className="smp-slot-mid">
        {hasPorts
          ? <SamplePortGrid ports={ports} M={M} iconSize={iconSize} />
          : <div style={{ color: 'var(--fg-3)', fontSize: 11, padding: '8px 0' }}>포트 없음</div>
        }
      </div>

      {/* Right: stats */}
      <div className="smp-slot-right">
        <div className="smp-stat"><span style={{ color: 'var(--ok)' }}>연결</span><b>{rj45}</b></div>
        <div className="smp-stat"><span style={{ color: 'var(--warn)' }}>모듈</span><b>{sfp}</b></div>
        <div className="smp-stat"><span>비어있음</span><b>{empty}</b></div>
        <div className="smp-stat-sep" />
        <div className="smp-stat"><span style={{ color: 'var(--ok)' }}>정상</span><b>{green}</b></div>
        <div className="smp-stat"><span style={{ color: 'var(--warn)' }}>경고</span><b>{orange}</b></div>
        <div className="smp-stat"><span>꺼짐</span><b>{off}</b></div>
      </div>
    </div>
  );
}

// ── Legend panel ─────────────────────────────────────────
function SampleLegend() {
  return (
    <div className="smp-legend">
      <div className="smp-legend-ttl">범례 (Legend)</div>
      <div className="smp-legend-body">
        <div className="smp-legend-col">
          <div className="smp-legend-hd">포트 연결 상태 (포트는 상/하 2개 단위로 표시)</div>
          <div className="smp-legend-items">
            {[
              { plug: 'rj45',  label: '연결됨',     sub: '케이블 연결', col: 'var(--ok)' },
              { plug: 'sfp',   label: '모듈만 장착', sub: '연결 없음',   col: 'var(--warn)' },
              { plug: 'empty', label: '비어 있음',   sub: '장착 없음',   col: 'var(--fg-2)' },
            ].map(({ plug, label, sub, col }) => (
              <div key={plug} className="smp-legend-item">
                <div className={`smp-port-icon plug-${plug}`} style={{ width: 28, height: 28 }}>
                  <PortPlugIcon plug={plug} />
                </div>
                <div>
                  <div style={{ color: col, fontSize: 11, fontWeight: 600 }}>{label}</div>
                  <div className="mono" style={{ fontSize: 9, color: 'var(--fg-3)' }}>{sub}</div>
                </div>
              </div>
            ))}
          </div>
        </div>
        <div className="smp-legend-col">
          <div className="smp-legend-hd">LED 상태</div>
          <div className="smp-legend-items">
            {[
              { led: 'green',        label: '녹색-ON',    sub: '정상 동작',      col: 'var(--ok)' },
              { led: 'green-blink',  label: '녹색-점등',  sub: '활성/데이터 전송', col: 'var(--ok)' },
              { led: 'orange',       label: '주황색-ON',  sub: '주의 필요',       col: 'var(--warn)' },
              { led: 'orange-blink', label: '주황색-점등', sub: '경고/점등',       col: 'var(--warn)' },
              { led: 'off',          label: '점등안됨',   sub: '전원/통신 없음',  col: 'var(--fg-2)' },
            ].map(({ led, label, sub, col }) => (
              <div key={led} className="smp-legend-item">
                <div className={`smp-led-dot led-${led}`} style={{ width: 16, height: 16 }} />
                <div>
                  <div style={{ color: col, fontSize: 11 }}>{label}</div>
                  <div className="mono" style={{ fontSize: 9, color: 'var(--fg-3)' }}>{sub}</div>
                </div>
              </div>
            ))}
          </div>
        </div>
        <div className="smp-legend-col">
          <div className="smp-legend-hd">포트 표기 가이드</div>
          <div style={{ display: 'flex', gap: 10, alignItems: 'center', marginTop: 4 }}>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', columnGap: 10, fontFamily: 'var(--font-mono)', fontSize: 12, lineHeight: 1.7 }}>
              <span style={{ color: 'var(--fg-1)' }}>1</span><span style={{ color: 'var(--fg-1)' }}>3</span>
              <span style={{ color: 'var(--fg-2)' }}>2</span><span style={{ color: 'var(--fg-2)' }}>4</span>
            </div>
            <div className="mono" style={{ fontSize: 9, color: 'var(--fg-3)', lineHeight: 1.55 }}>
              각 열은 상/하 2개 포트를 나타냄.<br/>
              숫자는 세로로 1~2, 3~4 순서입니다.
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

// ── One rack → list of slot rows ─────────────────────────
function SampleRackSection({ rack, M, K, iconSize = 22 }) {
  if (rack.absent) return null;
  return (
    <div className="smp-rack-sect">
      <div className="smp-rack-hdr">
        <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          <svg width="20" height="20" viewBox="0 0 24 24" fill="none"
               stroke="var(--accent)" strokeWidth="1.5" strokeLinecap="round">
            <rect x="2" y="3" width="20" height="18" rx="2"/>
            <line x1="2" y1="9" x2="22" y2="9"/>
            <line x1="2" y1="15" x2="22" y2="15"/>
          </svg>
          <div>
            <div className="smp-rack-name">{rack.alias || rack.label}</div>
            <div className="smp-rack-sub">{rack.label} / 전체 {2*M}포트 장비 기준</div>
          </div>
        </div>
      </div>
      <div className="smp-slot-list">
        {rack.slots.map((slot, i) => (
          <SampleSlotRow key={slot.id} slot={slot} M={M} unit={K - i} iconSize={iconSize} />
        ))}
      </div>
    </div>
  );
}

// ── V2: slot with CONFIG rack shape + large port icons ────
function SampleSlotV2({ slot, M, unit, iconSize = 22, onToggle, onOpenMedia, onSelect, onPortToggle, selected }) {
  const typeLabel = { server: 'SRV', switch: 'SW', empty: '—' }[slot.kind];
  const on = slot.power !== 'off';
  const hasPorts = slot.kind === 'switch';

  if (slot.kind === 'empty') {
    return (
      <div className={`slot empty ${selected ? 'selected' : ''}`}
           style={{ height: 'auto', minHeight: 38, cursor: onSelect ? 'pointer' : 'default' }}
           onClick={onSelect}>
        <div className="slot-u">U{String(unit).padStart(2, '0')}</div>
        <div className="slot-body"><div className="slot-type">empty</div></div>
        <div />
      </div>
    );
  }

  return (
    <div className={`slot ${on ? 'powered' : ''} ${slot.power === 'warn' ? 'powered-warn' : ''} ${selected ? 'selected' : ''}`}
         style={{ height: 'auto', minHeight: 44, alignItems: 'flex-start', paddingTop: 8, paddingBottom: 8,
                  cursor: onSelect ? 'pointer' : 'default' }}
         onClick={onSelect}>
      <div className="slot-u" style={{ paddingTop: 2 }}>U{String(unit).padStart(2, '0')}</div>
      <div className="slot-body">
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          <span className={`slot-type ${slot.kind}`}>{typeLabel}</span>
          <span className="slot-name">{slot.name}</span>
          {onOpenMedia && (
            <div style={{ marginLeft: 'auto' }} onClick={(e) => e.stopPropagation()}>
              <MediaButton count={(slot.media || []).length} onClick={() => onOpenMedia(slot)} />
            </div>
          )}
        </div>
        {hasPorts
          ? <div style={{ marginTop: 6 }} onClick={(e) => e.stopPropagation()}>
              <SamplePortGrid ports={slot.ports || []}
                              M={Math.max(1, Math.floor((slot.ports?.length || 2) / 2))}
                              iconSize={iconSize}
                              onPortClick={onPortToggle ? (pi, field) => onPortToggle(slot.id, pi, field) : null} />
            </div>
          : <div style={{ marginTop: 4, display: 'flex', gap: 4 }}>
              {Array.from({ length: 3 }).map((_, i) => (
                <div key={i} style={{
                  width: 18, height: 6,
                  background: slot.power === 'warn' ? 'var(--warn)' : on ? 'var(--ok)' : 'var(--bg-3)',
                  opacity: on ? 0.6 - i * 0.15 : 0.5,
                  borderRadius: 1,
                }} />
              ))}
            </div>}
      </div>
      <div style={{ position: 'relative', display: 'flex', justifyContent: 'flex-end', paddingTop: 2 }}
           onClick={(e) => e.stopPropagation()}>
        <SlotFeedWire power={slot.power} />
        <PowerSwitch power={slot.power} onClick={() => onToggle && onToggle(slot.id)} disabled={!onToggle} />
      </div>
    </div>
  );
}

// ── V2: full CONFIG-style rack column ─────────────────────
function SampleRackColumnV2({ rack, group, M, K, iconSize = 22, setCfg, apiKey = null,
                              onSelectSlot, selSlotId, readOnly = true,
                              scanning = false, showMedia = true }) {
  const [popup, setPopup] = useStateSR(null);

  const toggleSlot = setCfg ? (slotId) => {
    setCfg(c => ({
      ...c,
      groups: c.groups.map(g => g.id !== group.id ? g : {
        ...g,
        racks: g.racks.map(r => r.id !== rack.id ? r : {
          ...r,
          slots: r.slots.map(s => s.id === slotId
            ? { ...s, power: POWER_CYCLE[s.power] || 'on' }
            : s),
        }),
      }),
    }));
  } : null;

  const togglePort = setCfg ? (slotId, portIdx, field) => {
    setCfg(c => ({
      ...c,
      groups: c.groups.map(g => g.id !== group.id ? g : {
        ...g,
        racks: g.racks.map(r => r.id !== rack.id ? r : {
          ...r,
          slots: r.slots.map(s => s.id !== slotId ? s : {
            ...s,
            ports: (s.ports || []).map((p, i) => i !== portIdx ? p : {
              ...normPort(p),
              [field]: field === 'plug'
                ? PLUG_CYCLE[normPort(p).plug]
                : LED_CYCLE[normPort(p).led],
            }),
          }),
        }),
      }),
    }));
  } : null;

  const handleAdd = readOnly ? null : async (files) => {
    if (!apiKey) { alert('CONFIG → File 패널에서 API Key를 먼저 입력하세요.'); return; }
    for (const file of files) {
      try {
        const { src } = await serverUploadMedia(file, apiKey);
        const item = { id: sRid('m'), name: file.name,
                       type: file.type.startsWith('video/') ? 'video' : 'image', src };
        const slotId = popup?.target === 'rack' ? null : popup?.target;
        setCfg(c => addMedia(c, group.id, rack.id, slotId, item));
        setPopup(p => p ? { ...p, items: [...p.items, item] } : p);
      } catch (e) { alert('업로드 실패: ' + e.message); }
    }
  };

  const handleDelete = readOnly ? null : async (mediaId) => {
    const item = popup?.items?.find(m => m.id === mediaId);
    if (item?.src?.startsWith('/media/')) await serverDeleteMedia(item.src, apiKey);
    const slotId = popup?.target === 'rack' ? null : popup?.target;
    setCfg(c => removeMedia(c, group.id, rack.id, slotId, mediaId));
    setPopup(p => p ? { ...p, items: p.items.filter(m => m.id !== mediaId) } : p);
  };

  const openSlotMedia = (slot) => {
    setPopup({ target: slot.id, items: slot.media || [], title: `${slot.name || slot.kind} · Asset Media` });
  };

  if (rack.absent) return <AbsentRackPlaceholder rack={rack} K={K} />;

  const anyOn = rack.slots.some(s => s.power !== 'off');

  return (
    <div style={{ display: 'inline-flex', alignItems: 'flex-start', gap: 4 }}>
      <div className={`rack${scanning ? ' scanning' : ''}`} data-rack-id={rack.id} style={{ width: 'fit-content' }}>
        <div className={`rack-feed ${anyOn ? 'on' : ''}`}>
          {anyOn && (
            <>
              <div className="bolt-pulse" />
              <div className="bolt-pulse" />
              <div className="bolt-pulse" />
              <div className="bolt-spark" />
            </>
          )}
        </div>
        <div className="rack-head">
          <div className="rack-head-titles">
            <span className="id" title={rack.label}>
              {anyOn && <Bolt size={8} color="var(--electric)" glow={true} />}
              {rack.alias || rack.label}
            </span>
            {rack.alias && rack.alias.trim() && (
              <span className="rack-orig-label">{rack.label}</span>
            )}
          </div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
            {showMedia && (
              <MediaButton count={(rack.media || []).length}
                           onClick={() => setPopup({ target: 'rack', items: rack.media || [],
                             title: `${rack.alias || rack.label} · Rack Media` })} />
            )}
            <span className="tag">{K}U</span>
          </div>
        </div>
        <div className={`rack-rail ${anyOn ? 'on' : ''}`} aria-hidden="true">
          {anyOn && <div className="rack-rail-flow" />}
        </div>
        {rack.slots.map((s, i) => (
          <SampleSlotV2 key={s.id} slot={s} M={M} unit={K - i}
                        iconSize={iconSize}
                        onToggle={toggleSlot}
                        onPortToggle={togglePort}
                        onOpenMedia={showMedia ? openSlotMedia : null}
                        onSelect={onSelectSlot ? () => onSelectSlot(rack.id, s.id) : undefined}
                        selected={selSlotId === s.id} />
        ))}
      </div>

      {popup && (
        <MediaPopup items={popup.items} title={popup.title}
                    onClose={() => setPopup(null)}
                    onAdd={handleAdd} onDelete={handleDelete} />
      )}
    </div>
  );
}

// ── Main rack view (RACK tab) ─────────────────────────────
function SampleRackView({ cfg, setCfg, selGroupId, setSelGroupId }) {
  const stageRef = useRefSR(null);
  const group = cfg.groups.find(g => g.id === selGroupId) || cfg.groups[0];
  if (!group) return <div style={{ padding: 32, color: 'var(--fg-3)' }}>그룹 없음</div>;
  const { M, K, portIconSize = 22 } = cfg.params;

  const anyOnMap = {};
  group.racks.forEach(r => { anyOnMap[r.id] = r.slots.some(s => s.power !== 'off'); });

  return (
    <div className="smp-page">
      <div className="sample-group-tabs">
        {cfg.groups.map(g => (
          <button key={g.id}
                  className={`sample-group-tab ${g.id === group.id ? 'active' : ''}`}
                  onClick={() => setSelGroupId(g.id)}>
            {g.alias || g.tag}
          </button>
        ))}
      </div>
      <div className="smp-scroll">
        <div style={{ padding: '60px 40px 40px', position: 'relative', width: 'max-content' }}>
          <div className="rack-stage-inner" ref={stageRef}
               style={{ paddingTop: 0 }}>
            <PowerBus racks={group.racks} stageRef={stageRef} anyOnMap={anyOnMap} />
            {group.racks.map(rack => (
              <SampleRackColumnV2 key={rack.id} rack={rack} group={group} M={M} K={K}
                                  iconSize={portIconSize}
                                  setCfg={setCfg} />
            ))}
          </div>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { SampleRackView, MediaPopup, MediaButton, PortPlugIcon });
