import { test } from 'node:test';
import assert from 'node:assert/strict';
import http from 'node:http';
import type { AddressInfo } from 'node:net';
import type { Server } from 'node:http';
import { fetchFeed, FetchError } from '../src/feeds/fetcher.ts';

interface StartedServer {
  server: Server;
  port: number;
}

function startServer(handler: http.RequestListener): Promise<StartedServer> {
  const server = http.createServer(handler);
  return new Promise((resolve) => {
    server.listen(0, '127.0.0.1', () =>
      resolve({ server, port: (server.address() as AddressInfo).port }),
    );
  });
}

test('fetch returns body and headers', async () => {
  const { server, port } = await startServer((req, res) => {
    res.setHeader('ETag', '"abc"');
    res.end('hello');
  });
  const r = await fetchFeed(`http://127.0.0.1:${port}/`, {}, async () => {});
  assert.equal(r.status, 200);
  assert.equal(r.body, 'hello');
  assert.equal(r.etag, '"abc"');
  server.close();
});

test('handles 304', async () => {
  const { server, port } = await startServer((req, res) => {
    if (req.headers['if-none-match'] === '"abc"') {
      res.statusCode = 304;
      res.end();
      return;
    }
    res.setHeader('ETag', '"abc"');
    res.end('hello');
  });
  const first = await fetchFeed(`http://127.0.0.1:${port}/`, {}, async () => {});
  const second = await fetchFeed(
    `http://127.0.0.1:${port}/`,
    { etag: first.etag ?? undefined },
    async () => {},
  );
  assert.equal(second.status, 304);
  server.close();
});

test('enforces redirect limit', async () => {
  const { server, port } = await startServer((req, res) => {
    res.statusCode = 302;
    res.setHeader('Location', `http://127.0.0.1:${port}/r`);
    res.end();
  });
  await assert.rejects(
    fetchFeed(`http://127.0.0.1:${port}/`, {}, async () => {}),
    FetchError,
  );
  server.close();
});

test('rejects oversized body', async () => {
  const { server, port } = await startServer((req, res) => {
    res.end('x'.repeat(6 * 1024 * 1024));
  });
  await assert.rejects(
    fetchFeed(`http://127.0.0.1:${port}/`, {}, async () => {}),
    /too large/,
  );
  server.close();
});

test('ssrf blocks private url', async () => {
  await assert.rejects(fetchFeed('http://127.0.0.1:9999/'), (e: unknown) => {
    const name = (e as Error | undefined)?.name;
    return name === 'SsrfError' || name === 'FetchError';
  });
});
