const assert = require('node:assert/strict');
const { execFile } = require('node:child_process');
const { createServer } = require('node:http');
const path = require('node:path');
const { test } = require('node:test');

const { evaluatePerformanceGates, parseArgs } = require('./load-test');

const passingSummary = {
  requestsPerSecond: 250.25,
  successRate: 99.95,
  latencyMs: { p95: 120.5, p99: 180.75 },
};

test('parseArgs reads all optional performance gates', () => {
  const options = parseArgs([
    '--min-success-rate',
    '99.9',
    '--min-rps',
    '200',
    '--max-p95',
    '150',
    '--max-p99',
    '250',
  ]);

  assert.deepEqual(options.gates, {
    minSuccessRate: 99.9,
    minRequestsPerSecond: 200,
    maxP95Ms: 150,
    maxP99Ms: 250,
  });
});

test('parseArgs rejects invalid gate values', () => {
  assert.throws(
    () => parseArgs(['--min-success-rate', '100.1']),
    /min-success-rate must be a finite number between 0 and 100/,
  );
  assert.throws(() => parseArgs(['--min-rps', '-1']), /min-rps must be a finite number/);
  assert.throws(() => parseArgs(['--max-p95']), /--max-p95 requires a value/);
});

test('evaluatePerformanceGates passes when every threshold is satisfied', () => {
  const result = evaluatePerformanceGates(passingSummary, {
    minSuccessRate: 99.9,
    minRequestsPerSecond: 200,
    maxP95Ms: 150,
    maxP99Ms: 200,
  });

  assert.equal(result.configured, true);
  assert.equal(result.passed, true);
  assert.equal(result.checks.length, 4);
  assert.ok(result.checks.every((check) => check.passed));
});

test('evaluatePerformanceGates reports every failed threshold', () => {
  const result = evaluatePerformanceGates(passingSummary, {
    minSuccessRate: 100,
    minRequestsPerSecond: 300,
    maxP95Ms: 100,
    maxP99Ms: 150,
  });

  assert.equal(result.passed, false);
  assert.deepEqual(
    result.checks.filter((check) => !check.passed).map((check) => check.metric),
    ['successRate', 'requestsPerSecond', 'latencyMs.p95', 'latencyMs.p99'],
  );
});

test('load-test process exits with code 2 and a clear report when a gate fails', async () => {
  const server = createServer((_request, response) => {
    response.statusCode = 503;
    response.end('unavailable');
  });

  await new Promise((resolve, reject) => {
    server.once('error', reject);
    server.listen(0, '127.0.0.1', resolve);
  });

  try {
    const address = server.address();
    assert.ok(address && typeof address === 'object');

    const result = await runLoadTest([
      '--url',
      `http://127.0.0.1:${address.port}`,
      '--duration',
      '0.05',
      '--concurrency',
      '1',
      '--warmup',
      '0',
      '--min-success-rate',
      '100',
    ]);

    assert.equal(result.exitCode, 2);
    assert.match(result.stdout, /Performance gates: FAILED \(0\/1\)/);
    assert.match(result.stdout, /\[FAIL\] Success rate:/);
  } finally {
    await new Promise((resolve, reject) => {
      server.close((error) => (error ? reject(error) : resolve()));
    });
  }
});

function runLoadTest(args) {
  const scriptPath = path.join(__dirname, 'load-test.js');

  return new Promise((resolve) => {
    execFile(process.execPath, [scriptPath, ...args], (error, stdout, stderr) => {
      resolve({
        exitCode: error && typeof error.code === 'number' ? error.code : 0,
        stdout,
        stderr,
      });
    });
  });
}
