// INVYONE Stage-2 — UI / Interaction Layer
// 클릭 위임, 사이드 패널 (선택된 컴포넌트 컨트롤), 모드 버튼, 클럭

(function (global) {
  let selectedId = null;
  let selectedType = null;

  function init() {
    wireSceneClicks();
    wireSidebarButtons();
    wireGlobalControls();
    wireScenario();
    tickClock();
    setInterval(tickClock, 1000);
    setInterval(tickPhoneClock, 1000);
  }

  // ============================================================
  // SCENE CLICK DELEGATION
  // ============================================================
  function wireSceneClicks() {
    const scene = document.getElementById('scene');
    if (!scene) return;

    scene.addEventListener('click', (e) => {
      // cartridge takes precedence (deeper in DOM)
      const cart = e.target.closest('.cartridge');
      if (cart && cart.id.startsWith('cartridge-')) {
        const cid = cart.id.replace('cartridge-', '');
        global.INVYONE_ENGINE.isolateCartridge(cid);
        select(cid, 'cartridge');
        e.stopPropagation();
        return;
      }
      const comp = e.target.closest('[data-comp-id]');
      if (!comp) { clearSelection(); return; }
      const id = comp.getAttribute('data-comp-id');
      const type = comp.getAttribute('data-comp-type');
      if (!id || !type) return;
      select(id, type);
      // single-click action
      if (type === 'pump') global.INVYONE_ENGINE.togglePump(id);
      else if (type === 'valve') global.INVYONE_ENGINE.toggleValve(id);
    });
  }

  function select(id, type) {
    selectedId = id;
    selectedType = type;
    document.querySelectorAll('.comp.selected').forEach(el => el.classList.remove('selected'));
    const root = document.querySelector(`[data-comp-id="${id}"]${type ? `[data-comp-type="${type}"]` : ''}`);
    if (root) root.classList.add('selected');
    renderInspector();
  }

  function clearSelection() {
    selectedId = null;
    selectedType = null;
    document.querySelectorAll('.comp.selected').forEach(el => el.classList.remove('selected'));
    renderInspector();
  }

  // ============================================================
  // INSPECTOR (선택된 컴포넌트 상세 패널)
  // ============================================================
  function renderInspector() {
    const box = document.getElementById('inspector');
    if (!box) return;
    if (!selectedId) {
      box.innerHTML = `
        <div class="inspector-empty">
          <div class="muted">컴포넌트를 클릭해 제어 패널을 여세요</div>
          <ul class="hint">
            <li>펌프 클릭 → ON/OFF</li>
            <li>밸브 클릭 → OPEN/CLOSE</li>
            <li>탱크 클릭 → 수위 슬라이더</li>
            <li>센서 클릭 → 값 직접 입력</li>
            <li>카트리지 클릭 → 격리(BYPASS)</li>
          </ul>
        </div>`;
      return;
    }
    const E = global.INVYONE_ENGINE;
    if (selectedType === 'tank') {
      const t = E.state.tanks[selectedId];
      const def = global.INVYONE_TOPO.TANKS.find(x => x.id === selectedId);
      box.innerHTML = `
        <h3>탱크 — ${def.label}</h3>
        <div class="row"><span>현재 수위</span><b id="insp-tank-pct">${t.level.toFixed(1)} %</b></div>
        <input type="range" min="0" max="100" step="0.1" value="${t.level}" id="insp-tank-slider" />
        <div class="row"><span>용량</span><b>${def.capacity.toLocaleString()} L</b></div>
        <div class="row"><span>잔량</span><b id="insp-tank-liters">${(def.capacity * t.level / 100).toFixed(0)} L</b></div>
        <div class="row"><span>알람 임계 (HH/HL/LH/LL)</span><b>${def.alarms.hh}/${def.alarms.hl}/${def.alarms.lh}/${def.alarms.ll}</b></div>
        <div class="row"><span>현재 알람</span><b class="alarm-tag ${t.alarmState || ''}">${(t.alarmState || 'NORMAL').toUpperCase()}</b></div>
        <div class="actions">
          <button data-act="fill-100">100%</button>
          <button data-act="fill-50">50%</button>
          <button data-act="empty">0%</button>
        </div>`;
      const sl = document.getElementById('insp-tank-slider');
      sl.addEventListener('input', e => {
        const v = parseFloat(e.target.value);
        E.setTankLevel(selectedId, v);
        document.getElementById('insp-tank-pct').textContent = v.toFixed(1) + ' %';
        document.getElementById('insp-tank-liters').textContent = (def.capacity * v / 100).toFixed(0) + ' L';
      });
      box.querySelectorAll('button[data-act]').forEach(btn => {
        btn.addEventListener('click', () => {
          const act = btn.getAttribute('data-act');
          if (act === 'fill-100') E.setTankLevel(selectedId, 100);
          if (act === 'fill-50')  E.setTankLevel(selectedId, 50);
          if (act === 'empty')    E.setTankLevel(selectedId, 0);
          renderInspector();
        });
      });
    } else if (selectedType === 'pump') {
      const p = E.state.pumps[selectedId];
      box.innerHTML = `
        <h3>펌프 — ${p.label || selectedId}</h3>
        <div class="row"><span>운전</span><b class="${p.running ? 'on' : 'off'}">${p.running ? 'ON' : 'OFF'}</b></div>
        <div class="row"><span>RPM</span><b id="insp-pump-rpm">${p.rpm}</b></div>
        <input type="range" min="0" max="3000" step="50" value="${p.rpm}" id="insp-pump-slider" />
        <div class="row"><span>용량</span><b>${p.lpm} L/min</b></div>
        <div class="row"><span>현재 유량</span><b id="insp-pump-flow">${p.running ? (p.lpm * (p.rpm / 1450)).toFixed(1) : '0.0'} L/min</b></div>
        <div class="row"><span>가동시간</span><b>${p.runtime.toFixed(0)} s</b></div>
        <div class="row"><span>흡입/토출</span><b>${p.source || '-'} → ${p.dest || '-'}</b></div>
        <div class="actions">
          <button data-act="toggle">${p.running ? 'STOP' : 'START'}</button>
        </div>`;
      const sl = document.getElementById('insp-pump-slider');
      sl.addEventListener('input', e => {
        const v = parseInt(e.target.value, 10);
        E.setRpm(selectedId, v);
        document.getElementById('insp-pump-rpm').textContent = v;
        document.getElementById('insp-pump-flow').textContent = (E.state.pumps[selectedId].running ? (p.lpm * (v / 1450)).toFixed(1) : '0.0') + ' L/min';
      });
      box.querySelector('[data-act="toggle"]').addEventListener('click', () => { E.togglePump(selectedId); renderInspector(); });
    } else if (selectedType === 'valve') {
      const v = E.state.valves[selectedId];
      box.innerHTML = `
        <h3>밸브 — ${v.label || selectedId}</h3>
        <div class="row"><span>상태</span><b class="${v.open ? 'on' : 'off'}">${v.open ? 'OPEN' : 'CLOSED'}</b></div>
        <div class="actions">
          <button data-act="toggle">${v.open ? 'CLOSE' : 'OPEN'}</button>
        </div>`;
      box.querySelector('[data-act="toggle"]').addEventListener('click', () => { E.toggleValve(selectedId); renderInspector(); });
    } else if (selectedType === 'sensor') {
      const s = E.state.sensors[selectedId];
      box.innerHTML = `
        <h3>센서 — ${s.label}</h3>
        <div class="row"><span>현재 값</span><b id="insp-sensor-val">${typeof s.value === 'number' ? s.value.toFixed(2) : s.value} ${s.unit}</b></div>
        <input type="range" min="${s.min}" max="${s.max}" step="0.01" value="${s.value}" id="insp-sensor-slider" />
        <div class="row"><span>측정범위</span><b>${s.min} ~ ${s.max} ${s.unit}</b></div>
        <div class="row"><span>임계 (LO / HI)</span><b>${s.alarms?.lo ?? '-'} / ${s.alarms?.hi ?? '-'}</b></div>
        <div class="row"><span>현재 알람</span><b class="alarm-tag ${s.alarmState || ''}">${(s.alarmState || 'NORMAL').toUpperCase()}</b></div>`;
      const sl = document.getElementById('insp-sensor-slider');
      sl.addEventListener('input', e => {
        const v = parseFloat(e.target.value);
        E.setSensor(selectedId, v);
        document.getElementById('insp-sensor-val').textContent = v.toFixed(2) + ' ' + s.unit;
      });
    } else if (selectedType === 'gauge') {
      const g = E.state.gauges[selectedId];
      box.innerHTML = `
        <h3>압력계 — ${g.label}</h3>
        <div class="row"><span>현재 값</span><b>${g.value.toFixed(2)} ${g.unit}</b></div>
        <div class="row"><span>최대</span><b>${g.max} ${g.unit}</b></div>
        <div class="row"><span>참조 펌프</span><b>${g.source || '-'}</b></div>
        <div class="muted small">압력은 참조 펌프의 운전 상태에 따라 자동 변동됩니다.</div>`;
    } else if (selectedType === 'cartridge') {
      const c = E.state.cartridges[selectedId] || { isolated: false };
      box.innerHTML = `
        <h3>카트리지 — ${selectedId}</h3>
        <div class="row"><span>상태</span><b class="${c.isolated ? 'off' : 'on'}">${c.isolated ? '격리(BYPASS)' : '정상'}</b></div>
        <div class="actions">
          <button data-act="toggle">${c.isolated ? '복귀' : '격리'}</button>
        </div>`;
      box.querySelector('[data-act="toggle"]').addEventListener('click', () => { E.isolateCartridge(selectedId); renderInspector(); });
    } else {
      box.innerHTML = `<h3>${selectedType.toUpperCase()} — ${selectedId}</h3><div class="muted">상세 컨트롤 없음</div>`;
    }
  }

  // ============================================================
  // SIDEBAR BUTTONS / GLOBAL CONTROLS
  // ============================================================
  function wireSidebarButtons() {
    const E = global.INVYONE_ENGINE;
    document.querySelectorAll('[data-mode]').forEach(btn => {
      btn.addEventListener('click', () => {
        const mode = btn.getAttribute('data-mode');
        E.applyMode(mode);
        document.querySelectorAll('[data-mode]').forEach(b => b.classList.toggle('active', b.getAttribute('data-mode') === mode));
      });
    });
  }

  function wireGlobalControls() {
    const E = global.INVYONE_ENGINE;
    bind('btn-reset', () => E.reset());
    bind('btn-pause', (btn) => {
      E.state.paused = !E.state.paused;
      btn.textContent = E.state.paused ? '▶ 재개' : '⏸ 일시정지';
      E.emitEvent(E.state.paused ? '시뮬레이션 일시정지' : '시뮬레이션 재개', 'info');
    });
    bind('btn-alarm', () => E.triggerDemoAlarm());
    bind('btn-clear-log', () => {
      const log = document.getElementById('event-log');
      if (log) log.innerHTML = '';
      E.state.events.length = 0;
    });
    bind('btn-deselect', () => clearSelection());
    const flowMul = document.getElementById('flow-multiplier');
    if (flowMul) flowMul.addEventListener('input', e => {
      const v = parseFloat(e.target.value);
      E.state.flowMultiplier = v;
      const out = document.getElementById('flow-multiplier-val');
      if (out) out.textContent = v.toFixed(1) + 'x';
    });
  }

  function bind(id, fn) {
    const el = document.getElementById(id);
    if (el) el.addEventListener('click', () => fn(el));
  }

  // ============================================================
  // CLOCK
  // ============================================================
  function tickClock() {
    const el = document.getElementById('clock');
    if (!el) return;
    const d = new Date();
    const pad = (n) => String(n).padStart(2, '0');
    el.textContent = `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}  ${pad(d.getDate())}/${pad(d.getMonth() + 1)}/${d.getFullYear()}`;
  }

  function tickPhoneClock() {
    const el = document.getElementById('phone-time');
    if (!el) return;
    const d = new Date();
    const pad = (n) => String(n).padStart(2, '0');
    el.textContent = `${pad(d.getHours())}:${pad(d.getMinutes())}`;
  }

  // ============================================================
  // EMERGENCY SCENARIO — 시연 시퀀스 핸들러
  // ============================================================
  function wireScenario() {
    bind('btn-emergency', () => {
      // 활성 시나리오면 중단, 아니면 새로 시작
      if (global.INVYONE_SCENARIO.state.running) {
        closeScenario();
        global.INVYONE_SCENARIO.stop();
        global.INVYONE_ENGINE.emitEvent('시연 중단', 'warn');
      } else {
        global.INVYONE_SCENARIO.play('bw-a1-overpressure');
      }
    });

    // 다중 경고 시연 — CRITICAL 시나리오(모달+맵 풀) + HIGH/MEDIUM 핀 + dock
    bind('btn-multi-emergency', () => {
      if (global.INVYONE_SCENARIO.state.running || window.__multiActive) {
        closeScenario();
        global.INVYONE_SCENARIO.stop();
        stopMultiAlarm();
        window.__multiActive = false;
        global.INVYONE_ENGINE.emitEvent('[다중 경고] 시연 중단', 'warn');
        return;
      }
      window.__multiActive = true;
      // CRITICAL 시나리오 (모달 + 맵 + 배너 + ⚠ 핀) 그대로 시작
      global.INVYONE_SCENARIO.play('bw-a1-overpressure');
      // CRITICAL 발령 직후 (~1700ms) 추가 알람 발생
      setTimeout(() => {
        if (!window.__multiActive) return;
        startMultiAlarm();
        global.INVYONE_ENGINE.emitEvent('[다중 경고] HIGH/MEDIUM 알람 추가 발생 — 우측 알람 리스트 확인', 'alarm');
      }, 1700);
    });

    // 모달 푸터
    bind('modal-skip', () => { closeScenario(); global.INVYONE_SCENARIO.stop(); global.INVYONE_ENGINE.emitEvent('시연 스킵', 'info'); });
    bind('modal-close', () => { closeScenario(); global.INVYONE_SCENARIO.stop(); });
    bind('modal-ack',   () => { global.INVYONE_SCENARIO.ackAndResolve(); });
    bind('modal-dispatch', () => {
      // "즉시 출동 요청" — 핸드폰에 알림 한 번 더 (시연자 효과)
      const phone = document.getElementById('phone-notify');
      if (phone) { phone.classList.add('shake'); setTimeout(() => phone.classList.remove('shake'), 3500); }
      global.INVYONE_ENGINE.emitEvent('[추가발송] 출동 요청 재전송', 'warn');
    });
    bind('phone-confirm', () => {
      const phone = document.getElementById('phone-notify');
      if (phone) phone.classList.remove('show');
      global.INVYONE_ENGINE.emitEvent('[수신확인] 김영수 책임자 알림 확인 완료', 'info');
    });

    // 시나리오 이벤트 → UI 반영
    document.addEventListener('scenario:bannerOn', e => {
      const s = e.detail.scenario;
      const banner = qid('alarm-banner');
      if (!banner) return;
      qid('alarm-banner-code').textContent = s.alarm.code;
      qid('alarm-banner-msg').textContent  = `${s.targetComp.toUpperCase()} 토출 압력 — 정상범위(4.0~5.0 bar) 초과 / ${s.alarm.title.split('—').pop().trim() || '누설 의심'}`;
      qid('alarm-banner-loc-text').textContent = `펌프룸 A · ${s.targetComp.toUpperCase()} (원수 취수펌프 #1)`;
      banner.classList.add('show');
      // 전체 화면 CRITICAL 이펙트 (vignette + hazard stripe)
      document.body.classList.add('critical-alarm');
    });

    document.addEventListener('scenario:pinShow', e => {
      showPidAlarmPin(e.detail.scenario.targetComp);
    });

    document.addEventListener('scenario:openModal', e => {
      const s = e.detail.scenario;
      qid('modal-alarm-code').textContent     = s.alarm.code;
      qid('modal-alarm-title').textContent    = s.title;
      qid('modal-alarm-message').textContent  = s.alarm.message;
      qid('modal-cctv-cam').textContent       = `${s.cam.id} / ${s.cam.label}`;
      // 사진 이미지 사용 — textContent 덮어쓰면 <img> 가 사라지므로 비활성화
      // qid('modal-officer-avatar').textContent = s.officer.avatar;
      qid('modal-officer-name').textContent   = s.officer.name;
      qid('modal-officer-title').textContent  = s.officer.title;
      qid('modal-officer-role').textContent   = s.officer.role;
      qid('modal-officer-phone').textContent  = s.officer.phone;
      qid('modal-officer-loc').textContent    = '관제실';
      const stat = qid('modal-officer-status');
      stat.textContent = '📡 알림 발송 중...';
      stat.className = 'officer-status';
      qid('modal-ack').disabled = true;
      qid('modal-ack').textContent = '✓ 확인 (ACK) — 담당자 도착 후 활성화';

      const modal = qid('emergency-modal');
      if (modal) modal.classList.add('show');
      // 모달 떠있는 동안 메인 P&ID 엔진 일시정지 — 250ms tick / 펌프회전 / 게이지 갱신 부하 제거
      try { global.INVYONE_ENGINE.state.paused = true; } catch {}
      // CCTV time 업데이트 시작
      startCctvClock();
      // CCTV 영상 초기화 — 출동 중(going) 영상 fade-in, 도착(arrived) 영상은 hidden 으로 미리 buffer
      const going = document.querySelector('.cctv-video-going');
      const arrived = document.querySelector('.cctv-video-arrived');
      if (going) {
        // 첫 재생은 풀(0~8초), 그 이후 루프는 5초부터 (모터 정지 구간 회피)
        going.currentTime = 0;
        if (!going.dataset.loopHandler) {
          going.addEventListener('ended', () => {
            going.currentTime = 7;
            going.play().catch(() => {});
          });
          going.dataset.loopHandler = '1';
        }
        going.classList.add('active');
        going.play().catch(() => {});
      }
      if (arrived) {
        arrived.classList.remove('active');
        arrived.pause();
        arrived.currentTime = 0;
      }
    });

    document.addEventListener('scenario:phoneAlert', e => {
      const s = e.detail.scenario;
      qid('phone-msg').textContent = `${s.alarm.title}\n현장으로 즉시 이동 바랍니다.`;
      const phone = qid('phone-notify');
      if (phone) {
        phone.classList.add('show');
        phone.classList.add('shake');
        setTimeout(() => phone.classList.remove('shake'), 3500);
      }
      const stat = qid('modal-officer-status');
      if (stat) { stat.textContent = '📲 휴대폰 알림 도달'; stat.className = 'officer-status delivered'; }
      global.INVYONE_ENGINE.emitEvent(`[자동발송] ${s.officer.name} ${s.officer.title} 휴대폰 알림 전송 (${s.officer.phone})`, 'info');
    });

    document.addEventListener('scenario:openMap', e => {
      const map = qid('map-panel');
      if (map) map.classList.add('open');
      // 모달 backdrop과 상단 배너 모두 좌측만 차지하도록 좁힘 (맵이 보이게)
      const modal = qid('emergency-modal');
      if (modal) modal.classList.add('with-map');
      const banner = qid('alarm-banner');
      if (banner) banner.classList.add('with-map');
      // 전체 화면 critical vignette 도 우측 540px 빼고 그려지게 (맵 영역에 안 새어들어감)
      document.body.classList.add('map-open');
      const pin   = qid('map-alarm-pin');
      const route = qid('map-route');
      if (pin) pin.classList.add('active');
      if (route) route.classList.add('active');
      const target = document.querySelector(`.map-building[data-bid="pump-room-a"]`);
      if (target) target.classList.add('alarm-target');
    });

    document.addEventListener('scenario:officerMove', e => {
      const off = qid('map-officer');
      if (off) {
        off.classList.add('moving');
        // 관제실(160,140) → 펌프룸 A(180,345)
        off.setAttribute('transform', 'translate(110 95)');
      }
      const stat = qid('modal-officer-status');
      if (stat) { stat.textContent = '🏃 현장 이동 중'; stat.className = 'officer-status responding'; }
      qid('modal-officer-loc').textContent = '이동 중...';
      qid('map-officer-status').textContent = '김영수 책임자 — 펌프룸 A 이동 중';
      global.INVYONE_ENGINE.emitEvent(`[이동개시] 관제실 → 펌프룸 A`, 'info');
    });

    document.addEventListener('scenario:phoneTap', e => {
      const btn = qid('phone-confirm');
      if (btn) {
        btn.classList.add('highlight');
        setTimeout(() => {
          btn.classList.add('tapped');
          setTimeout(() => {
            const phone = qid('phone-notify');
            if (phone) phone.classList.remove('show');
            btn.classList.remove('tapped', 'highlight');
          }, 600);
        }, 1500);
      }
      global.INVYONE_ENGINE.emitEvent(`[수신확인] 김영수 책임자 알림 확인 완료`, 'info');
    });

    document.addEventListener('scenario:officerArrived', e => {
      const off = qid('map-officer');
      if (off) off.classList.remove('moving');
      const stat = qid('modal-officer-status');
      if (stat) { stat.textContent = '✅ 현장 도착 — 점검 시작'; stat.className = 'officer-status arrived'; }
      qid('modal-officer-loc').textContent = '펌프룸 A';
      qid('map-officer-status').textContent = '김영수 책임자 — 펌프룸 A 도착, 점검 중';
      // CCTV 영상 전환 (fade swap, 깜빡임 없음): 출동 중 → 도착 영상
      const going = document.querySelector('.cctv-video-going');
      const arrived = document.querySelector('.cctv-video-arrived');
      if (arrived) {
        arrived.currentTime = 0;
        arrived.loop = false;
        arrived.classList.add('active');
        arrived.play().catch(() => {});
      }
      if (going) {
        // fade-out 끝난 후 정지 (transition 0.45s 와 매칭)
        setTimeout(() => { going.pause(); going.classList.remove('active'); }, 500);
      }
    });

    document.addEventListener('scenario:waitAck', e => {
      const ack = qid('modal-ack');
      if (ack) {
        ack.disabled = false;
        ack.textContent = '✓ 확인 (ACK) — 알람 해제';
      }
    });

    document.addEventListener('scenario:resolved', e => {
      // 천천히 닫기 (시연자가 흐름 인지하도록)
      setTimeout(() => closeScenario(), 1500);
    });
  }

  function closeScenario() {
    const modal = qid('emergency-modal');
    if (modal) { modal.classList.remove('show'); modal.classList.remove('with-map'); }
    const map   = qid('map-panel');       if (map) map.classList.remove('open');
    const phone = qid('phone-notify');    if (phone) phone.classList.remove('show');
    const banner = qid('alarm-banner');
    if (banner) { banner.classList.remove('show'); banner.classList.remove('with-map'); }
    hidePidAlarmPin('bw-a1');
    // 맵 상태 리셋
    const off = qid('map-officer');
    if (off) { off.classList.remove('moving'); off.setAttribute('transform', 'translate(857 110)'); }
    // CCTV 영상 정지 — 모달 닫힐 때 두 video 모두 백그라운드 재생 방지
    document.querySelectorAll('.cctv-video').forEach(v => {
      v.pause();
      v.classList.remove('active');
    });
    // 메인 P&ID 엔진 재개
    try { global.INVYONE_ENGINE.state.paused = false; } catch {}
    // 전체 화면 CRITICAL 이펙트 + 맵 open 상태 해제
    document.body.classList.remove('critical-alarm');
    document.body.classList.remove('map-open');
    const pin   = qid('map-alarm-pin'); if (pin) pin.classList.remove('active');
    const route = qid('map-route');     if (route) route.classList.remove('active');
    const target = document.querySelector(`.map-building[data-bid="pump-room-a"]`);
    if (target) target.classList.remove('alarm-target');
    qid('map-officer-status').textContent = '김영수 책임자 — 관제실 대기';
    stopCctvClock();
    // 다중 경고 시연이었으면 추가 핀 + dock 도 같이 정리
    if (window.__multiActive) {
      stopMultiAlarm();
      window.__multiActive = false;
    }
  }

  // P&ID 안에 알람 핀 SVG 그룹 동적 부착/제거 (severity: critical | high | medium)
  // CRITICAL 은 가장 크고 강조, HIGH/MEDIUM 은 보조 사이즈
  function showPidAlarmPin(compId, severity) {
    severity = severity || 'critical';
    const palette = {
      critical: { core: '#dc1c2e', accent: '#ffd400', text: '#ffd400', label: 'CRITICAL',
                  ringR: 34, coreR: 24, iconSize: 30, labelW: 116, labelH: 22, labelFont: 13, lift: 50, rectStroke: 3 },
      high:     { core: '#ff8a3a', accent: '#fff',    text: '#fff',    label: 'HIGH',
                  ringR: 22, coreR: 16, iconSize: 20, labelW: 70,  labelH: 14, labelFont: 9,  lift: 32, rectStroke: 2 },
      medium:   { core: '#ffd400', accent: '#000',    text: '#000',    label: 'MEDIUM',
                  ringR: 16, coreR: 12, iconSize: 14, labelW: 56,  labelH: 12, labelFont: 8,  lift: 26, rectStroke: 1.5 },
    };
    const c = palette[severity] || palette.critical;
    const ns = 'http://www.w3.org/2000/svg';
    const sceneContent = document.getElementById('scene-content');
    if (!sceneContent) return;

    const compEl = document.querySelector(`[data-comp-id="${compId}"]`);
    if (!compEl) { console.warn('[scenario] compEl not found for', compId); return; }

    // outer g 의 transform="translate(tx ty)" 직접 parse — anchor 시스템이 박은 절대 위치
    const tAttr = compEl.getAttribute('transform') || '';
    const m = tAttr.match(/translate\(\s*([\-\d.]+)[\s,]+([\-\d.]+)\s*\)/);
    if (!m) { console.warn('[scenario] no translate on', compId, 'transform=', tAttr); return; }
    const tx = parseFloat(m[1]);
    const ty = parseFloat(m[2]);

    // pump SVG 의 inner content 는 (0,0) 기준 — components.js 의 COMP.pump 참조
    // 펌프 박스 60×60 (rect x=0 y=0 w=60 h=60), 라벨은 y=76. 박스 top center = (30, 0)
    const localCx = 30;
    const localCy = 0;
    const localW = 60;
    const localH = 60;

    // 절대 좌표 (sceneContent 좌표계)
    const cx = tx + localCx;
    const cy = ty + localCy;

    console.log('[scenario] pin pos', { compId, tx, ty, cx, cy });

    // 펌프 외곽 두 겹 — outer 두꺼운 빨강 + inner 노란 hazard 점선
    let rectEl = document.getElementById('scenario-rect-' + compId);
    if (!rectEl) {
      rectEl = document.createElementNS(ns, 'rect');
      rectEl.setAttribute('id', 'scenario-rect-' + compId);
      rectEl.setAttribute('class', 'scenario-alarm-rect');
      sceneContent.appendChild(rectEl);
    }
    rectEl.setAttribute('x', tx - 6);
    rectEl.setAttribute('y', ty - 6);
    rectEl.setAttribute('width', localW + 12);
    rectEl.setAttribute('height', localH + 12);
    rectEl.setAttribute('fill', 'none');
    rectEl.setAttribute('stroke', c.core);
    rectEl.setAttribute('stroke-width', String(c.rectStroke));
    rectEl.setAttribute('rx', '2');

    let rectInner = document.getElementById('scenario-rect-inner-' + compId);
    if (!rectInner) {
      rectInner = document.createElementNS(ns, 'rect');
      rectInner.setAttribute('id', 'scenario-rect-inner-' + compId);
      rectInner.setAttribute('class', 'scenario-alarm-rect-inner');
      sceneContent.appendChild(rectInner);
    }
    rectInner.setAttribute('x', tx - 3);
    rectInner.setAttribute('y', ty - 3);
    rectInner.setAttribute('width', localW + 6);
    rectInner.setAttribute('height', localH + 6);
    rectInner.setAttribute('fill', 'none');
    rectInner.setAttribute('stroke', '#ffd400');
    rectInner.setAttribute('stroke-width', '1.2');
    rectInner.setAttribute('stroke-dasharray', '4 3');
    rectInner.setAttribute('rx', '2');

    // ⚠ CRITICAL 핀 — 산업 워닝 표지 스타일 (빨강 + 노란 ⚠)
    let g = document.getElementById('scenario-pin-' + compId);
    if (!g) {
      g = document.createElementNS(ns, 'g');
      g.setAttribute('id', 'scenario-pin-' + compId);
      sceneContent.appendChild(g);
    }
    g.setAttribute('class', `scenario-alarm-pin severity-${severity}`);
    g.setAttribute('transform', `translate(${cx} ${cy - c.lift})`);
    const labelY = -(c.coreR + 8);
    const labelTextY = labelY + c.labelH - 6;
    const iconDy = c.iconSize / 3;
    g.innerHTML = `
      <circle class="pin-ring-1" r="${c.ringR}" fill="${c.core}" opacity="0.5"/>
      <circle class="pin-ring-2" r="${c.ringR}" fill="${c.core}" opacity="0.5"/>
      <circle class="pin-core" r="${c.coreR}" fill="${c.core}"/>
      <circle r="${c.coreR}" fill="none" stroke="${c.accent}" stroke-width="${severity === 'critical' ? 2.5 : 1.5}"/>
      <text dy="${iconDy}" text-anchor="middle" font-size="${c.iconSize}" font-weight="900" fill="${c.text}" pointer-events="none">⚠</text>
      <rect x="${-c.labelW/2}" y="${labelY - c.labelH}" width="${c.labelW}" height="${c.labelH}" fill="${c.core}" stroke="${c.accent}" stroke-width="1" pointer-events="none"/>
      <text y="${labelY - 5}" text-anchor="middle" font-size="${c.labelFont}" font-weight="900" fill="${c.text}" letter-spacing="1" pointer-events="none">${c.label}</text>
    `;
  }
  function hidePidAlarmPin(compId) {
    const g = document.getElementById('scenario-pin-' + compId);
    if (g) g.remove();
    const r = document.getElementById('scenario-rect-' + compId);
    if (r) r.remove();
    const ri = document.getElementById('scenario-rect-inner-' + compId);
    if (ri) ri.remove();
  }

  // ============================================================
  // 다중 경고 시연 — severity 별 동시 알람
  // CRITICAL 은 메인 emergency-modal 이 처리, HIGH/MEDIUM 은 dock 의 mini-modal 카드로 표시
  // ============================================================
  const MULTI_ALARMS = [
    {
      comp: 'bw-a1',
      severity: 'critical',
      code: 'P-IN-HH',
      title: 'BW-A1 펌프 과압 / 누설 의심',
      loc: '펌프룸 A · BW-A1 (원수 취수펌프 #1)',
      msg: 'BW-A1 토출 압력 7.2bar — 누설 의심',
      detail: 'BW-A1 토출 압력이 정상 운전 범위(4.0~5.0 bar)를 초과했습니다. 현장 점검이 즉시 필요합니다.',
    },
    {
      comp: 'uf-pump-a',
      severity: 'high',
      code: 'UF-FLOW',
      title: 'UF 펌프 A 유량 저하 / 막힘 의심',
      loc: 'UF 시스템 · UF Pump A',
      msg: 'UF 펌프 A 유량 저하 — 막힘 의심',
      detail: 'UF 펌프 A 토출 유량이 정상 범위 이하로 떨어졌습니다. 흡입측 막힘 또는 임펠러 이상 가능성 — 현장 점검 필요.',
    },
    {
      comp: 'ro-feed',
      severity: 'medium',
      code: 'RO-PRESS',
      title: 'RO 공급 압력 임계 근접',
      loc: 'RO 라인 · RO Feed Pump',
      msg: 'RO 공급 압력 임계 근접',
      detail: 'RO 멤브레인 공급 압력이 운전 임계점에 근접하고 있습니다. 추세 모니터링 및 사전 점검 권장.',
    },
  ];

  function startMultiAlarm() {
    MULTI_ALARMS.forEach(a => showPidAlarmPin(a.comp, a.severity));

    const dock = document.getElementById('alarm-list-dock');
    const rows = document.getElementById('alarm-list-rows');
    const count = document.getElementById('alarm-list-count');
    if (!dock || !rows || !count) return;

    // 다중 모드 — 메인 모달 + dock 을 .emergency-stack 으로 묶어 viewport 안에서 자동 분할
    const backdrop = document.getElementById('emergency-modal');
    if (backdrop) {
      backdrop.classList.add('with-multi');
      const modal = backdrop.querySelector('.emergency-modal');
      let stack = backdrop.querySelector('.emergency-stack');
      if (!stack) {
        stack = document.createElement('div');
        stack.className = 'emergency-stack';
      }
      if (modal) stack.appendChild(modal);
      stack.appendChild(dock);
      backdrop.appendChild(stack);
    }

    // CRITICAL 은 메인 모달이 이미 표시 → dock 에는 HIGH/MEDIUM 만 mini-modal 로
    const dockAlarms = MULTI_ALARMS.filter(a => a.severity !== 'critical');
    count.textContent = String(dockAlarms.length);
    const order = { high: 0, medium: 1 };
    const sorted = [...dockAlarms].sort((a, b) => order[a.severity] - order[b.severity]);
    const now = new Date();
    const time = `${String(now.getHours()).padStart(2,'0')}:${String(now.getMinutes()).padStart(2,'0')}:${String(now.getSeconds()).padStart(2,'0')}`;

    rows.innerHTML = sorted.map(a => `
      <div class="alarm-mini-modal severity-${a.severity}" data-comp="${a.comp}">
        <header class="mini-head">
          <div class="mini-head-left">
            <span class="mini-severity severity-${a.severity}">${a.severity.toUpperCase()}</span>
            <span class="mini-code">${a.code}</span>
          </div>
          <div class="mini-title">${a.title}</div>
          <button class="mini-close" type="button" data-action="close" title="닫기">×</button>
        </header>
        <div class="mini-location">
          <span class="mini-loc-icon">📍</span>
          <span class="mini-loc-text">위치: <b>${a.loc}</b></span>
          <span class="mini-loc-time">${time}</span>
        </div>
        <div class="mini-message">
          <div class="mini-msg-label">▲ 알람 상세</div>
          <div class="mini-msg-text">${a.detail}</div>
        </div>
        <footer class="mini-foot">
          <button class="mini-btn mini-btn-skip" type="button" data-action="close">⏭ 무시</button>
          <button class="mini-btn mini-btn-ack" type="button" data-action="ack">✓ 확인 (ACK)</button>
        </footer>
      </div>
    `).join('');

    rows.querySelectorAll('[data-action]').forEach(btn => {
      btn.addEventListener('click', () => {
        const card = btn.closest('.alarm-mini-modal');
        if (!card) return;
        dismissOneAlarm(card.dataset.comp, btn.dataset.action);
      });
    });

    dock.classList.add('show');
  }

  function dismissOneAlarm(comp, action) {
    hidePidAlarmPin(comp);
    const rows = document.getElementById('alarm-list-rows');
    if (!rows) return;
    const card = rows.querySelector(`[data-comp="${comp}"]`);
    if (card) card.remove();
    const remaining = rows.querySelectorAll('.alarm-mini-modal').length;
    const count = document.getElementById('alarm-list-count');
    if (count) count.textContent = String(remaining);
    if (remaining === 0) {
      const dock = document.getElementById('alarm-list-dock');
      if (dock) dock.classList.remove('show');
    }
    const a = MULTI_ALARMS.find(x => x.comp === comp);
    if (a && global.INVYONE_ENGINE?.emitEvent) {
      const tag = action === 'ack' ? '확인(ACK)' : '무시';
      const lvl = action === 'ack' ? 'info' : 'warn';
      global.INVYONE_ENGINE.emitEvent(`[${a.severity.toUpperCase()}] ${a.code} ${tag} — ${a.msg}`, lvl);
    }
  }

  function stopMultiAlarm() {
    MULTI_ALARMS.forEach(a => hidePidAlarmPin(a.comp));
    const rows = document.getElementById('alarm-list-rows');
    const dock = document.getElementById('alarm-list-dock');
    if (rows) rows.innerHTML = '';
    if (dock) dock.classList.remove('show');
    const backdrop = document.getElementById('emergency-modal');
    if (backdrop) {
      // stack 안의 modal 과 dock 을 원위치로 복귀 후 stack 제거
      const stack = backdrop.querySelector('.emergency-stack');
      const modal = stack?.querySelector('.emergency-modal');
      if (modal) backdrop.appendChild(modal);
      if (dock) document.body.appendChild(dock);
      stack?.remove();
      backdrop.classList.remove('with-multi');
    }
  }

  let cctvClockTimer = null;
  function startCctvClock() {
    stopCctvClock();
    const upd = () => {
      const el = qid('modal-cctv-time');
      if (!el) return;
      const d = new Date();
      const pad = (n) => String(n).padStart(2, '0');
      el.textContent = `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
    };
    upd();
    cctvClockTimer = setInterval(upd, 1000);
  }
  function stopCctvClock() {
    if (cctvClockTimer) { clearInterval(cctvClockTimer); cctvClockTimer = null; }
  }

  function qid(id) { return document.getElementById(id); }

  global.INVYONE_UI = { init, select, clearSelection };
})(window);
