#!/usr/bin/env node

/**
 * Module dependencies.
 */

var app = require('../app');
var debug = require('debug')('proxy-api:server');
var http = require('http');
const conf = require('@simpleworkjs/conf');

/**
 * Get port from environment and store in Express.
 */

var port = normalizePort(process.env.NODE_PORT || conf.port || '3000');
app.set('port', port);

/**
 * Create HTTP server.
 */

var server = http.createServer(app);

var io = require('socket.io')(server);
app.io = io;

const WebSocket = require('ws');
const wss = new WebSocket.Server({ noServer: true });
server.on('upgrade', (request, socket, head) => {
  // We only handle upgrade for /api/agent/ws.
  // Socket.IO handles its own upgrades natively because it attaches directly to `server`.
  if (request.url.startsWith('/api/agent/ws')) {
    wss.handleUpgrade(request, socket, head, (ws) => {
      wss.emit('connection', ws, request);
    });
  }
});
app.wss = wss;

const models = require('../models');

/**
 * Initialize ORM, then Listen on provided port, on all network interfaces.
 */
models.initORM().then(() => {
  // Overlay secret/sso-manager/conf from OpenBao over the file-loaded conf.
  // Fail-soft: if OpenBao is unreachable, conf keeps the ./config/sso-secrets.js
  // values and boot continues. (Same position the old conf_manager held, so
  // call-time conf readers — which is how sso consumes its secrets — are
  // unaffected; nothing in sso captures a secret at require time.)
  return require('@simpleworkjs/bao-conf').init({ path: 'sso-manager', conf });
}).then(() => {
  server.listen(port);
  server.on('error', onError);
  server.on('listening', onListening);

  // Initialize scheduler
  const { initScheduler } = require('../services/scheduler');
  initScheduler(conf.discovery).catch(err => {
    console.error('Failed to initialize scheduler:', err);
  });

  // Keep external-app vault tokens alive: renew every stored accessor now and
  // on an interval (see vault_broker.startAppTokenRenewal). Only meaningful
  // when OpenBao is configured; without VAULT_TOKEN the loop's calls fail soft.
  if (process.env.VAULT_TOKEN) {
    require('../utils/vault_broker').startAppTokenRenewal();
  }
}).catch(err => {
  console.error('Failed to initialize ORM:', err);
  process.exit(1);
});

/**
 * Normalize a port into a number, string, or false.
 */

function normalizePort(val) {
  var port = parseInt(val, 10);

  if (isNaN(port)) {
    // named pipe
    return val;
  }

  if (port >= 0) {
    // port number
    return port;
  }

  return false;
}

/**
 * Event listener for HTTP server "error" event.
 */

function onError(error) {
  if (error.syscall !== 'listen') {
    throw error;
  }

  var bind = typeof port === 'string'
    ? 'Pipe ' + port
    : 'Port ' + port;

  // handle specific listen errors with friendly messages
  switch (error.code) {
    case 'EACCES':
      console.error(bind + ' requires elevated privileges');
      process.exit(1);
      break;
    case 'EADDRINUSE':
      console.error(bind + ' is already in use');
      process.exit(1);
      break;
    default:
      throw error;
  }
}

/**
 * Event listener for HTTP server "listening" event.
 */

function onListening() {
  var addr = server.address();
  var bind = typeof addr === 'string'
    ? 'pipe ' + addr
    : 'port ' + addr.port;
  console.log('Listening on ' + bind);

  for(let listener of app.onListen || []){
    listener()
  }
}
