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 type { SmtpClient } from '../../../ts/mail/delivery/smtpclient/smtp-client.js';
import { Email } from '../../../ts/mail/core/classes.email.js';

let testServer: ITestServer;
let smtpClient: SmtpClient;

tap.test('setup test SMTP server', async () => {
  testServer = await startTestServer({
    port: 2549,
    tlsEnabled: false,
    authRequired: false
  });
  expect(testServer).toBeTruthy();
  expect(testServer.port).toBeGreaterThan(0);
});

tap.test('CCMD-09: Connection keepalive test', async () => {
  // NOOP is used internally for keepalive - test that connections remain active
  smtpClient = createSmtpClient({
    host: testServer.hostname,
    port: testServer.port,
    secure: false,
    connectionTimeout: 10000,
    greetingTimeout: 5000,
    socketTimeout: 10000
  });

  // Send an initial email to establish connection
  const email1 = new Email({
    from: 'sender@example.com',
    to: ['recipient@example.com'],
    subject: 'Initial connection test',
    text: 'Testing connection establishment'
  });

  await smtpClient.sendMail(email1);
  console.log('First email sent successfully');

  // Wait 5 seconds (connection should stay alive with internal NOOP)
  await new Promise(resolve => setTimeout(resolve, 5000));

  // Send another email on the same connection
  const email2 = new Email({
    from: 'sender@example.com',
    to: ['recipient@example.com'],
    subject: 'Keepalive test',
    text: 'Testing connection after delay'
  });

  await smtpClient.sendMail(email2);
  console.log('Second email sent successfully after 5 second delay');
});

tap.test('CCMD-09: Multiple emails in sequence', async () => {
  // Test that client can handle multiple emails without issues
  // Internal NOOP commands may be used between transactions
  
  const emails = [];
  for (let i = 0; i < 5; i++) {
    emails.push(new Email({
      from: 'sender@example.com',
      to: [`recipient${i}@example.com`],
      subject: `Sequential email ${i + 1}`,
      text: `This is email number ${i + 1}`
    }));
  }

  console.log('Sending 5 emails in sequence...');
  
  for (let i = 0; i < emails.length; i++) {
    await smtpClient.sendMail(emails[i]);
    console.log(`Email ${i + 1} sent successfully`);
    
    // Small delay between emails
    await new Promise(resolve => setTimeout(resolve, 500));
  }

  console.log('All emails sent successfully');
});

tap.test('CCMD-09: Rapid email sending', async () => {
  // Test rapid email sending without delays
  // Internal connection management should handle this properly
  
  const emailCount = 10;
  const emails = [];
  
  for (let i = 0; i < emailCount; i++) {
    emails.push(new Email({
      from: 'sender@example.com',
      to: ['recipient@example.com'],
      subject: `Rapid email ${i + 1}`,
      text: `Rapid fire email number ${i + 1}`
    }));
  }

  console.log(`Sending ${emailCount} emails rapidly...`);
  const startTime = Date.now();
  
  // Send all emails as fast as possible
  for (const email of emails) {
    await smtpClient.sendMail(email);
  }
  
  const elapsed = Date.now() - startTime;
  console.log(`All ${emailCount} emails sent in ${elapsed}ms`);
  console.log(`Average: ${(elapsed / emailCount).toFixed(2)}ms per email`);
});

tap.test('CCMD-09: Long-lived connection test', async () => {
  // Test that connection stays alive over extended period
  // SmtpClient should use internal keepalive mechanisms
  
  console.log('Testing connection over 10 seconds with periodic emails...');
  
  const testDuration = 10000;
  const emailInterval = 2500;
  const iterations = Math.floor(testDuration / emailInterval);
  
  for (let i = 0; i < iterations; i++) {
    const email = new Email({
      from: 'sender@example.com',
      to: ['recipient@example.com'],
      subject: `Keepalive test ${i + 1}`,
      text: `Testing connection keepalive - email ${i + 1}`
    });
    
    const startTime = Date.now();
    await smtpClient.sendMail(email);
    const elapsed = Date.now() - startTime;
    
    console.log(`Email ${i + 1} sent in ${elapsed}ms`);
    
    if (i < iterations - 1) {
      await new Promise(resolve => setTimeout(resolve, emailInterval));
    }
  }
  
  console.log('Connection remained stable over 10 seconds');
});

tap.test('CCMD-09: Connection pooling behavior', async () => {
  // Test connection pooling with different email patterns
  // Internal NOOP may be used to maintain pool connections
  
  const testPatterns = [
    { count: 3, delay: 0, desc: 'Burst of 3 emails' },
    { count: 2, delay: 1000, desc: '2 emails with 1s delay' },
    { count: 1, delay: 3000, desc: '1 email after 3s delay' }
  ];
  
  for (const pattern of testPatterns) {
    console.log(`\nTesting: ${pattern.desc}`);
    
    if (pattern.delay > 0) {
      await new Promise(resolve => setTimeout(resolve, pattern.delay));
    }
    
    for (let i = 0; i < pattern.count; i++) {
      const email = new Email({
        from: 'sender@example.com',
        to: ['recipient@example.com'],
        subject: `${pattern.desc} - Email ${i + 1}`,
        text: 'Testing connection pooling behavior'
      });
      
      await smtpClient.sendMail(email);
    }
    
    console.log(`Completed: ${pattern.desc}`);
  }
});

