// ============================================================
// Config / Simulation View
// ============================================================
const { useState: useStateC, useEffect: useEffectC, useRef: useRefC } = React;

function Field({ label, value, children }) {
  return (
    <div className="field">
      <div className="field-label">
        <span>{label}</span>
        {value != null && <span className="v">{value}</span>}
      </div>
      {children}
    </div>
  );
}

function RackEditor({ group, cfg, setCfg, selRackIdx, setSelRackIdx }) {
  const { M, K } = cfg.params;
  const rack = group?.racks[selRackIdx];
  const [selSlotIdx, setSelSlotIdx] = useStateC(0);
  if (!group) return <div className="panel"><div className="dim">랙 그룹을 먼저 선택하세요.</div></div>;

  const updateRack = (patchFn) => {
    setCfg(c => ({
      ...c,
      groups: c.groups.map(g => g.id !== group.id ? g : {
        ...g,
        racks: g.racks.map((r, ri) => ri !== selRackIdx ? r : patchFn(r)),
      }),
    }));
  };

  const toggleAbsent = () => {
    if (!rack) return;
    if (rack.absent) {
      // materialize empty rack
      updateRack(r => ({
        ...r,
        absent: false,
        slots: Array.from({ length: K }, () => ({
          id: rid('slot'), kind: 'empty', name: '', power: 'off', ports: [],
        })),
      }));
    } else {
      updateRack(r => ({ ...r, absent: true, slots: [] }));
    }
  };

  const addRack = () => {
    setCfg(c => ({
      ...c,
      groups: c.groups.map(g => {
        if (g.id !== group.id) return g;
        const idx = g.racks.length;
        const label = `${g.tag}-${String.fromCharCode(65 + idx)}`;
        return { ...g, racks: [...g.racks, makeRack(label.toLowerCase(), K, M)] };
      }),
    }));
  };

  const removeRack = () => {
    if (group.racks.length <= 1) return;
    setCfg(c => ({
      ...c,
      groups: c.groups.map(g => g.id !== group.id ? g : { ...g, racks: g.racks.filter((_, i) => i !== selRackIdx) }),
    }));
    setSelRackIdx(Math.max(0, selRackIdx - 1));
  };

  const slot = rack.absent ? null : rack.slots[selSlotIdx];
  const setSlot = (patch) => {
    setCfg(c => ({
      ...c,
      groups: c.groups.map(g => g.id !== group.id ? g : {
        ...g,
        racks: g.racks.map((r, ri) => ri !== selRackIdx ? r : {
          ...r,
          slots: r.slots.map((s, si) => si !== selSlotIdx ? s : { ...s, ...patch }),
        }),
      }),
    }));
  };
  const setKind = (kind) => {
    const hasPorts = kind === 'server' || kind === 'switch';
    const ports = hasPorts ? Array(2 * M).fill('unplugged') : [];
    setSlot({
      kind, ports,
      power: kind === 'empty' ? 'off' : 'on',
      name: kind === 'empty' ? '' : ({ server: 'app-new', switch: 'sw-new', storage: 'vol-new' })[kind],
    });
  };
  return (
    <>
      <div className="panel">
        <div className="panel-title"><span>Rack · 랙 선택</span><span className="spacer"/><span className="mono">{group.tag}</span></div>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(44px, 1fr))', gap: 4 }}>
          {group.racks.map((r, i) => (
            <button key={r.id}
              className={`btn ${i === selRackIdx ? 'primary' : ''}`}
              style={{
                padding: '6px 4px',
                opacity: r.absent ? 0.5 : 1,
                borderStyle: r.absent ? 'dashed' : 'solid',
              }}
              title={r.absent ? '빈 슬롯' : r.label}
              onClick={() => { setSelRackIdx(i); setSelSlotIdx(0); }}>
              {String.fromCharCode(65 + i)}{r.absent ? '·' : ''}
            </button>
          ))}
        </div>
        <div className="btn-row" style={{ marginTop: 10 }}>
          <button className="btn" onClick={addRack} title="그룹에 랙 추가">
            <Icon kind="add" size={12} /> 랙 추가
          </button>
          <button className="btn danger" onClick={removeRack}
                  disabled={group.racks.length <= 1}
                  style={{ opacity: group.racks.length <= 1 ? 0.4 : 1 }}>
            <Icon kind="trash" size={12} /> 랙 제거
          </button>
        </div>
        <div style={{ marginTop: 10 }}>
          <Field label="Presence · 랙 존재 여부">
            <div className="segmented">
              <button className={!rack.absent ? 'active' : ''} onClick={() => { if (rack.absent) toggleAbsent(); }}>PRESENT</button>
              <button className={rack.absent ? 'active' : ''}
                      onClick={() => { if (!rack.absent) toggleAbsent(); }}>ABSENT · 빈 자리</button>
            </div>
          </Field>
          <div className="mono" style={{ fontSize: 9, color: 'var(--fg-3)', marginTop: 4 }}>
            ABSENT: 자리는 남아있지만 랙이 없는 상태. 상세 뷰에서 빈 프레임으로 표시됩니다.
          </div>
        </div>
      </div>
      {rack.absent ? (
        <div className="panel" style={{ borderStyle: 'dashed', borderColor: 'var(--line-faint)' }}>
          <div className="panel-title"><span style={{ color: 'var(--fg-3)' }}>—  빈 자리 · Empty slot</span><span className="spacer"/></div>
          <div className="mono" style={{ fontSize: 10, color: 'var(--fg-3)', lineHeight: 1.6 }}>
            이 랙은 현재 없음으로 표시되어 있습니다. PRESENT로 바꾸면 {K}U 슬롯이 비어 있는 상태로 생성됩니다.
          </div>
        </div>
      ) : (
      <div className="panel">
        <div className="panel-title"><span>Slots · {rack.label}</span><span className="spacer"/><span className="mono">{K}U</span></div>
        <div className="rack-mini">
          {rack.slots.map((s, i) => (
            <div key={s.id}
              className={`rack-mini-row ${i === selSlotIdx ? 'sel' : ''} ${s.power === 'on' ? 'on' : ''}`}
              onClick={() => setSelSlotIdx(i)}>
              <span className="u">U{String(K - i).padStart(2, '0')}</span>
              <span className="tp">{s.kind === 'empty' ? '(empty)' : `${s.kind.toUpperCase()}·${s.name}`}</span>
              <span className="dot" />
            </div>
          ))}
        </div>
      </div>
      )}
      {!rack.absent && (
      <>
      <div className="panel">
        <div className="panel-title"><span>Type · 장비 종류</span><span className="spacer"/></div>
        <div className="slot-picker">
          {['empty', 'server', 'switch', 'storage'].map(k => (
            <button key={k} className={slot.kind === k ? 'active' : ''} onClick={() => setKind(k)}>{k}</button>
          ))}
        </div>
        {slot.kind !== 'empty' && (
          <div style={{ marginTop: 10 }}>
            <Field label="Power · 전원">
              <div className="segmented">
                <button className={slot.power === 'on' ? 'active' : ''} onClick={() => setSlot({ power: 'on' })}>ON</button>
                <button className={slot.power === 'off' ? 'active' : ''} onClick={() => setSlot({ power: 'off' })}>OFF</button>
              </div>
            </Field>
          </div>
        )}
      </div>
      {slot.kind !== 'empty' && slot.kind !== 'storage' && (
        <div className="panel">
          <div className="panel-title"><span>Ports · 포트 상태</span><span className="spacer"/><span className="mono">{2*M}</span></div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
            {[0, 1].map(row => (
              <div key={row} style={{ display: 'grid', gridTemplateColumns: `repeat(${M}, 1fr)`, gap: 4 }}>
                {slot.ports.slice(row * M, (row + 1) * M).map((p, i) => {
                  const idx = row * M + i;
                  const next = p === 'unplugged' ? 'connected' : p === 'connected' ? 'suspicious' : 'unplugged';
                  return (
                    <button key={i}
                      className={`port ${p}`}
                      style={{ width: '100%', height: 22, border: '1px solid var(--line-faint)', borderRadius: 2 }}
                      onClick={() => {
                        const ports = [...slot.ports];
                        ports[idx] = next;
                        setSlot({ ports });
                      }}
                      title={`P${idx + 1}: ${p}`} />
                  );
                })}
              </div>
            ))}
          </div>
          <div className="mono" style={{ fontSize: 9, color: 'var(--fg-3)', marginTop: 8 }}>
            클릭 → unplugged → connected → suspicious 순 순환
          </div>
        </div>
      )}
      </>
      )}
    </>
  );
}

