import * as plugins from '@git.zone/tstest/tapbundle';
import { expect, tap } from '@git.zone/tstest/tapbundle';
import * as net from 'net';
import { startTestServer, stopTestServer } from '../../helpers/server.loader.js';

const TEST_PORT = 2525;

let testServer;

// Helper function to wait for SMTP response
const waitForResponse = (socket: net.Socket, expectedCode: string, timeout = 5000): Promise<string> => {
  return new Promise((resolve, reject) => {
    let buffer = '';
    const timer = setTimeout(() => {
      socket.removeListener('data', handler);
      reject(new Error(`Timeout waiting for ${expectedCode} response`));
    }, timeout);
    
    const handler = (data: Buffer) => {
      buffer += data.toString();
      const lines = buffer.split('\r\n');
      
      // Check if we have a complete response
      for (const line of lines) {
        if (line.startsWith(expectedCode + ' ')) {
          clearTimeout(timer);
          socket.removeListener('data', handler);
          resolve(buffer);
          return;
        }
      }
    };
    
    socket.on('data', handler);
  });
};

tap.test('prepare server', async () => {
  testServer = await startTestServer({ port: TEST_PORT });
  await new Promise(resolve => setTimeout(resolve, 100));
});

tap.test('PERF-06: Message processing time - Various message sizes', async (tools) => {
  const done = tools.defer();
  const messageSizes = [1000, 5000, 10000, 25000, 50000]; // bytes
  const messageProcessingTimes: number[] = [];
  const processingRates: number[] = [];

  try {
    const socket = net.createConnection({
      host: 'localhost',
      port: TEST_PORT,
      timeout: 30000
    });

    await new Promise<void>((resolve, reject) => {
      socket.once('connect', resolve);
      socket.once('error', reject);
    });

    // Read greeting
    await waitForResponse(socket, '220');

    // Send EHLO
    socket.write('EHLO testhost\r\n');
    await waitForResponse(socket, '250');

    console.log('Testing message processing times for various sizes...\n');

    for (let i = 0; i < messageSizes.length; i++) {
      const messageSize = messageSizes[i];
      const messageContent = 'A'.repeat(messageSize);
      
      const messageStart = Date.now();

      // Send MAIL FROM
      socket.write(`MAIL FROM:<sender${i}@example.com>\r\n`);
      await waitForResponse(socket, '250');

      // Send RCPT TO
      socket.write(`RCPT TO:<recipient${i}@example.com>\r\n`);
      await waitForResponse(socket, '250');

      // Send DATA
      socket.write('DATA\r\n');
      await waitForResponse(socket, '354');

      // Send email content
      const emailContent = [
        `From: sender${i}@example.com`,
        `To: recipient${i}@example.com`,
        `Subject: Message Processing Test ${i} (${messageSize} bytes)`,
        '',
        messageContent,
        '.',
        ''
      ].join('\r\n');

      socket.write(emailContent);
      await waitForResponse(socket, '250');

      const messageProcessingTime = Date.now() - messageStart;
      messageProcessingTimes.push(messageProcessingTime);
      
      const processingRateKBps = (messageSize / 1024) / (messageProcessingTime / 1000);
      processingRates.push(processingRateKBps);

      console.log(`${messageSize} bytes: ${messageProcessingTime}ms (${processingRateKBps.toFixed(1)} KB/s)`);

      // Send RSET
      socket.write('RSET\r\n');
      
      await new Promise<void>((resolve) => {
        socket.once('data', (chunk) => {
          const response = chunk.toString();
          expect(response).toInclude('250');
          resolve();
        });
      });

      // Small delay between tests
      await new Promise(resolve => setTimeout(resolve, 100));
    }

    // Calculate statistics
    const avgProcessingTime = messageProcessingTimes.reduce((a, b) => a + b, 0) / messageProcessingTimes.length;
    const avgProcessingRate = processingRates.reduce((a, b) => a + b, 0) / processingRates.length;
    const minProcessingTime = Math.min(...messageProcessingTimes);
    const maxProcessingTime = Math.max(...messageProcessingTimes);

    console.log(`\nMessage Processing Results:`);
    console.log(`Average processing time: ${avgProcessingTime.toFixed(0)}ms`);
    console.log(`Min/Max processing time: ${minProcessingTime}ms / ${maxProcessingTime}ms`);
    console.log(`Average processing rate: ${avgProcessingRate.toFixed(1)} KB/s`);

    socket.write('QUIT\r\n');
    socket.end();

    // Test passes if average processing time is less than 3000ms and rate > 10KB/s
    expect(avgProcessingTime).toBeLessThan(3000);
    expect(avgProcessingRate).toBeGreaterThan(10);
    done.resolve();
  } catch (error) {
    done.reject(error);
  }
});

