import { tap, expect } from '@git.zone/tstest/tapbundle';
import { startTestServer, stopTestServer, type ITestServer } from '../../helpers/server.loader.js';
import { createSmtpClient, createPooledSmtpClient } 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;

// Helper function to get memory usage
const getMemoryUsage = () => {
  if (process.memoryUsage) {
    const usage = process.memoryUsage();
    return {
      heapUsed: usage.heapUsed,
      heapTotal: usage.heapTotal,
      external: usage.external,
      rss: usage.rss
    };
  }
  return null;
};

// Helper function to format bytes
const formatBytes = (bytes: number) => {
  if (bytes < 1024) return `${bytes} B`;
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
};

tap.test('setup - start SMTP server for memory tests', async () => {
  testServer = await startTestServer({
    port: 0,
    enableStarttls: false,
    authRequired: false
  });
  
  expect(testServer.port).toBeGreaterThan(0);
});

tap.test('CPERF-03: Memory usage during connection lifecycle', async (tools) => {
  tools.timeout(30000);
  
  const memoryBefore = getMemoryUsage();
  console.log('Initial memory usage:', {
    heapUsed: formatBytes(memoryBefore.heapUsed),
    heapTotal: formatBytes(memoryBefore.heapTotal),
    rss: formatBytes(memoryBefore.rss)
  });
  
  // Create and close multiple connections
  const connectionCount = 10;
  
  for (let i = 0; i < connectionCount; i++) {
    const client = await createSmtpClient({
      host: testServer.hostname,
      port: testServer.port,
      secure: false,
      debug: false
    });
    
    // Send a test email
    const email = new Email({
      from: 'sender@example.com',
      to: ['recipient@example.com'],
      subject: `Memory test ${i + 1}`,
      text: 'Testing memory usage'
    });
    
    await client.sendMail(email);
    await client.close();
    
    // Small delay between connections
    await new Promise(resolve => setTimeout(resolve, 100));
  }
  
  // Force garbage collection if available
  if (global.gc) {
    global.gc();
    await new Promise(resolve => setTimeout(resolve, 100));
  }
  
  const memoryAfter = getMemoryUsage();
  const memoryIncrease = memoryAfter.heapUsed - memoryBefore.heapUsed;
  
  console.log(`Memory after ${connectionCount} connections:`, {
    heapUsed: formatBytes(memoryAfter.heapUsed),
    heapTotal: formatBytes(memoryAfter.heapTotal),
    rss: formatBytes(memoryAfter.rss)
  });
  console.log(`Memory increase: ${formatBytes(memoryIncrease)}`);
  console.log(`Average per connection: ${formatBytes(memoryIncrease / connectionCount)}`);
  
  // Memory increase should be reasonable
  expect(memoryIncrease / connectionCount).toBeLessThan(1024 * 1024); // Less than 1MB per connection
});

tap.test('CPERF-03: Memory usage with large messages', async (tools) => {
  tools.timeout(30000);
  
  const client = await createSmtpClient({
    host: testServer.hostname,
    port: testServer.port,
    secure: false,
    debug: false
  });
  
  const memoryBefore = getMemoryUsage();
  console.log('Memory before large messages:', {
    heapUsed: formatBytes(memoryBefore.heapUsed)
  });
  
  // Send messages of increasing size
  const sizes = [1024, 10240, 102400]; // 1KB, 10KB, 100KB
  
  for (const size of sizes) {
    const email = new Email({
      from: 'sender@example.com',
      to: ['recipient@example.com'],
      subject: `Large message test (${formatBytes(size)})`,
      text: 'x'.repeat(size)
    });
    
    await client.sendMail(email);
    
    const memoryAfter = getMemoryUsage();
    console.log(`Memory after ${formatBytes(size)} message:`, {
      heapUsed: formatBytes(memoryAfter.heapUsed),
      increase: formatBytes(memoryAfter.heapUsed - memoryBefore.heapUsed)
    });
    
    // Small delay
    await new Promise(resolve => setTimeout(resolve, 200));
  }
  
  await client.close();
  
  const memoryFinal = getMemoryUsage();
  const totalIncrease = memoryFinal.heapUsed - memoryBefore.heapUsed;
  
  console.log(`Total memory increase: ${formatBytes(totalIncrease)}`);
  
  // Memory should not grow excessively
  expect(totalIncrease).toBeLessThan(10 * 1024 * 1024); // Less than 10MB total
});

