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;

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

tap.test('PERF-03: CPU utilization - Load test', async (tools) => {
  const done = tools.defer();
  const monitoringDuration = 3000; // 3 seconds (reduced from 5)
  const connectionCount = 5; // Reduced from 10
  const connections: net.Socket[] = [];

  // Add timeout to prevent hanging
  const testTimeout = setTimeout(() => {
    console.log('CPU test timeout reached, cleaning up...');
    for (const socket of connections) {
      if (!socket.destroyed) socket.destroy();
    }
    done.resolve();
  }, 30000); // 30 second timeout

  try {
    // Record initial CPU usage
    const initialCpuUsage = process.cpuUsage();
    const startTime = Date.now();

    // Create multiple connections and send emails
    console.log(`Creating ${connectionCount} connections for CPU load test...`);

    for (let i = 0; i < connectionCount; i++) {
      const socket = net.createConnection({
        host: 'localhost',
        port: TEST_PORT,
        timeout: 30000
      });

      connections.push(socket);

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

      // Process greeting
      await new Promise<void>((resolve) => {
        let greeting = '';
        const handleGreeting = (chunk: Buffer) => {
          greeting += chunk.toString();
          if (greeting.includes('220') && greeting.includes('\r\n')) {
            socket.removeListener('data', handleGreeting);
            resolve();
          }
        };
        socket.on('data', handleGreeting);
      });

      // Send EHLO
      socket.write(`EHLO testhost-cpu-${i}\r\n`);
      
      await new Promise<void>((resolve) => {
        let data = '';
        const handleData = (chunk: Buffer) => {
          data += chunk.toString();
          if (data.includes('250 ')) {
            socket.removeListener('data', handleData);
            resolve();
          }
        };
        socket.on('data', handleData);
      });

      // Keep connection active, don't send full transaction to avoid timeout
    }

    // Keep connections active during monitoring period
    console.log(`Monitoring CPU usage for ${monitoringDuration}ms...`);
    
    // Send periodic NOOP commands to keep connections active
    const noopInterval = setInterval(() => {
      connections.forEach((socket, idx) => {
        if (socket.writable) {
          socket.write('NOOP\r\n');
        }
      });
    }, 1000);

    await new Promise(resolve => setTimeout(resolve, monitoringDuration));
    clearInterval(noopInterval);

    // Calculate CPU usage
    const finalCpuUsage = process.cpuUsage(initialCpuUsage);
    const totalCpuTimeMs = (finalCpuUsage.user + finalCpuUsage.system) / 1000;
    const elapsedTime = Date.now() - startTime;
    const cpuUtilizationPercent = (totalCpuTimeMs / elapsedTime) * 100;

    console.log(`\nCPU Utilization Results:`);
    console.log(`Total CPU time: ${totalCpuTimeMs.toFixed(0)}ms`);
    console.log(`Elapsed time: ${elapsedTime}ms`);
    console.log(`CPU utilization: ${cpuUtilizationPercent.toFixed(1)}%`);
    console.log(`User CPU: ${(finalCpuUsage.user / 1000).toFixed(0)}ms`);
    console.log(`System CPU: ${(finalCpuUsage.system / 1000).toFixed(0)}ms`);

    // Clean up connections
    for (const socket of connections) {
      if (socket.writable) {
        socket.write('QUIT\r\n');
        socket.end();
      }
    }

    // Test passes if CPU usage is reasonable (less than 80%)
    expect(cpuUtilizationPercent).toBeLessThan(80);
    clearTimeout(testTimeout);
    done.resolve();
  } catch (error) {
    // Clean up on error
    connections.forEach(socket => socket.destroy());
    clearTimeout(testTimeout);
    done.reject(error);
  }
});

tap.test('PERF-03: CPU utilization - Stress test', async (tools) => {
  const done = tools.defer();
  const testDuration = 2000; // 2 seconds (reduced from 3)
  let requestCount = 0;

  // Add timeout to prevent hanging
  const testTimeout = setTimeout(() => {
    console.log('Stress test timeout reached, completing...');
    done.resolve();
  }, 15000); // 15 second timeout

  try {
    const initialCpuUsage = process.cpuUsage();
    const startTime = Date.now();

    console.log(`\nRunning CPU stress test for ${testDuration}ms...`);

    // Create a single connection for rapid requests
    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 new Promise<void>((resolve) => {
      let greeting = '';
      const handleGreeting = (chunk: Buffer) => {
        greeting += chunk.toString();
        if (greeting.includes('220') && greeting.includes('\r\n')) {
          socket.removeListener('data', handleGreeting);
          resolve();
        }
      };
      socket.on('data', handleGreeting);
    });

    // Send EHLO
    socket.write('EHLO stresstest\r\n');
    
    await new Promise<void>((resolve) => {
      let data = '';
      const handleData = (chunk: Buffer) => {
        data += chunk.toString();
        if (data.includes('250 ')) {
          socket.removeListener('data', handleData);
          resolve();
        }
      };
      socket.on('data', handleData);
    });

    // Rapid command loop
    const endTime = Date.now() + testDuration;
    const commands = ['NOOP', 'RSET', 'VRFY test@example.com', 'HELP'];
    let commandIndex = 0;

    while (Date.now() < endTime) {
      const command = commands[commandIndex % commands.length];
      socket.write(`${command}\r\n`);
      
      await new Promise<void>((resolve) => {
        socket.once('data', () => {
          requestCount++;
          resolve();
        });
      });

      commandIndex++;
      
      // Small delay to avoid overwhelming
      if (requestCount % 20 === 0) {
        await new Promise(resolve => setTimeout(resolve, 10));
      }
    }

    // Calculate final CPU usage
    const finalCpuUsage = process.cpuUsage(initialCpuUsage);
    const totalCpuTimeMs = (finalCpuUsage.user + finalCpuUsage.system) / 1000;
    const elapsedTime = Date.now() - startTime;
    const cpuUtilizationPercent = (totalCpuTimeMs / elapsedTime) * 100;
    const requestsPerSecond = (requestCount / elapsedTime) * 1000;

    console.log(`\nStress Test Results:`);
    console.log(`Requests processed: ${requestCount}`);
    console.log(`Requests per second: ${requestsPerSecond.toFixed(1)}`);
    console.log(`CPU utilization: ${cpuUtilizationPercent.toFixed(1)}%`);
    console.log(`CPU time per request: ${(totalCpuTimeMs / requestCount).toFixed(2)}ms`);

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

    // Test passes if CPU usage per request is reasonable
    const cpuPerRequest = totalCpuTimeMs / requestCount;
    expect(cpuPerRequest).toBeLessThan(10); // Less than 10ms CPU per request
    clearTimeout(testTimeout);
    done.resolve();
  } catch (error) {
    clearTimeout(testTimeout);
    done.reject(error);
  }
});

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

export default tap.start();