// Graceful shutdown
process.on('SIGINT', () => {
  console.log('[pty-sidecar] shutting down...');
  store.killAll();
  if (ws) {
    try {
      ws.close();
    } catch {
      /* ignore */
    }
  }
  process.exit(0);
});
process.on('SIGTERM', () => process.emit('SIGINT'));

import WebSocket from 'ws';
import * as store from './sessions.mjs';
import { startServer } from './server.mjs';

import 'dotenv/config';
import { API_URL } from '../../officer-url.mjs';

const REGISTER_URL = `${API_URL}/api/sidecar/register`;
const RECONNECT_DELAYS = [200, 500, 1000, 2000, 4000, 8000, 15000];

// This file is now only the registration socket.
//
// It used to be the whole sidecar: the platform sent pty:init / pty:input / pty:resize / pty:close, and
// officer filtered one global output stream per session and rewrapped every frame, double-JSON-encoded.
// Terminals live in `sessions.mjs` and are served over this process's own listener (`server.mjs`), which
// the browser reaches through a byte relay. All that crosses this socket now is the port number.

let serverPort = null;

const sendJson = (msg) => {
  try {
    if (ws && ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify(msg));
  } catch {
    // ignore
  }
};

// ── Connect to API server with reconnect ──

let ws = null;
let reconnectAttempt = 0;
let reconnectTimer = null;

console.log(`[pty-sidecar] starting, connecting to ${REGISTER_URL}`);

function connect() {
  if (ws && (ws.readyState === WebSocket.CONNECTING || ws.readyState === WebSocket.OPEN)) return;

  try {
    ws = new WebSocket(REGISTER_URL);
  } catch (err) {
    console.error(`[pty-sidecar] failed to create WebSocket:`, err.message ?? err);
    scheduleReconnect();
    return;
  }

  ws.on('open', () => {
    reconnectAttempt = 0;
    console.log('[pty-sidecar] connected, sending registration...');
    sendJson({ type: 'register', name: 'pty', capabilities: ['terminal'] });
    // Re-announce on every reconnect: officer forgets the port when the socket drops, and this process
    // keeps the same listener across officer restarts.
    if (serverPort) sendJson({ type: 'pty:server', port: serverPort });
  });

  ws.on('message', (data) => {
    try {
      const msg = JSON.parse(typeof data === 'string' ? data : data.toString());
      if (msg.type === 'registered') {
        console.log(`[pty-sidecar] registered with API server (id=${msg.id})`);
        if (serverPort) sendJson({ type: 'pty:server', port: serverPort });
      }
    } catch {
      // skip malformed messages
    }
  });

  ws.on('close', () => {
    console.log('[pty-sidecar] disconnected from API server');
    ws = null;
    scheduleReconnect();
  });

  ws.on('error', (err) => {
    if (reconnectAttempt <= 1) {
      console.error(`[pty-sidecar] connection error: ${err.message ?? err}`);
    }
    // onclose will fire after this
  });
}

function scheduleReconnect() {
  if (reconnectTimer) return;
  const delay = RECONNECT_DELAYS[Math.min(reconnectAttempt, RECONNECT_DELAYS.length - 1)];
  reconnectAttempt++;
  reconnectTimer = setTimeout(() => {
    reconnectTimer = null;
    connect();
  }, delay);
}

// Listener first, so the port is known before the first registration goes out.
startServer().then((port) => {
  serverPort = port;
  connect();
});