// Facing direction dial (inline)
function FacingDial({ value, onChange, size = 88 }) {
  const ref = useRefC(null);
  const onPoint = (clientX, clientY) => {
    const rect = ref.current.getBoundingClientRect();
    const cx = rect.left + rect.width / 2;
    const cy = rect.top + rect.height / 2;
    const a = Math.atan2(clientY - cy, clientX - cx);
    onChange(a);
  };
  const onDown = (e) => {
    onPoint(e.clientX, e.clientY);
    const move = (ev) => onPoint(ev.clientX, ev.clientY);
    const up = () => { window.removeEventListener('mousemove', move); window.removeEventListener('mouseup', up); };
    window.addEventListener('mousemove', move); window.addEventListener('mouseup', up);
  };
  const deg = (value * 180 / Math.PI + 360) % 360;
  const r = size / 2 - 8;
  const tipX = size / 2 + Math.cos(value) * r;
  const tipY = size / 2 + Math.sin(value) * r;
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
      <svg ref={ref} width={size} height={size} onMouseDown={onDown}
           style={{ cursor: 'grab', touchAction: 'none' }}>
        <circle cx={size/2} cy={size/2} r={r+4} fill="var(--bg-1)" stroke="var(--line-soft)"/>
        <circle cx={size/2} cy={size/2} r={r} fill="none" stroke="var(--line-faint)" strokeDasharray="2 3"/>
        {/* cardinal ticks */}
        {[0, 90, 180, 270].map(d => {
          const a = d * Math.PI / 180;
          return <line key={d}
            x1={size/2 + Math.cos(a) * (r-2)} y1={size/2 + Math.sin(a) * (r-2)}
            x2={size/2 + Math.cos(a) * (r+2)} y2={size/2 + Math.sin(a) * (r+2)}
            stroke="var(--fg-2)" strokeWidth="1"/>;
        })}
        <text x={size/2} y="9" fontSize="8" fontFamily="var(--font-mono)" fill="var(--fg-2)" textAnchor="middle">W</text>
        <text x={size-6} y={size/2+3} fontSize="8" fontFamily="var(--font-mono)" fill="var(--fg-2)" textAnchor="end">N</text>
        <text x={size/2} y={size-3} fontSize="8" fontFamily="var(--font-mono)" fill="var(--fg-2)" textAnchor="middle">E</text>
        <text x="6" y={size/2+3} fontSize="8" fontFamily="var(--font-mono)" fill="var(--fg-2)">S</text>
        <line x1={size/2} y1={size/2} x2={tipX} y2={tipY}
              stroke="var(--warn)" strokeWidth="1.6" strokeLinecap="round"/>
        <circle cx={tipX} cy={tipY} r="3" fill="var(--warn)"/>
        <circle cx={size/2} cy={size/2} r="3" fill="var(--warn)"/>
      </svg>
      <div>
        <div className="mono" style={{ color: 'var(--warn)', fontSize: 14 }}>{deg.toFixed(0)}°</div>
        <div className="mono" style={{ color: 'var(--fg-3)', fontSize: 9 }}>facing</div>
      </div>
    </div>
  );
}

