/**
 * SipJambonzEngine — registers the /sip-stream/:sessionId WebSocket endpoint
 * for jambonz audio streams. Only registered when FEATURE_SIP_ENABLED is on.
 */
import * as http from 'http';
import { WebSocketServer, WebSocket } from 'ws';
import { IVoiceEngine } from '../types';
import { handleSipCall } from './bridge';

import { childLogger } from '@server/logger';
const log = childLogger('engine.sip');

/**
 * Allow-list of upgrade Origins for the public /sip-stream/* endpoint.
 *
 * Comma-separated values from JAMBONZ_WS_ALLOWED_ORIGINS (e.g.
 * "https://your-jambonz.example.com"). When unset:
 *   - production: only allow same-origin (host header equals origin host)
 *     and reject everything else.
 *   - dev/test:   permissive (so localhost tools can connect).
 *
 * jambonz callouts typically have NO `Origin` header — those are accepted
 * in both modes; the allow-list only restricts browser-style upgrades.
 */
function isOriginAllowed(req: http.IncomingMessage): boolean {
  const origin = req.headers.origin;
  if (!origin) return true; // server-to-server (jambonz) — no Origin header

  const list = (process.env.JAMBONZ_WS_ALLOWED_ORIGINS || '')
    .split(',')
    .map(s => s.trim())
    .filter(Boolean);

  if (list.length > 0) return list.includes(origin);

  if (process.env.NODE_ENV !== 'production') return true;

  try {
    const originHost = new URL(origin).host;
    return originHost === (req.headers.host || '');
  } catch {
    return false;
  }
}

export class SipJambonzEngine implements IVoiceEngine {
  attach(server: http.Server): void {
    const wss = new WebSocketServer({ noServer: true });

    server.on('upgrade', (request, socket, head) => {
      const url = new URL(request.url || '/', `http://${request.headers.host}`);

      if (!url.pathname.startsWith('/sip-stream/')) return;

      if (!isOriginAllowed(request)) {
        log.warn({ origin: request.headers.origin }, 'rejected upgrade');
        socket.write('HTTP/1.1 403 Forbidden\r\n\r\n');
        socket.destroy();
        return;
      }

      let sessionId: string;
      try {
        sessionId = decodeURIComponent(url.pathname.slice('/sip-stream/'.length));
      } catch {
        log.warn('malformed session ID encoding in path — rejecting');
        socket.destroy();
        return;
      }
      if (!sessionId) {
        log.warn('upgrade request missing session ID in path — rejecting');
        socket.destroy();
        return;
      }

      wss.handleUpgrade(request, socket, head, (ws: WebSocket) => {
        wss.emit('connection', ws, request);
        handleSipCall(ws, sessionId).catch((err) => {
          log.error({ err }, 'unhandled error in handleSipCall');
          ws.close();
        });
      });
    });

    log.info('SipJambonzEngine attached — listening for /sip-stream upgrades');
  }
}
