// Shared OCaml HTTP server lifecycle for Playwright tests.
// Starts sx_server.exe --http on a random port, waits for ready.
// One instance serves all tests in a spec file.

const { spawn } = require('child_process');
const path = require('path');

const PROJECT_ROOT = path.resolve(__dirname, '../..');

class SiteServer {
  constructor() {
    this.port = 49152 + Math.floor(Math.random() * 16000);
    this.proc = null;
    this._stderr = '';
  }

  async start() {
    const serverBin = path.join(PROJECT_ROOT, 'hosts/ocaml/_build/default/bin/sx_server.exe');

    this.proc = spawn(serverBin, ['--http', String(this.port)], {
      cwd: PROJECT_ROOT,
      env: { ...process.env, SX_PROJECT_DIR: PROJECT_ROOT, OCAMLRUNPARAM: 'b' },
      stdio: ['ignore', 'pipe', 'pipe'],
    });

    this.proc.stderr.on('data', chunk => { this._stderr += chunk.toString(); });
    this.proc.stdout.on('data', () => {});

    await new Promise((resolve, reject) => {
      const timeout = setTimeout(() => reject(new Error('Server did not start within 30s\n' + this._stderr.slice(-500))), 30000);
      this.proc.stderr.on('data', () => {
        if (this._stderr.includes('Listening on port')) {
          clearTimeout(timeout);
          resolve();
        }
      });
      this.proc.on('error', err => { clearTimeout(timeout); reject(err); });
      this.proc.on('exit', code => { clearTimeout(timeout); reject(new Error('Server exited: ' + code)); });
    });
  }

  get baseUrl() { return `http://localhost:${this.port}`; }

  stop() {
    if (this.proc && !this.proc.killed) {
      this.proc.kill('SIGTERM');
      setTimeout(() => { if (this.proc && !this.proc.killed) this.proc.kill('SIGKILL'); }, 2000);
    }
  }
}

module.exports = { SiteServer, PROJECT_ROOT };