function LayoutEditor({ cfg, setCfg, selGroupId, setSelGroupId, editMode, setEditMode, selWpIdx, setSelWpIdx }) {
  const wrapRef = useRefC(null);
  const [size, setSize] = useStateC({ w: 800, h: 600 });
  const [drag, setDrag] = useStateC(null);

  useEffectC(() => {
    if (!wrapRef.current) return;
    const ro = new ResizeObserver(([e]) => setSize({ w: e.contentRect.width, h: e.contentRect.height }));
    ro.observe(wrapRef.current);
    return () => ro.disconnect();
  }, []);

  const { gridW, gridH } = cfg.params;
  const pad = 44;
  const cell = Math.min((size.w - pad * 2) / gridW, (size.h - pad * 2) / gridH);
  const W = cell * gridW, H = cell * gridH;
  const ox = (size.w - W) / 2, oy = (size.h - H) / 2;

  const toGrid = (clientX, clientY, snap = true) => {
    const rect = wrapRef.current.getBoundingClientRect();
    const gx = (clientX - rect.left - ox) / cell;
    const gy = (clientY - rect.top - oy) / cell;
    return snap
      ? { x: Math.round(gx), y: Math.round(gy) }
      : { x: gx, y: gy };
  };

  const robot = cfg.robots[0];

  const onCanvasClick = (e) => {
    if (drag) return;
    if (editMode === 'add-group') {
      const p = toGrid(e.clientX, e.clientY);
      const newG = {
        id: rid('rg'),
        x: Math.max(0, Math.min(gridW - 3, p.x)),
        y: Math.max(0, Math.min(gridH - 2, p.y)),
        w: 3, h: 2,
        tag: `RG-${String(cfg.groups.length + 1).padStart(2, '0')}`,
        racks: Array.from({ length: cfg.params.N }, (_, i) => ({
          id: `r_${Date.now()}_${i}`,
          label: `RG-${String(cfg.groups.length + 1).padStart(2, '0')}-${String.fromCharCode(65 + i)}`,
          slots: Array.from({ length: cfg.params.K }, () => ({
            id: rid('slot'), kind: 'empty', name: '', power: 'off', ports: [],
          })),
        })),
      };
      setCfg(c => ({ ...c, groups: [...c.groups, newG] }));
      setEditMode('select');
    } else if (editMode === 'add-entry') {
      const p = toGrid(e.clientX, e.clientY);
      setCfg(c => ({
        ...c,
        entrances: [...c.entrances, {
          id: rid('ent'),
          x: Math.max(0, Math.min(gridW - 1, p.x)),
          y: Math.max(0, Math.min(gridH - 2, p.y)),
          w: 1, h: 2,
        }],
      }));
      setEditMode('select');
    } else if (editMode === 'add-waypoint' && robot) {
      const p = toGrid(e.clientX, e.clientY);
      setCfg(c => ({
        ...c,
        robots: c.robots.map((r, i) => i !== 0 ? r : { ...r, path: [...r.path, { x: p.x, y: p.y }] }),
      }));
      setSelWpIdx(robot.path.length);
    } else if (editMode === 'add-inspect' && robot) {
      const p = toGrid(e.clientX, e.clientY);
      setCfg(c => ({
        ...c,
        robots: c.robots.map((r, i) => i !== 0 ? r : {
          ...r,
          path: [...r.path, { x: p.x, y: p.y, inspect: { facing: 0, duration: 3 } }],
        }),
      }));
      setSelWpIdx(robot.path.length);
    }
  };

  const startDrag = (type, id, e, extra = {}) => {
    e.stopPropagation();
    if (editMode !== 'select') return;
    if (type === 'group') setSelGroupId(id);
    if (type === 'waypoint') setSelWpIdx(extra.idx);
    setDrag({ type, id, extra });
  };

  const onMove = (e) => {
    if (!drag) return;
    const p = toGrid(e.clientX, e.clientY);
    if (drag.type === 'group') {
      setCfg(c => ({
        ...c,
        groups: c.groups.map(g => g.id !== drag.id ? g : {
          ...g,
          x: Math.max(0, Math.min(gridW - groupWidth(g), p.x - Math.floor(groupWidth(g) / 2))),
          y: Math.max(0, Math.min(gridH - g.h, p.y - Math.floor(g.h / 2))),
        }),
      }));
    } else if (drag.type === 'entrance') {
      setCfg(c => ({
        ...c,
        entrances: c.entrances.map(en => en.id !== drag.id ? en : {
          ...en, x: Math.max(0, Math.min(gridW - en.w, p.x)), y: Math.max(0, Math.min(gridH - en.h, p.y)),
        }),
      }));
    } else if (drag.type === 'waypoint') {
      const pf = toGrid(e.clientX, e.clientY, false);
      const x = Math.max(0, Math.min(gridW, Math.round(pf.x * 2) / 2));
      const y = Math.max(0, Math.min(gridH, Math.round(pf.y * 2) / 2));
      setCfg(c => ({
        ...c,
        robots: c.robots.map((r, ri) => ri !== 0 ? r : {
          ...r, path: r.path.map((wp, wi) => wi !== drag.extra.idx ? wp : { ...wp, x, y }),
        }),
      }));
    }
  };
  const endDrag = () => setTimeout(() => setDrag(null), 0);

  const delSel = () => {
    if (!selGroupId) return;
    setCfg(c => ({ ...c, groups: c.groups.filter(g => g.id !== selGroupId) }));
    setSelGroupId(null);
  };

  const pathStr = (path) => path.length
    ? path.map(p => `${ox + p.x * cell},${oy + p.y * cell}`).join(' ') + ` ${ox + path[0].x * cell},${oy + path[0].y * cell}`
    : '';

  return (
    <div className="cfg-main">
      <div className="bp-grid" />
      <Corners />
      <div className="cfg-toolbar">
        <div className="cfg-mode-pill">
          <button className={editMode === 'select' ? 'active' : ''} onClick={() => setEditMode('select')}>Select</button>
          <button className={editMode === 'add-group' ? 'active' : ''} onClick={() => setEditMode('add-group')}>+ Rack</button>
          <button className={editMode === 'add-entry' ? 'active' : ''} onClick={() => setEditMode('add-entry')}>+ Entry</button>
          <button className={editMode === 'add-waypoint' ? 'active' : ''} onClick={() => setEditMode('add-waypoint')}>+ Waypoint</button>
          <button className={editMode === 'add-inspect' ? 'active' : ''} onClick={() => setEditMode('add-inspect')}
            style={{ color: editMode === 'add-inspect' ? 'var(--warn)' : undefined }}>+ Inspect</button>
        </div>
        <div className="cfg-hint">
          {editMode === 'select' && '드래그 · 웨이포인트도 끌어서 이동'}
          {editMode === 'add-group' && '격자 클릭 → 랙 그룹 추가'}
          {editMode === 'add-entry' && '격자 클릭 → 출입구 추가'}
          {editMode === 'add-waypoint' && '클릭 → 이동 웨이포인트'}
          {editMode === 'add-inspect' && '클릭 → 점검 포인트 (스캔 위치)'}
        </div>
      </div>

      <div ref={wrapRef} style={{ position: 'absolute', inset: 0 }}
        onMouseMove={onMove} onMouseUp={endDrag} onMouseLeave={endDrag}
        onClick={onCanvasClick}>
        <svg className="cfg-editor-svg" viewBox={`0 0 ${size.w} ${size.h}`} preserveAspectRatio="none">
          {Array.from({ length: gridW + 1 }).map((_, i) => (
            <text key={`tx${i}`} className="axis-tick" x={ox + i * cell} y={oy - 8} textAnchor="middle">{String(i).padStart(2, '0')}</text>
          ))}
          {Array.from({ length: gridH + 1 }).map((_, j) => (
            <text key={`ty${j}`} className="axis-tick" x={ox - 8} y={oy + j * cell + 3} textAnchor="end">{String.fromCharCode(65 + j)}</text>
          ))}
          <rect x={ox} y={oy} width={W} height={H} fill="none" stroke="var(--line-soft)" strokeWidth="1" />

          {cfg.entrances.map(e => {
            const x = ox + e.x * cell, y = oy + e.y * cell;
            return (
              <g key={e.id} onMouseDown={(ev) => startDrag('entrance', e.id, ev)} style={{ cursor: editMode === 'select' ? 'grab' : 'default' }}>
                <rect className="entrance-rect" x={x} y={y} width={e.w * cell} height={e.h * cell} />
                <text className="entrance-label" x={x + e.w * cell / 2} y={y + e.h * cell / 2 + 3} textAnchor="middle">ENTRY</text>
              </g>
            );
          })}

          {cfg.groups.map(g => {
            const x = ox + g.x * cell, y = oy + g.y * cell;
            const sel = selGroupId === g.id;
            return (
              <g key={g.id}
                onMouseDown={(ev) => startDrag('group', g.id, ev)}
                style={{ cursor: editMode === 'select' ? 'grab' : 'default' }}>
                <rect x={x + 2} y={y + 2} width={groupWidth(g) * cell - 4} height={g.h * cell - 4}
                  fill={sel ? 'rgba(79,209,255,0.14)' : 'rgba(30,42,60,0.6)'}
                  stroke={sel ? 'var(--accent)' : 'var(--ok)'}
                  strokeWidth={sel ? 1.5 : 1} />
                <text x={x + 8} y={y + 14} fill="var(--fg-0)" fontFamily="var(--font-mono)" fontSize="10">{g.tag}</text>
                <text x={x + 8} y={y + 26} fill="var(--fg-2)" fontFamily="var(--font-mono)" fontSize="8">
                  {g.racks.length}R · {groupWidth(g)}×{g.h}
                </text>
              </g>
            );
          })}

          {/* robot path */}
          {robot && (
            <g>
              <polyline points={pathStr(robot.path)}
                fill="none" stroke="var(--accent)" strokeWidth="1" strokeDasharray="3 3" opacity="0.7" />
              {robot.path.map((p, i) => {
                const px = ox + p.x * cell, py = oy + p.y * cell;
                const isInspect = !!p.inspect;
                const isSel = selWpIdx === i;
                return (
                  <g key={i}
                     onMouseDown={(ev) => startDrag('waypoint', robot.id, ev, { idx: i })}
                     style={{ cursor: editMode === 'select' ? 'grab' : 'pointer' }}>
                    {isInspect && (
                      <>
                        <circle cx={px} cy={py} r="10" fill="rgba(245,180,84,0.05)"
                                stroke="var(--warn)" strokeWidth="1" strokeDasharray="2 2"/>
                        <line x1={px} y1={py}
                              x2={px + Math.cos(p.inspect.facing) * 18}
                              y2={py + Math.sin(p.inspect.facing) * 18}
                              stroke="var(--warn)" strokeWidth="1.4"/>
                        <polygon
                          points={`0,-4 7,0 0,4`}
                          transform={`translate(${px + Math.cos(p.inspect.facing) * 18}, ${py + Math.sin(p.inspect.facing) * 18}) rotate(${p.inspect.facing * 180 / Math.PI})`}
                          fill="var(--warn)"/>
                      </>
                    )}
                    <circle cx={px} cy={py} r={isSel ? 8 : 6}
                      fill={isSel ? (isInspect ? 'var(--warn)' : 'var(--accent)') : 'var(--bg-0)'}
                      stroke={isInspect ? 'var(--warn)' : 'var(--accent)'}
                      strokeWidth={isSel ? 2 : 1.2}/>
                    <text x={px} y={py + 3} textAnchor="middle"
                          fontFamily="var(--font-mono)" fontSize="8"
                          fill={isSel ? 'var(--bg-0)' : (isInspect ? 'var(--warn)' : 'var(--accent)')}>
                      {i + 1}
                    </text>
                  </g>
                );
              })}
              {/* start marker (first wp) */}
              {robot.path[0] && (
                <g>
                  <circle cx={ox + robot.path[0].x * cell} cy={oy + robot.path[0].y * cell}
                          r="14" fill="none" stroke="var(--ok)" strokeWidth="1" opacity="0.6"/>
                  <text x={ox + robot.path[0].x * cell} y={oy + robot.path[0].y * cell - 18}
                        textAnchor="middle" fontFamily="var(--font-mono)" fontSize="8"
                        fill="var(--ok)" letterSpacing="1">START</text>
                </g>
              )}
            </g>
          )}
        </svg>
      </div>

      {selGroupId && editMode === 'select' && (
        <div style={{ position: 'absolute', bottom: 18, left: 18, display: 'flex', gap: 8, zIndex: 5 }}>
          <button className="btn danger" onClick={delSel}>
            <Icon kind="trash" size={12} /> 그룹 삭제
          </button>
        </div>
      )}
    </div>
  );
}

