import { createServer, type Server } from 'node:http';
import type { AddressInfo } from 'node:net';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
import { gameServerHealthy } from './health';

let server: Server;
let base = '';

async function listen(s: Server): Promise<string> {
  await new Promise<void>((resolve) => s.listen(0, '127.0.0.1', resolve));
  return `http://127.0.0.1:${(s.address() as AddressInfo).port}`;
}

beforeAll(async () => {
  server = createServer((req, res) => {
    if (req.url === '/health') res.writeHead(200, { 'content-type': 'application/json' }).end(JSON.stringify({ ok: true, uptimeSec: 1 }));
    else if (req.url === '/broken/health') res.writeHead(500).end();
    else res.writeHead(404).end();
  });
  base = await listen(server);
});

afterAll(async () => {
  await new Promise<void>((resolve) => server.close(() => resolve()));
});

describe('gameServerHealthy', () => {
  it('is true when /health answers {ok: true}, resolved from the origin', async () => {
    expect(await gameServerHealthy(base)).toBe(true);
    expect(await gameServerHealthy(`${base}/some/path`)).toBe(true);
  });

  it('is false for a refused connection or an invalid URL', async () => {
    const closed = createServer();
    const url = await listen(closed);
    await new Promise<void>((resolve) => closed.close(() => resolve()));
    expect(await gameServerHealthy(url, 1000)).toBe(false);
    expect(await gameServerHealthy('not a url')).toBe(false);
  });

  it('is false for an error status or an unexpected body', async () => {
    const odd = createServer((req, res) => {
      if (req.url === '/health') res.writeHead(503).end(JSON.stringify({ ok: true }));
    });
    const oddUrl = await listen(odd);
    expect(await gameServerHealthy(oddUrl)).toBe(false);
    await new Promise<void>((resolve) => odd.close(() => resolve()));

    const html = createServer((_req, res) => res.writeHead(200, { 'content-type': 'text/html' }).end('<html>proxy page</html>'));
    const htmlUrl = await listen(html);
    expect(await gameServerHealthy(htmlUrl)).toBe(false);
    await new Promise<void>((resolve) => html.close(() => resolve()));
  });

  it('is false when the server is too slow', async () => {
    const slow = createServer(() => undefined); // never responds
    const url = await listen(slow);
    expect(await gameServerHealthy(url, 150)).toBe(false);
    slow.closeAllConnections();
    await new Promise<void>((resolve) => slow.close(() => resolve()));
  });
});
