// Remote Client — 활성화 시 Remote Manager 서버로 아웃바운드 연결하여
// 원격 상태 조회 / 제어 / 패치·업그레이드 / 재시작 명령을 수행한다.
// 보안: 매니저가 발급한 토큰으로 인증하며, 사용자가 직접 활성화해야만 동작한다.
import { WebSocket } from 'ws';

export function createRemoteClient(getConfig, handlers) {
  let ws = null;
  let stopped = false;
  let reconnectTimer = null;
  let hbTimer = null;
  let state = 'disabled'; // disabled | connecting | connected | disconnected | denied

  function log(...a) { console.log('[remote]', ...a); }
  function setState(s) { state = s; handlers.onState?.(s); }

  function send(obj) { if (ws && ws.readyState === 1) ws.send(JSON.stringify(obj)); }

  function connect() {
    const cfg = getConfig();
    if (stopped || !cfg.enabled || !cfg.managerUrl) { setState('disabled'); return; }
    setState('connecting');
    try {
      ws = new WebSocket(cfg.managerUrl, { handshakeTimeout: 8000 });
    } catch (e) {
      log('연결 실패:', e.message); scheduleReconnect(); return;
    }

    ws.on('open', () => {
      log('매니저 연결됨:', cfg.managerUrl);
      send({ type: 'hello', token: cfg.token, name: cfg.clientName || 'signage', version: handlers.version, platform: process.platform });
    });

    ws.on('message', async (raw) => {
      let msg; try { msg = JSON.parse(raw.toString()); } catch { return; }
      if (msg.type === 'welcome') {
        setState('connected');
        clearInterval(hbTimer);
        hbTimer = setInterval(sendHeartbeat, 5000);
        sendHeartbeat();
      } else if (msg.type === 'denied') {
        log('인증 거부됨 — 토큰을 확인하세요');
        setState('denied');
      } else if (msg.type === 'cmd') {
        await handleCommand(msg);
      }
    });

    ws.on('close', () => { cleanup(); if (state !== 'denied') setState('disconnected'); scheduleReconnect(); });
    ws.on('error', (e) => { log('오류:', e.message); });
  }

  async function handleCommand(msg) {
    const { id, action, payload } = msg;
    const reply = (ok, data) => send({ type: 'result', id, ok, data });
    try {
      if (action === 'status') {
        reply(true, handlers.getStatus());
      } else if (action === 'config') {
        reply(true, handlers.getConfig());
      } else if (action === 'screenshot') {
        const shot = await handlers.captureScreen();
        reply(true, shot);
      } else if (action === 'history') {
        reply(true, { history: handlers.getHistory(payload && payload.limit) });
      } else if (action === 'logs') {
        reply(true, { logs: handlers.getLogs() });
      } else if (action === 'control') {
        const r = await handlers.control(payload || {});
        reply(true, { applied: r, status: handlers.getStatus() });
      } else if (action === 'update') {
        const cfg = getConfig();
        if (!cfg.allowUpdate) return reply(false, { error: '이 전광판은 원격 업데이트가 비활성화되어 있습니다' });
        const r = handlers.applyUpdate(payload?.files || []);
        reply(r.ok, r);
        if (r.ok && payload?.restart !== false) setTimeout(() => handlers.restart(), 600);
      } else if (action === 'restart') {
        reply(true, { restarting: true });
        setTimeout(() => handlers.restart(), 400);
      } else {
        reply(false, { error: '알 수 없는 명령: ' + action });
      }
    } catch (e) {
      reply(false, { error: e.message });
    }
  }

  function sendHeartbeat() { send({ type: 'status', data: handlers.getStatus() }); }

  function cleanup() { clearInterval(hbTimer); hbTimer = null; if (ws) { try { ws.removeAllListeners(); } catch {} } ws = null; }

  function scheduleReconnect() {
    clearTimeout(reconnectTimer);
    if (stopped) return;
    const cfg = getConfig();
    if (!cfg.enabled) { setState('disabled'); return; }
    reconnectTimer = setTimeout(connect, 3000);
  }

  return {
    start() { stopped = false; connect(); },
    stop() { stopped = true; clearTimeout(reconnectTimer); cleanup(); setState('disabled'); if (ws) try { ws.close(); } catch {} },
    restart() { this.stop(); stopped = false; setTimeout(connect, 300); },
    getState: () => state,
  };
}
