import assert from 'node:assert/strict';
import test from 'node:test';
import { DedicatedSupervisor } from '../server/supervisor.js';

test('dedicated supervisor backs off after a spawn failure and stops retries', async () => {
  const supervisor = new DedicatedSupervisor({
    enabled: true,
    command: '/definitely/missing/openq4ded',
    args: [],
    cwd: '/',
    backoffBaseMs: 100,
    backoffMaxMs: 200,
    stableMs: 1_000,
    stopTimeoutMs: 100,
  }, { info() {}, warn() {} });

  supervisor.start();
  await new Promise((resolve) => setTimeout(resolve, 30));
  const failed = supervisor.status();
  assert.equal(failed.running, false);
  assert.equal(failed.restartPending, true);
  assert.equal(failed.restartAttempt, 1);
  assert.match(failed.lastExit.error, /ENOENT/);

  await supervisor.stop();
  await new Promise((resolve) => setTimeout(resolve, 130));
  const stopped = supervisor.status();
  assert.equal(stopped.running, false);
  assert.equal(stopped.restartPending, false);
  assert.equal(stopped.restartAttempt, 1);
});

test('dedicated supervisor is not ready until OpenQ4 finishes loading its map', async (t) => {
  const supervisor = new DedicatedSupervisor({
    enabled: true,
    command: '/bin/sh',
    args: ['-c', "printf 'starting\\n'; sleep 0.05; printf 'net_LANServer is enabled. Not sending heartbeats\\n'; exec sleep 60"],
    cwd: '/',
    backoffBaseMs: 100,
    backoffMaxMs: 200,
    stableMs: 1_000,
    stopTimeoutMs: 100,
  }, { info() {}, warn() {} });
  t.after(() => supervisor.stop());

  supervisor.start();
  assert.equal(supervisor.status().running, true);
  assert.equal(supervisor.status().ready, false);

  const deadline = Date.now() + 1_000;
  while (!supervisor.status().ready && Date.now() < deadline) {
    await new Promise((resolve) => setTimeout(resolve, 10));
  }
  const ready = supervisor.status();
  assert.equal(ready.running, true);
  assert.equal(ready.ready, true);
  assert.match(ready.readyAt, /^\d{4}-\d{2}-\d{2}T/);

  await supervisor.stop();
  assert.equal(supervisor.status().running, false);
  assert.equal(supervisor.status().ready, false);
});

test('dedicated supervisor finds readiness before the tail of a large output chunk', async (t) => {
  const marker = 'net_LANServer is enabled. Not sending heartbeats\n';
  const program = [
    'import os, time',
    `os.write(1, b"${marker.replace(/\n/g, '\\n')}" + b"x" * 600)`,
    'time.sleep(60)',
  ].join('; ');
  const supervisor = new DedicatedSupervisor({
    enabled: true,
    command: '/usr/bin/python3',
    args: ['-c', program],
    cwd: '/',
    backoffBaseMs: 100,
    backoffMaxMs: 200,
    stableMs: 1_000,
    stopTimeoutMs: 100,
  }, { info() {}, warn() {} });
  t.after(() => supervisor.stop());

  supervisor.start();
  const deadline = Date.now() + 1_000;
  while (!supervisor.status().ready && Date.now() < deadline) {
    await new Promise((resolve) => setTimeout(resolve, 10));
  }
  assert.equal(supervisor.status().ready, true);
});
