const fs = require('fs');
const os = require('os');
const path = require('path');

const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mazex-api-security-'));
process.env.MAZEX_DATA_DIR = dataDir;
process.env.MAZEX_TRUST_PROXY = '1';
process.env.MAZEX_API_ALLOWED_ORIGINS = 'http://allowed.example';

const { server } = require('../src/server');

async function request(baseUrl, pathName, options = {}) {
  const response = await fetch(`${baseUrl}${pathName}`, options);
  const text = await response.text();
  let body = null;
  try {
    body = text ? JSON.parse(text) : null;
  } catch {
    body = { raw: text };
  }
  return { response, body };
}

function assert(condition, message) {
  if (!condition) throw new Error(message);
}

async function main() {
  await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
  const { port } = server.address();
  const baseUrl = `http://127.0.0.1:${port}`;

  try {
    const health = await request(baseUrl, '/api/health');
    assert(health.response.status === 200, `health expected 200, got ${health.response.status}`);
    assert(health.response.headers.get('content-security-policy'), 'missing Content-Security-Policy');
    assert(health.response.headers.get('x-request-id'), 'missing X-Request-Id');

    const badType = await request(baseUrl, '/api/base1/sample', {
      method: 'POST',
      headers: { 'Content-Type': 'text/plain' },
      body: '{"x":1,"y":1}',
    });
    assert(badType.response.status === 415, `bad content-type expected 415, got ${badType.response.status}`);
    assert(badType.body.code === 'UNSUPPORTED_MEDIA_TYPE', `unexpected bad type code ${badType.body.code}`);

    const badOrigin = await request(baseUrl, '/api/base1/sample', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Origin: 'http://evil.example',
      },
      body: '{"x":1,"y":1}',
    });
    assert(badOrigin.response.status === 403, `bad origin expected 403, got ${badOrigin.response.status}`);
    assert(badOrigin.body.code === 'ORIGIN_NOT_ALLOWED', `unexpected origin code ${badOrigin.body.code}`);

    const forbiddenKey = await request(baseUrl, '/api/base1/sample', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: '{"x":1,"y":1,"__proto__":{"polluted":true}}',
    });
    assert(forbiddenKey.response.status === 400, `forbidden key expected 400, got ${forbiddenKey.response.status}`);
    assert(forbiddenKey.body.code === 'JSON_FORBIDDEN_KEY', `unexpected json key code ${forbiddenKey.body.code}`);

    const walletRemote = await request(baseUrl, '/api/wallet/create', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-Forwarded-For': '203.0.113.9',
      },
      body: '{}',
    });
    assert(walletRemote.response.status === 403, `remote wallet expected 403, got ${walletRemote.response.status}`);
    assert(walletRemote.body.code === 'WALLET_API_LOOPBACK_ONLY', `unexpected wallet code ${walletRemote.body.code}`);

    const preflight = await request(baseUrl, '/api/base1/sample', {
      method: 'OPTIONS',
      headers: {
        Origin: 'http://allowed.example',
        'Access-Control-Request-Method': 'POST',
      },
    });
    assert(preflight.response.status === 204, `preflight expected 204, got ${preflight.response.status}`);
    assert(
      preflight.response.headers.get('access-control-allow-origin') === 'http://allowed.example',
      'allowed CORS origin was not echoed',
    );

    console.table([
      { check: 'security headers', status: health.response.status, request_id: Boolean(health.response.headers.get('x-request-id')) },
      { check: 'content type gate', status: badType.response.status, code: badType.body.code },
      { check: 'origin gate', status: badOrigin.response.status, code: badOrigin.body.code },
      { check: 'json pollution gate', status: forbiddenKey.response.status, code: forbiddenKey.body.code },
      { check: 'wallet loopback gate', status: walletRemote.response.status, code: walletRemote.body.code },
      { check: 'cors preflight', status: preflight.response.status, origin: preflight.response.headers.get('access-control-allow-origin') },
    ]);
  } finally {
    await new Promise((resolve) => server.close(resolve));
    fs.rmSync(dataDir, { recursive: true, force: true });
  }
}

main().catch(async (error) => {
  try {
    await new Promise((resolve) => server.close(resolve));
  } catch {}
  fs.rmSync(dataDir, { recursive: true, force: true });
  console.error(error);
  process.exit(1);
});
