// 轻量信令服务器，协议与 WebRTCSignalServer (Spring) 完全兼容。
// 端点: ws://host:PORT/ws/signal
// 支持消息: REGISTER / DEVICE_LIST / OFFER / ANSWER / ICE_CANDIDATE / CONTROL_COMMAND / CONNECTION_REJECTED
// 支持通知: REGISTER_SUCCESS / DEVICE_LIST / TARGET_OFFLINE / REQUEST_ERROR / REQUEST_TIMEOUT
import { WebSocketServer } from 'ws';

const PORT = Number(process.env.PORT || 8088);
const PATH = process.env.WS_PATH || '/ws/signal';

const wss = new WebSocketServer({
  port: PORT,
  path: PATH,
  // WebRTC SDP 消息可能较大，将最大负载设为 1MB（默认 100MB 但在某些代理环境下受限）
  maxPayload: 1024 * 1024,
});

// deviceId -> { ws, type }
const sessions = new Map();
// "from->to" -> true（等待被控端确认）
const pending = new Map();

const keyOf = (a, b) => `${a}->${b}`;

function send(ws, obj) {
  if (ws && ws.readyState === ws.OPEN) {
    try {
      ws.send(JSON.stringify(obj));
    } catch (e) {
      console.error('[send] 发送消息失败:', e.message);
    }
  }
}

// —— 服务端主动 ping/pong 保活：每 30 秒向所有连接发送 ping，
//    若 60 秒内未收到 pong 则强制关闭连接（防止僵死连接占用资源）。
const HEARTBEAT_INTERVAL = 30_000;
const heartbeatTimer = setInterval(() => {
  wss.clients.forEach((ws) => {
    if (ws._alive === false) {
      console.warn('[heartbeat] 连接无响应，强制关闭');
      return ws.terminate();
    }
    ws._alive = false;
    try { ws.ping(); } catch { /* ignore */ }
  });
}, HEARTBEAT_INTERVAL);
wss.on('close', () => clearInterval(heartbeatTimer));

wss.on('connection', (ws) => {
  ws._alive = true;
  ws.on('pong', () => { ws._alive = true; });

  ws.on('message', (data) => {
    let msg;
    try {
      msg = JSON.parse(data.toString());
    } catch {
      return;
    }
    const type = (msg.type || '').toUpperCase();
    // 忽略客户端心跳 ping，仅用于保活
    if (type === 'PING') return;
    switch (type) {
      case 'REGISTER': handleRegister(ws, msg); break;
      case 'DEVICE_LIST': handleDeviceList(ws); break;
      case 'OFFER': handleOffer(msg); break;
      case 'ANSWER':
      case 'CONNECTION_REJECTED':
        pending.delete(keyOf(msg.toDeviceId, msg.fromDeviceId));
        forward(msg);
        break;
      case 'ICE_CANDIDATE':
      case 'CONTROL_COMMAND':
        forward(msg);
        break;
      default:
        forward(msg);
    }
  });

  ws.on('close', () => {
    for (const [id, s] of sessions) {
      if (s.ws === ws) { sessions.delete(id); break; }
    }
  });
});

function handleRegister(ws, msg) {
  const id = msg.fromDeviceId;
  const type = (msg.deviceType || '').toUpperCase();
  if (!id || !type) return;
  sessions.set(id, { ws, type });
  send(ws, { type: 'REGISTER_SUCCESS', deviceId: id });
  console.log(`设备注册: ${id} (${type})`);
}

function handleDeviceList(ws) {
  const controllers = [];
  const controlled = [];
  for (const [id, s] of sessions) {
    if (s.type === 'CONTROLLER') controllers.push(id);
    else if (s.type === 'CONTROLLED') controlled.push(id);
  }
  send(ws, { type: 'DEVICE_LIST', controllers, controlled });
}

function handleOffer(msg) {
  const from = msg.fromDeviceId;
  const to = msg.toDeviceId;
  const fromType = sessions.get(from)?.type;
  const toType = sessions.get(to)?.type;

  if (fromType !== 'CONTROLLER' || toType !== 'CONTROLLED') {
    send(sessions.get(from)?.ws, {
      type: 'REQUEST_ERROR',
      toDeviceId: to,
      payload: '连接请求只能由 CONTROLLER 发往 CONTROLLED',
    });
    return;
  }
  const target = sessions.get(to)?.ws;
  if (!target) {
    send(sessions.get(from)?.ws, {
      type: 'TARGET_OFFLINE',
      toDeviceId: to,
      payload: '目标被控端不在线，请确认设备已开启并连接服务器',
    });
    return;
  }
  pending.set(keyOf(from, to), true);
  forward(msg);
}

function forward(msg) {
  const target = sessions.get(msg.toDeviceId)?.ws;
  if (!target) return;
  send(target, msg);
}

console.log(`信令服务器已启动: ws://localhost:${PORT}${PATH}`);