function RobotPanel({ cfg, setCfg, selWpIdx, setSelWpIdx, setEditMode }) {
  const robot = cfg.robots[0];
  if (!robot) return null;
  const wp = robot.path[selWpIdx];

  const setRobot = (patch) => {
    setCfg(c => ({ ...c, robots: c.robots.map((r, i) => i === 0 ? { ...r, ...patch } : r) }));
  };
  const setWp = (patch) => {
    setCfg(c => ({
      ...c,
      robots: c.robots.map((r, i) => i !== 0 ? r : {
        ...r, path: r.path.map((w, wi) => wi !== selWpIdx ? w : { ...w, ...patch }),
      }),
    }));
  };
  const toggleInspect = () => {
    if (!wp) return;
    if (wp.inspect) setWp({ inspect: undefined });
    else setWp({ inspect: { facing: 0, duration: 3 } });
  };
  const setInspect = (patch) => {
    if (!wp?.inspect) return;
    setWp({ inspect: { ...wp.inspect, ...patch } });
  };
  const delWp = () => {
    if (selWpIdx == null) return;
    setRobot({ path: robot.path.filter((_, i) => i !== selWpIdx) });
    setSelWpIdx(null);
  };
  const moveUp = () => {
    if (selWpIdx <= 0) return;
    const p = [...robot.path];
    [p[selWpIdx - 1], p[selWpIdx]] = [p[selWpIdx], p[selWpIdx - 1]];
    setRobot({ path: p });
    setSelWpIdx(selWpIdx - 1);
  };
  const moveDown = () => {
    if (selWpIdx >= robot.path.length - 1) return;
    const p = [...robot.path];
    [p[selWpIdx + 1], p[selWpIdx]] = [p[selWpIdx], p[selWpIdx + 1]];
    setRobot({ path: p });
    setSelWpIdx(selWpIdx + 1);
  };

  return (
    <>
      <div className="panel">
        <div className="panel-title"><span>Robot · {robot.name}</span><span className="spacer"/></div>
        <Field label={`Speed · 이동 속도`} value={`${robot.speed.toFixed(2)}/s`}>
          <input type="range" className="slider" min="0.1" max="3" step="0.1" value={robot.speed}
            onChange={(e) => setRobot({ speed: +e.target.value })} />
        </Field>
        <div style={{ marginTop: 10 }}>
          <div className="btn-row">
            <button className="btn primary" onClick={() => setEditMode('add-waypoint')}>
              <Icon kind="add" size={12} /> 이동점
            </button>
            <button className="btn" onClick={() => setEditMode('add-inspect')}
                    style={{ color: 'var(--warn)', borderColor: 'var(--warn)' }}>
              <Icon kind="add" size={12} /> 점검점
            </button>
          </div>
        </div>
      </div>

      <div className="panel">
        <div className="panel-title">
          <span>Waypoints · 경로</span><span className="spacer"/>
          <span className="mono">{robot.path.length}</span>
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 3, maxHeight: 180, overflowY: 'auto' }}>
          {robot.path.map((p, i) => (
            <button key={i}
              className={`rack-mini-row ${i === selWpIdx ? 'sel' : ''}`}
              style={{ cursor: 'pointer', textAlign: 'left' }}
              onClick={() => setSelWpIdx(i)}>
              <span className="u" style={{ color: p.inspect ? 'var(--warn)' : 'var(--accent)' }}>
                {String(i + 1).padStart(2, '0')}
              </span>
              <span className="tp" style={{ color: p.inspect ? 'var(--warn)' : 'var(--fg-1)' }}>
                {p.inspect ? 'INSPECT' : 'MOVE'} · [{p.x.toFixed(1)},{p.y.toFixed(1)}]
              </span>
              {p.inspect && <span className="mono" style={{ fontSize: 9, color: 'var(--warn)' }}>
                {p.inspect.duration.toFixed(1)}s
              </span>}
            </button>
          ))}
          {robot.path.length === 0 && (
            <div className="dim" style={{ fontSize: 11, padding: 8 }}>웨이포인트가 없습니다.</div>
          )}
        </div>
      </div>

      {wp && (
        <div className="panel" style={{ borderColor: wp.inspect ? 'var(--warn)' : 'var(--accent)' }}>
          <div className="panel-title">
            <span style={{ color: wp.inspect ? 'var(--warn)' : 'var(--accent)' }}>
              WP {String(selWpIdx + 1).padStart(2, '0')} · {wp.inspect ? '점검' : '이동'}
            </span>
            <span className="spacer"/>
          </div>

          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
            <Field label="X" value="">
              <input type="number" step="0.5" value={wp.x}
                style={{ width: '100%', padding: '6px 8px', background: 'var(--bg-2)',
                         border: '1px solid var(--line-faint)', borderRadius: 2,
                         fontFamily: 'var(--font-mono)', color: 'var(--fg-0)' }}
                onChange={(e) => setWp({ x: +e.target.value })} />
            </Field>
            <Field label="Y" value="">
              <input type="number" step="0.5" value={wp.y}
                style={{ width: '100%', padding: '6px 8px', background: 'var(--bg-2)',
                         border: '1px solid var(--line-faint)', borderRadius: 2,
                         fontFamily: 'var(--font-mono)', color: 'var(--fg-0)' }}
                onChange={(e) => setWp({ y: +e.target.value })} />
            </Field>
          </div>

          <div style={{ marginTop: 12 }}>
            <Field label="점검 모드 · Inspection">
              <div className="segmented">
                <button className={!wp.inspect ? 'active' : ''} onClick={() => { if (wp.inspect) toggleInspect(); }}>OFF</button>
                <button className={wp.inspect ? 'active' : ''}
                        style={{ color: wp.inspect ? 'var(--warn)' : undefined }}
                        onClick={() => { if (!wp.inspect) toggleInspect(); }}>ON</button>
              </div>
            </Field>
          </div>

          {wp.inspect && (
            <>
              <div style={{ marginTop: 14 }}>
                <div className="field-label">
                  <span>Facing · 바라볼 방향</span>
                  <span className="v" style={{ color: 'var(--warn)' }}>
                    {((wp.inspect.facing * 180 / Math.PI + 360) % 360).toFixed(0)}°
                  </span>
                </div>
                <div style={{ marginTop: 8, display: 'flex', flexDirection: 'column', gap: 8 }}>
                  <FacingDial value={wp.inspect.facing} onChange={(v) => setInspect({ facing: v })} />
                  <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 4 }}>
                    {[['E', 0], ['S', Math.PI/2], ['W', Math.PI], ['N', -Math.PI/2]].map(([lab, a]) => (
                      <button key={lab} className="btn"
                        style={{ padding: '6px 4px' }}
                        onClick={() => setInspect({ facing: a })}>
                        {lab}
                      </button>
                    ))}
                  </div>
                </div>
              </div>

              <div style={{ marginTop: 14 }}>
                <Field label="Duration · 점검 시간" value={`${wp.inspect.duration.toFixed(1)}s`}>
                  <input type="range" className="slider" min="0.5" max="10" step="0.5"
                    value={wp.inspect.duration}
                    onChange={(e) => setInspect({ duration: +e.target.value })} />
                </Field>
              </div>

              <div style={{ marginTop: 14 }}>
                <div className="field-label">
                  <span>Target · 스캔할 랙</span>
                  <span className="v" style={{ color: 'var(--warn)', fontSize: 10 }}>
                    {wp.inspect.targetRackId
                      ? (() => {
                          const g = cfg.groups.find(g => g.id === wp.inspect.targetGroupId);
                          const r = g?.racks.find(r => r.id === wp.inspect.targetRackId);
                          return r ? `${g.tag}·${r.label.split('-').pop()}` : '—';
                        })()
                      : 'none'}
                  </span>
                </div>
                <select
                  value={wp.inspect.targetRackId || ''}
                  onChange={(e) => {
                    const val = e.target.value;
                    if (!val) { setInspect({ targetGroupId: undefined, targetRackId: undefined }); return; }
                    const [gid, rid] = val.split('|');
                    setInspect({ targetGroupId: gid, targetRackId: rid });
                  }}
                  style={{
                    width: '100%', marginTop: 6,
                    padding: '7px 8px', background: 'var(--bg-2)',
                    border: '1px solid var(--line-faint)', borderRadius: 2,
                    fontFamily: 'var(--font-mono)', fontSize: 10,
                    color: 'var(--fg-0)',
                  }}>
                  <option value="">— 스캔 대상 없음</option>
                  {cfg.groups.map(g => (
                    <optgroup key={g.id} label={g.tag}>
                      {g.racks.map(r => (
                        <option key={r.id} value={`${g.id}|${r.id}`}>
                          {r.label}
                        </option>
                      ))}
                    </optgroup>
                  ))}
                </select>
                <div className="mono" style={{ fontSize: 9, color: 'var(--fg-3)', marginTop: 4 }}>
                  선택한 랙의 상세 화면에서 점검 표시가 나타납니다.
                </div>
              </div>
            </>
          )}

          <div style={{ display: 'flex', gap: 6, marginTop: 14 }}>
            <button className="btn" onClick={moveUp} style={{ flex: 1 }}>↑</button>
            <button className="btn" onClick={moveDown} style={{ flex: 1 }}>↓</button>
            <button className="btn danger" onClick={delWp} style={{ flex: 1.5 }}>
              <Icon kind="trash" size={12} /> 삭제
            </button>
          </div>
        </div>
      )}
    </>
  );
}

