import { tap, expect } from '@git.zone/tstest/tapbundle';
import { startTestServer, stopTestServer, type ITestServer } from '../../helpers/server.loader.js';
import { createSmtpClient } from '../../../ts/mail/delivery/smtpclient/index.js';
import { Email } from '../../../ts/mail/core/classes.email.js';
import * as net from 'net';

let testServer: ITestServer;

tap.test('setup - start SMTP server for connection pool tests', async () => {
  testServer = await startTestServer({
    port: 2583,
    tlsEnabled: false,
    authRequired: false
  });
  
  expect(testServer.port).toEqual(2583);
});

tap.test('CERR-09: Connection pool with concurrent sends', async () => {
  // Test basic connection pooling functionality
  const pooledClient = await createSmtpClient({
    host: testServer.hostname,
    port: testServer.port,
    secure: false,
    pool: true,
    maxConnections: 2,
    connectionTimeout: 5000
  });

  console.log('Testing connection pool with concurrent sends...');

  // Send multiple messages concurrently
  const emails = [
    new Email({
      from: 'sender@example.com',
      to: 'recipient1@example.com',
      subject: 'Pool test 1',
      text: 'Testing connection pool'
    }),
    new Email({
      from: 'sender@example.com',
      to: 'recipient2@example.com',
      subject: 'Pool test 2',
      text: 'Testing connection pool'
    }),
    new Email({
      from: 'sender@example.com',
      to: 'recipient3@example.com',
      subject: 'Pool test 3',
      text: 'Testing connection pool'
    })
  ];

  const results = await Promise.all(
    emails.map(email => pooledClient.sendMail(email))
  );

  const successful = results.filter(r => r.success).length;
  
  console.log(`✅ Sent ${successful} messages using connection pool`);
  expect(successful).toBeGreaterThan(0);

  await pooledClient.close();
});

tap.test('CERR-09: Connection pool with server limit', async () => {
  // Create server that limits concurrent connections
  let activeConnections = 0;
  const maxServerConnections = 1;
  
  const limitedServer = net.createServer((socket) => {
    activeConnections++;
    
    if (activeConnections > maxServerConnections) {
      socket.write('421 4.7.0 Too many connections\r\n');
      socket.end();
      activeConnections--;
      return;
    }
    
    socket.write('220 Limited Server\r\n');
    
    let buffer = '';

    socket.on('data', (data) => {
      buffer += data.toString();
      
      let lines = buffer.split('\r\n');
      buffer = lines.pop() || '';
      
      for (const line of lines) {
        const command = line.trim();
        if (!command) continue;
        
        if (command.startsWith('EHLO')) {
          socket.write('250 OK\r\n');
        } else if (command === 'QUIT') {
          socket.write('221 Bye\r\n');
          socket.end();
        } else {
          socket.write('250 OK\r\n');
        }
      }
    });
    
    socket.on('close', () => {
      activeConnections--;
    });
  });

  await new Promise<void>((resolve) => {
    limitedServer.listen(2584, () => resolve());
  });

  const pooledClient = await createSmtpClient({
    host: '127.0.0.1',
    port: 2584,
    secure: false,
    pool: true,
    maxConnections: 3, // Client wants 3 but server only allows 1
    connectionTimeout: 5000
  });

  // Try concurrent connections
  const results = await Promise.all([
    pooledClient.verify(),
    pooledClient.verify(),
    pooledClient.verify()
  ]);

  const successful = results.filter(r => r === true).length;
  
  console.log(`✅ ${successful} connections succeeded with server limit`);
  expect(successful).toBeGreaterThan(0);

  await pooledClient.close();
  await new Promise<void>((resolve) => {
    limitedServer.close(() => resolve());
  });
});

tap.test('CERR-09: Connection pool recovery after error', async () => {
  // Create server that fails sometimes
  let requestCount = 0;
  
  const flakyServer = net.createServer((socket) => {
    requestCount++;
    
    // Fail every 3rd connection
    if (requestCount % 3 === 0) {
      socket.destroy();
      return;
    }
    
    socket.write('220 Flaky Server\r\n');
    
    let buffer = '';
    let inData = false;

    socket.on('data', (data) => {
      buffer += data.toString();
      
      let lines = buffer.split('\r\n');
      buffer = lines.pop() || '';
      
      for (const line of lines) {
        const command = line.trim();
        if (!command) continue;
        
        if (inData) {
          if (command === '.') {
            inData = false;
            socket.write('250 OK\r\n');
          }
          continue;
        }
        
        if (command.startsWith('EHLO')) {
          socket.write('250 OK\r\n');
        } else if (command.startsWith('MAIL FROM')) {
          socket.write('250 OK\r\n');
        } else if (command.startsWith('RCPT TO')) {
          socket.write('250 OK\r\n');
        } else if (command === 'DATA') {
          socket.write('354 Send data\r\n');
          inData = true;
        } else if (command === 'QUIT') {
          socket.write('221 Bye\r\n');
          socket.end();
        }
      }
    });
  });

  await new Promise<void>((resolve) => {
    flakyServer.listen(2585, () => resolve());
  });

  const pooledClient = await createSmtpClient({
    host: '127.0.0.1',
    port: 2585,
    secure: false,
    pool: true,
    maxConnections: 2,
    connectionTimeout: 5000
  });

  // Send multiple messages to test recovery
  const results = [];
  for (let i = 0; i < 5; i++) {
    const email = new Email({
      from: 'sender@example.com',
      to: 'recipient@example.com',
      subject: `Recovery test ${i}`,
      text: 'Testing pool recovery'
    });
    
    const result = await pooledClient.sendMail(email);
    results.push(result.success);
    console.log(`Message ${i}: ${result.success ? 'Success' : 'Failed'}`);
  }

  const successful = results.filter(r => r === true).length;
  
  console.log(`✅ Pool recovered from errors: ${successful}/5 succeeded`);
  expect(successful).toBeGreaterThan(2);

  await pooledClient.close();
  await new Promise<void>((resolve) => {
    flakyServer.close(() => resolve());
  });
});

tap.test('CERR-09: Connection pool timeout handling', async () => {
  // Create very slow server
  const slowServer = net.createServer((socket) => {
    // Wait 2 seconds before sending greeting
    setTimeout(() => {
      socket.write('220 Very Slow Server\r\n');
    }, 2000);
    
    socket.on('data', () => {
      // Don't respond to any commands
    });
  });

  await new Promise<void>((resolve) => {
    slowServer.listen(2586, () => resolve());
  });

  const pooledClient = await createSmtpClient({
    host: '127.0.0.1',
    port: 2586,
    secure: false,
    pool: true,
    connectionTimeout: 1000 // 1 second timeout
  });

  const result = await pooledClient.verify();
  
  expect(result).toBeFalse();
  console.log('✅ Connection pool handled timeout correctly');

  await pooledClient.close();
  await new Promise<void>((resolve) => {
    slowServer.close(() => resolve());
  });
});

tap.test('CERR-09: Normal pooled operation', async () => {
  // Test successful pooled operation
  const pooledClient = await createSmtpClient({
    host: testServer.hostname,
    port: testServer.port,
    secure: false,
    pool: true,
    maxConnections: 2
  });

  const email = new Email({
    from: 'sender@example.com',
    to: 'recipient@example.com',
    subject: 'Pool Test',
    text: 'Testing normal pooled operation'
  });

  const result = await pooledClient.sendMail(email);
  
  expect(result.success).toBeTrue();
  console.log('✅ Normal pooled email sent successfully');

  await pooledClient.close();
});

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

export default tap.start();