import http from 'node:http';
import { URL } from 'node:url';

const baseUrl = process.env.MCP_HTTP_URL ?? 'http://127.0.0.1:3940';
const authToken = process.env.MCP_HTTP_TOKEN;

function request(path, body, headers = {}) {
  return new Promise((resolve, reject) => {
    const url = new URL(path, baseUrl);
    const postData = body ? JSON.stringify(body) : undefined;
    const req = http.request(
      {
        hostname: url.hostname,
        port: url.port,
        path: url.pathname,
        method: body ? 'POST' : 'GET',
        headers: {
          Accept: 'application/json, text/event-stream',
          'Content-Type': 'application/json',
          ...(authToken ? { Authorization: `Bearer ${authToken}` } : {}),
          ...headers,
          ...(postData ? { 'Content-Length': Buffer.byteLength(postData) } : {}),
        },
      },
      (res) => {
        let data = '';
        res.on('data', (chunk) => {
          data += chunk;
        });
        res.on('end', () => {
          const parsedBody = parseSseBody(data);
          resolve({ status: res.statusCode, headers: res.headers, body: parsedBody });
        });
      },
    );
    req.on('error', reject);
    if (postData) req.write(postData);
    req.end();
  });
}

function parseSseBody(raw) {
  if (!raw.includes('event:')) return raw;
  const lines = raw.split(/\r?\n/);
  const dataLines = lines.filter((line) => line.startsWith('data:')).map((line) => line.slice(5).trim());
  return dataLines.join('');
}

async function main() {
  const health = await request('/healthz');
  console.log('healthz:', health.status, health.body);

  const init = await request('/mcp', {
    jsonrpc: '2.0',
    id: 1,
    method: 'initialize',
    params: {
      protocolVersion: '2024-11-05',
      capabilities: {},
      clientInfo: { name: 'http-smoke', version: '0.0.1' },
    },
  });
  console.log('initialize:', init.status, init.body);

  if (init.status !== 200) {
    throw new Error(`initialize failed: ${init.body}`);
  }

  const initResult = JSON.parse(init.body);
  const sessionId = init.headers['mcp-session-id'];
  console.log('sessionId:', sessionId);

  const notif = await request('/mcp', {
    jsonrpc: '2.0',
    method: 'notifications/initialized',
  }, { 'mcp-session-id': sessionId });
  console.log('notifications/initialized:', notif.status, notif.body);

  const resources = await request('/mcp', { jsonrpc: '2.0', id: 2, method: 'resources/list' }, { 'mcp-session-id': sessionId });
  console.log('resources/list:', resources.status, resources.body);

  const prompts = await request('/mcp', { jsonrpc: '2.0', id: 3, method: 'prompts/list' }, { 'mcp-session-id': sessionId });
  console.log('prompts/list:', prompts.status, prompts.body);

  const read = await request(
    '/mcp',
    { jsonrpc: '2.0', id: 4, method: 'resources/read', params: { uri: 'workspace://project.json' } },
    { 'mcp-session-id': sessionId },
  );
  console.log('resources/read:', read.status, read.body);

  const expectedResourceUris = ['workspace://project.json'];
  const expectedPrompts = ['new-feature', 'new-laravel-resource', 'new-codeigniter-resource', 'new-react-component'];

  let failed = false;
  const resourcesResult = JSON.parse(resources.body);
  const promptsResult = JSON.parse(prompts.body);
  const readResult = JSON.parse(read.body);

  const staticUris = resourcesResult?.result?.resources?.map((r) => r.uri) ?? [];
  for (const uri of expectedResourceUris) {
    if (!staticUris.includes(uri)) {
      console.error(`Missing static resource: ${uri}`);
      failed = true;
    }
  }

  const promptNames = promptsResult?.result?.prompts?.map((p) => p.name) ?? [];
  for (const name of expectedPrompts) {
    if (!promptNames.includes(name)) {
      console.error(`Missing prompt: ${name}`);
      failed = true;
    }
  }

  if (!readResult?.result?.contents?.[0]?.text) {
    console.error('resources/read failed', readResult);
    failed = true;
  }

  console.log('Result:', failed ? 'FAILED' : 'OK');
  process.exit(failed ? 1 : 0);
}

main().catch((error) => {
  console.error(error);
  process.exit(1);
});