function ConfigView({ cfg, setCfg, selGroupId, setSelGroupId }) {
  const [editMode, setEditMode] = useStateC('select');
  const [selRackIdx, setSelRackIdx] = useStateC(0);
  const [selWpIdx, setSelWpIdx] = useStateC(0);
  const [rightTab, setRightTab] = useStateC('rack'); // 'rack' | 'robot'
  const fileRef = useRefC(null);

  const group = cfg.groups.find(g => g.id === selGroupId) || cfg.groups[0];

  const setParams = (patch) => setCfg(c => ({ ...c, params: { ...c.params, ...patch } }));

  const regenFromParams = () => {
    const { K, M } = cfg.params;
    setCfg(c => ({
      ...c,
      groups: c.groups.map(g => ({
        ...g,
        racks: g.racks.map(r => {
          if (r.absent) return r;
          return {
            ...r,
            slots: Array.from({ length: K }, (_, i) => {
              const prev = r.slots[i];
              const kind = prev?.kind || 'empty';
              const hasPorts = kind === 'server' || kind === 'switch';
              return {
                id: prev?.id || rid('slot'),
                kind, name: prev?.name || '',
                power: prev?.power || 'off',
                ports: hasPorts
                  ? Array.from({ length: 2 * M }, (_, pi) => (prev?.ports?.[pi]) || 'unplugged')
                  : [],
              };
            }),
          };
        }),
      })),
    }));
  };

  const doExport = () => {
    const blob = new Blob([JSON.stringify(cfg, null, 2)], { type: 'application/json' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url; a.download = `server-room-config-${Date.now()}.json`;
    a.click(); URL.revokeObjectURL(url);
  };
  const doImport = (e) => {
    const f = e.target.files?.[0]; if (!f) return;
    const reader = new FileReader();
    reader.onload = () => {
      try { setCfg(JSON.parse(reader.result)); } catch (err) { alert('Invalid JSON'); }
    };
    reader.readAsText(f);
  };
  const saveL = () => { saveLocal(cfg); alert('저장됨 · Saved to browser'); };
  const reset = () => { if (confirm('모든 설정을 기본값으로 초기화할까요?')) setCfg(defaultConfig()); };

  return (
    <div className="cfg-wrap view-enter">
      <div className="cfg-col">
        <div className="panel">
          <div className="panel-title"><span>Global Parameters</span><span className="spacer"/></div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
            <Field label={`N · 랙/그룹`} value={cfg.params.N}>
              <input type="range" className="slider" min="2" max="8" value={cfg.params.N}
                onChange={(e) => setParams({ N: +e.target.value })} />
            </Field>
            <Field label={`M · 포트/행`} value={cfg.params.M}>
              <input type="range" className="slider" min="2" max="8" value={cfg.params.M}
                onChange={(e) => setParams({ M: +e.target.value })} />
            </Field>
            <Field label={`K · 층 수`} value={cfg.params.K}>
              <input type="range" className="slider" min="3" max="10" value={cfg.params.K}
                onChange={(e) => setParams({ K: +e.target.value })} />
            </Field>
            <button className="btn" onClick={regenFromParams}>
              <Icon kind="reset" size={12} /> 파라미터 적용
            </button>
          </div>
        </div>

        <div className="panel">
          <div className="panel-title"><span>Floor Preview · 평면도 표시</span><span className="spacer"/></div>
          <Field label="Detail Level · 상세도">
            <div className="segmented" style={{ gridTemplateColumns: 'repeat(3, 1fr)' }}>
              <button className={(cfg.params.floorDetail || 'status') === 'simple' ? 'active' : ''}
                      onClick={() => setParams({ floorDetail: 'simple' })}>Simple</button>
              <button className={(cfg.params.floorDetail || 'status') === 'status' ? 'active' : ''}
                      onClick={() => setParams({ floorDetail: 'status' })}>Status</button>
              <button className={(cfg.params.floorDetail || 'status') === 'full' ? 'active' : ''}
                      onClick={() => setParams({ floorDetail: 'full' })}>Full</button>
            </div>
          </Field>
          <div className="mono" style={{ fontSize: 9, color: 'var(--fg-3)', marginTop: 6, lineHeight: 1.6 }}>
            · Simple: 그룹 박스와 이름만 표시<br/>
            · Status: 랙별 헬스 색상 바 (기본값)<br/>
            · Full: 랙 내부 슬롯 · 포트까지 모두 미니뷰
          </div>
        </div>

        <div className="panel">
          <div className="panel-title"><span>File · 저장/불러오기</span><span className="spacer"/></div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
            <div className="btn-row">
              <button className="btn primary" onClick={doExport}><Icon kind="export" size={12} /> Export</button>
              <button className="btn" onClick={() => fileRef.current?.click()}>
                <Icon kind="import" size={12} /> Import
              </button>
              <input ref={fileRef} type="file" accept="application/json" style={{ display: 'none' }} onChange={doImport} />
            </div>
            <div className="btn-row">
              <button className="btn" onClick={saveL}><Icon kind="save" size={12} /> Save</button>
              <button className="btn danger" onClick={reset}><Icon kind="reset" size={12} /> Reset</button>
            </div>
          </div>
        </div>
      </div>

      <LayoutEditor
        cfg={cfg} setCfg={setCfg}
        selGroupId={group?.id} setSelGroupId={setSelGroupId}
        editMode={editMode} setEditMode={setEditMode}
        selWpIdx={selWpIdx} setSelWpIdx={setSelWpIdx}
      />

      <div className="cfg-col right">
        <div className="segmented" style={{ marginBottom: 4 }}>
          <button className={rightTab === 'rack' ? 'active' : ''} onClick={() => setRightTab('rack')}>Rack</button>
          <button className={rightTab === 'robot' ? 'active' : ''} onClick={() => setRightTab('robot')}>Robot</button>
        </div>
        {rightTab === 'rack' ? (
          <RackEditor group={group} cfg={cfg} setCfg={setCfg}
            selRackIdx={selRackIdx} setSelRackIdx={setSelRackIdx} />
        ) : (
          <RobotPanel cfg={cfg} setCfg={setCfg}
            selWpIdx={selWpIdx} setSelWpIdx={setSelWpIdx}
            setEditMode={setEditMode} />
        )}
      </div>
    </div>
  );
}

Object.assign(window, { ConfigView });