tap.test('CCMD-09: Email sending performance', async () => {
  // Measure email sending performance
  // Connection management (including internal NOOP) affects timing
  
  const measurements = 20;
  const times: number[] = [];
  
  console.log(`Measuring performance over ${measurements} emails...`);
  
  for (let i = 0; i < measurements; i++) {
    const email = new Email({
      from: 'sender@example.com',
      to: ['recipient@example.com'],
      subject: `Performance test ${i + 1}`,
      text: 'Measuring email sending performance'
    });
    
    const startTime = Date.now();
    await smtpClient.sendMail(email);
    const elapsed = Date.now() - startTime;
    times.push(elapsed);
  }
  
  // Calculate statistics
  const avgTime = times.reduce((a, b) => a + b, 0) / times.length;
  const minTime = Math.min(...times);
  const maxTime = Math.max(...times);
  
  // Calculate standard deviation
  const variance = times.reduce((sum, time) => sum + Math.pow(time - avgTime, 2), 0) / times.length;
  const stdDev = Math.sqrt(variance);
  
  console.log(`\nPerformance analysis (${measurements} emails):`);
  console.log(`  Average: ${avgTime.toFixed(2)}ms`);
  console.log(`  Min: ${minTime}ms`);
  console.log(`  Max: ${maxTime}ms`);
  console.log(`  Std Dev: ${stdDev.toFixed(2)}ms`);
  
  // First email might be slower due to connection establishment
  const avgWithoutFirst = times.slice(1).reduce((a, b) => a + b, 0) / (times.length - 1);
  console.log(`  Average (excl. first): ${avgWithoutFirst.toFixed(2)}ms`);
  
  // Performance should be reasonable
  expect(avgTime).toBeLessThan(200);
});

tap.test('CCMD-09: Email with NOOP in content', async () => {
  // Test that NOOP as email content doesn't affect delivery
  const email = new Email({
    from: 'sender@example.com',
    to: ['recipient@example.com'],
    subject: 'Email containing NOOP',
    text: `This email contains SMTP commands as content:

NOOP
HELO test
MAIL FROM:<test@example.com>

These should be treated as plain text, not commands.
The word NOOP appears multiple times in this email.

NOOP is used internally by SMTP for keepalive.`
  });
  
  await smtpClient.sendMail(email);
  console.log('Email with NOOP content sent successfully');
  
  // Send another email to verify connection still works
  const email2 = new Email({
    from: 'sender@example.com',
    to: ['recipient@example.com'],
    subject: 'Follow-up email',
    text: 'Verifying connection still works after NOOP content'
  });
  
  await smtpClient.sendMail(email2);
  console.log('Follow-up email sent successfully');
});

tap.test('CCMD-09: Concurrent email sending', async () => {
  // Test concurrent email sending
  // Connection pooling and internal management should handle this
  
  const concurrentCount = 5;
  const emails = [];
  
  for (let i = 0; i < concurrentCount; i++) {
    emails.push(new Email({
      from: 'sender@example.com',
      to: [`recipient${i}@example.com`],
      subject: `Concurrent email ${i + 1}`,
      text: `Testing concurrent email sending - message ${i + 1}`
    }));
  }
  
  console.log(`Sending ${concurrentCount} emails concurrently...`);
  const startTime = Date.now();
  
  // Send all emails concurrently
  try {
    await Promise.all(emails.map(email => smtpClient.sendMail(email)));
    const elapsed = Date.now() - startTime;
    console.log(`All ${concurrentCount} emails sent concurrently in ${elapsed}ms`);
  } catch (error) {
    // Concurrent sending might not be supported - that's OK
    console.log('Concurrent sending not supported, falling back to sequential');
    for (const email of emails) {
      await smtpClient.sendMail(email);
    }
  }
});

tap.test('CCMD-09: Connection recovery test', async () => {
  // Test connection recovery and error handling
  // SmtpClient should handle connection issues gracefully
  
  // Create a new client with shorter timeouts for testing
  const testClient = createSmtpClient({
    host: testServer.hostname,
    port: testServer.port,
    secure: false,
    connectionTimeout: 3000,
    socketTimeout: 3000
  });
  
  // Send initial email
  const email1 = new Email({
    from: 'sender@example.com',
    to: ['recipient@example.com'],
    subject: 'Connection test 1',
    text: 'Testing initial connection'
  });
  
  await testClient.sendMail(email1);
  console.log('Initial email sent');
  
  // Simulate long delay that might timeout connection
  console.log('Waiting 5 seconds to test connection recovery...');
  await new Promise(resolve => setTimeout(resolve, 5000));
  
  // Try to send another email - client should recover if needed
  const email2 = new Email({
    from: 'sender@example.com',
    to: ['recipient@example.com'],
    subject: 'Connection test 2',
    text: 'Testing connection recovery'
  });
  
  try {
    await testClient.sendMail(email2);
    console.log('Email sent successfully after delay - connection recovered');
  } catch (error) {
    console.log('Connection recovery failed (this might be expected):', error.message);
  }
});

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

export default tap.start();