tap.test('CPERF-03: Memory usage with connection pooling', async (tools) => {
  tools.timeout(30000);
  
  const memoryBefore = getMemoryUsage();
  console.log('Memory before pooling test:', {
    heapUsed: formatBytes(memoryBefore.heapUsed)
  });
  
  const pooledClient = await createPooledSmtpClient({
    host: testServer.hostname,
    port: testServer.port,
    secure: false,
    maxConnections: 3,
    debug: false
  });
  
  // Send multiple emails through the pool
  const emailCount = 15;
  const emails = Array(emailCount).fill(null).map((_, i) => 
    new Email({
      from: 'sender@example.com',
      to: [`recipient${i}@example.com`],
      subject: `Pooled memory test ${i + 1}`,
      text: 'Testing memory with connection pooling'
    })
  );
  
  // Send in batches
  for (let i = 0; i < emails.length; i += 3) {
    const batch = emails.slice(i, i + 3);
    await Promise.all(batch.map(email => 
      pooledClient.sendMail(email).catch(err => console.log('Send error:', err.message))
    ));
    
    // Check memory after each batch
    const memoryNow = getMemoryUsage();
    console.log(`Memory after batch ${Math.floor(i/3) + 1}:`, {
      heapUsed: formatBytes(memoryNow.heapUsed),
      increase: formatBytes(memoryNow.heapUsed - memoryBefore.heapUsed)
    });
    
    await new Promise(resolve => setTimeout(resolve, 100));
  }
  
  await pooledClient.close();
  
  const memoryFinal = getMemoryUsage();
  const totalIncrease = memoryFinal.heapUsed - memoryBefore.heapUsed;
  
  console.log(`Total memory increase with pooling: ${formatBytes(totalIncrease)}`);
  console.log(`Average per email: ${formatBytes(totalIncrease / emailCount)}`);
  
  // Pooling should be memory efficient
  expect(totalIncrease / emailCount).toBeLessThan(500 * 1024); // Less than 500KB per email
});

tap.test('CPERF-03: Memory cleanup after errors', async (tools) => {
  tools.timeout(30000);
  
  const memoryBefore = getMemoryUsage();
  console.log('Memory before error test:', {
    heapUsed: formatBytes(memoryBefore.heapUsed)
  });
  
  // Try to send emails that might fail
  const errorCount = 5;
  
  for (let i = 0; i < errorCount; i++) {
    try {
      const client = await createSmtpClient({
        host: testServer.hostname,
        port: testServer.port,
        secure: false,
        connectionTimeout: 1000, // Short timeout
        debug: false
      });
      
      // Create a large email that might cause issues
      const email = new Email({
        from: 'sender@example.com',
        to: ['recipient@example.com'],
        subject: `Error test ${i + 1}`,
        text: 'x'.repeat(100000), // 100KB
        attachments: [{
          filename: 'test.txt',
          content: Buffer.alloc(50000).toString('base64'), // 50KB attachment
          encoding: 'base64'
        }]
      });
      
      await client.sendMail(email);
      await client.close();
    } catch (error) {
      console.log(`Error ${i + 1} handled: ${error.message}`);
    }
    
    await new Promise(resolve => setTimeout(resolve, 100));
  }
  
  // Force garbage collection if available
  if (global.gc) {
    global.gc();
    await new Promise(resolve => setTimeout(resolve, 100));
  }
  
  const memoryAfter = getMemoryUsage();
  const memoryIncrease = memoryAfter.heapUsed - memoryBefore.heapUsed;
  
  console.log(`Memory after ${errorCount} error scenarios:`, {
    heapUsed: formatBytes(memoryAfter.heapUsed),
    increase: formatBytes(memoryIncrease)
  });
  
  // Memory should be properly cleaned up after errors
  expect(memoryIncrease).toBeLessThan(5 * 1024 * 1024); // Less than 5MB increase
});

tap.test('CPERF-03: Long-running memory stability', async (tools) => {
  tools.timeout(60000);
  
  const client = await createSmtpClient({
    host: testServer.hostname,
    port: testServer.port,
    secure: false,
    debug: false
  });
  
  const memorySnapshots = [];
  const duration = 10000; // 10 seconds
  const interval = 2000; // Check every 2 seconds
  const startTime = Date.now();
  
  console.log('Testing memory stability over time...');
  
  let emailsSent = 0;
  
  while (Date.now() - startTime < duration) {
    // Send an email
    const email = new Email({
      from: 'sender@example.com',
      to: ['recipient@example.com'],
      subject: `Stability test ${++emailsSent}`,
      text: `Testing memory stability at ${new Date().toISOString()}`
    });
    
    try {
      await client.sendMail(email);
    } catch (error) {
      console.log('Send error:', error.message);
    }
    
    // Take memory snapshot
    const memory = getMemoryUsage();
    const elapsed = Date.now() - startTime;
    memorySnapshots.push({
      time: elapsed,
      heapUsed: memory.heapUsed
    });
    
    console.log(`[${elapsed}ms] Heap: ${formatBytes(memory.heapUsed)}, Emails sent: ${emailsSent}`);
    
    await new Promise(resolve => setTimeout(resolve, interval));
  }
  
  await client.close();
  
  // Analyze memory growth
  const firstSnapshot = memorySnapshots[0];
  const lastSnapshot = memorySnapshots[memorySnapshots.length - 1];
  const memoryGrowth = lastSnapshot.heapUsed - firstSnapshot.heapUsed;
  const growthRate = memoryGrowth / (lastSnapshot.time / 1000); // bytes per second
  
  console.log(`\nMemory stability results:`);
  console.log(`  Duration: ${lastSnapshot.time}ms`);
  console.log(`  Emails sent: ${emailsSent}`);
  console.log(`  Memory growth: ${formatBytes(memoryGrowth)}`);
  console.log(`  Growth rate: ${formatBytes(growthRate)}/second`);
  
  // Memory growth should be minimal over time
  expect(growthRate).toBeLessThan(150 * 1024); // Less than 150KB/second growth
});

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

export default tap.start();