tap.test('PERF-06: Message processing time - Large message handling', async (tools) => {
  const done = tools.defer();
  const largeSizes = [100000, 250000, 500000]; // 100KB, 250KB, 500KB
  const results: Array<{ size: number; time: number; rate: number }> = [];

  try {
    const socket = net.createConnection({
      host: 'localhost',
      port: TEST_PORT,
      timeout: 60000 // Longer timeout for large messages
    });

    await new Promise<void>((resolve, reject) => {
      socket.once('connect', resolve);
      socket.once('error', reject);
    });

    // Read greeting
    await waitForResponse(socket, '220');

    // Send EHLO
    socket.write('EHLO testhost-large\r\n');
    await waitForResponse(socket, '250');

    console.log('\nTesting large message processing...\n');

    for (let i = 0; i < largeSizes.length; i++) {
      const messageSize = largeSizes[i];
      
      const messageStart = Date.now();

      // Send MAIL FROM
      socket.write(`MAIL FROM:<largesender${i}@example.com>\r\n`);
      await waitForResponse(socket, '250');

      // Send RCPT TO
      socket.write(`RCPT TO:<largerecipient${i}@example.com>\r\n`);
      await waitForResponse(socket, '250');

      // Send DATA
      socket.write('DATA\r\n');
      await waitForResponse(socket, '354');

      // Send large email content in chunks to avoid buffer issues
      socket.write(`From: largesender${i}@example.com\r\n`);
      socket.write(`To: largerecipient${i}@example.com\r\n`);
      socket.write(`Subject: Large Message Test ${i} (${messageSize} bytes)\r\n\r\n`);

      // Send content in 10KB chunks
      const chunkSize = 10000;
      let remaining = messageSize;
      while (remaining > 0) {
        const currentChunk = Math.min(remaining, chunkSize);
        socket.write('B'.repeat(currentChunk));
        remaining -= currentChunk;
        
        // Small delay to avoid overwhelming buffers
        if (remaining > 0) {
          await new Promise(resolve => setTimeout(resolve, 10));
        }
      }

      socket.write('\r\n.\r\n');
      
      const response = await waitForResponse(socket, '250', 30000);
      expect(response).toInclude('250');

      const messageProcessingTime = Date.now() - messageStart;
      const processingRateMBps = (messageSize / (1024 * 1024)) / (messageProcessingTime / 1000);
      
      results.push({
        size: messageSize,
        time: messageProcessingTime,
        rate: processingRateMBps
      });

      console.log(`${(messageSize/1024).toFixed(0)}KB: ${messageProcessingTime}ms (${processingRateMBps.toFixed(2)} MB/s)`);

      // Send RSET
      socket.write('RSET\r\n');
      await waitForResponse(socket, '250');

      // Delay between large tests
      await new Promise(resolve => setTimeout(resolve, 500));
    }

    const avgRate = results.reduce((sum, r) => sum + r.rate, 0) / results.length;
    console.log(`\nAverage large message rate: ${avgRate.toFixed(2)} MB/s`);

    socket.write('QUIT\r\n');
    socket.end();

    // Test passes if we can process at least 0.5 MB/s
    expect(avgRate).toBeGreaterThan(0.5);
    done.resolve();
  } catch (error) {
    done.reject(error);
  }
});

tap.test('cleanup server', async () => {
  await stopTestServer(testServer);
});

export default tap.start();