import { tap, expect } from '@git.zone/tstest/tapbundle';
import * as net from 'net';
import { createTestSmtpClient } from '../../helpers/smtp.client.js';
import { Email } from '../../../ts/mail/core/classes.email.js';

tap.test('CREL-04: Basic Connection Recovery from Server Issues', async () => {
  console.log('\n💥 Testing SMTP Client Connection Recovery');
  console.log('=' .repeat(60));
  console.log('\n🔌 Testing recovery from connection drops...');
  
  let connectionCount = 0;
  let dropConnections = false;
  
  // Create test server that can simulate connection drops
  const server = net.createServer(socket => {
    connectionCount++;
    console.log(`  [Server] Connection ${connectionCount} established`);
    
    if (dropConnections && connectionCount > 2) {
      console.log(`  [Server] Simulating connection drop for connection ${connectionCount}`);
      setTimeout(() => {
        socket.destroy();
      }, 100);
      return;
    }
    
    socket.write('220 localhost SMTP Test Server\r\n');
    
    socket.on('data', (data) => {
      const lines = data.toString().split('\r\n');
      
      lines.forEach(line => {
        if (line.startsWith('EHLO') || line.startsWith('HELO')) {
          socket.write('250-localhost\r\n');
          socket.write('250 SIZE 10485760\r\n');
        } else if (line.startsWith('MAIL FROM:')) {
          socket.write('250 OK\r\n');
        } else if (line.startsWith('RCPT TO:')) {
          socket.write('250 OK\r\n');
        } else if (line === 'DATA') {
          socket.write('354 Send data\r\n');
        } else if (line === '.') {
          socket.write('250 OK Message accepted\r\n');
        } else if (line === 'QUIT') {
          socket.write('221 Bye\r\n');
          socket.end();
        }
      });
    });
  });

  await new Promise<void>((resolve) => {
    server.listen(0, '127.0.0.1', () => {
      resolve();
    });
  });

  const port = (server.address() as net.AddressInfo).port;

  try {
    console.log('  Creating SMTP client with connection recovery settings...');
    const smtpClient = createTestSmtpClient({
      host: '127.0.0.1',
      port: port,
      secure: false,
      maxConnections: 2,
      maxMessages: 50,
      connectionTimeout: 2000
    });

    const emails = [];
    for (let i = 0; i < 8; i++) {
      emails.push(new Email({
        from: 'sender@crashtest.example',
        to: [`recipient${i}@crashtest.example`],
        subject: `Connection Recovery Test ${i + 1}`,
        text: `Testing connection recovery, email ${i + 1}`
      }));
    }

    console.log('  Phase 1: Sending initial emails (connections should succeed)...');
    const results1 = [];
    for (let i = 0; i < 3; i++) {
      try {
        await smtpClient.sendMail(emails[i]);
        results1.push({ success: true, index: i });
        console.log(`  ✓ Email ${i + 1} sent successfully`);
      } catch (error) {
        results1.push({ success: false, index: i, error });
        console.log(`  ✗ Email ${i + 1} failed: ${error.message}`);
      }
    }

    console.log('  Phase 2: Enabling connection drops...');
    dropConnections = true;

    console.log('  Sending emails during connection instability...');
    const results2 = [];
    const promises = emails.slice(3).map((email, index) => {
      const actualIndex = index + 3;
      return smtpClient.sendMail(email).then(result => {
        console.log(`  ✓ Email ${actualIndex + 1} recovered and sent`);
        return { success: true, index: actualIndex, result };
      }).catch(error => {
        console.log(`  ✗ Email ${actualIndex + 1} failed permanently: ${error.message}`);
        return { success: false, index: actualIndex, error };
      });
    });

    const results2Resolved = await Promise.all(promises);
    results2.push(...results2Resolved);

    const totalSuccessful = [...results1, ...results2].filter(r => r.success).length;
    const totalFailed = [...results1, ...results2].filter(r => !r.success).length;

    console.log(`  Connection attempts: ${connectionCount}`);
    console.log(`  Emails sent successfully: ${totalSuccessful}/${emails.length}`);
    console.log(`  Failed emails: ${totalFailed}`);
    console.log(`  Recovery effectiveness: ${((totalSuccessful / emails.length) * 100).toFixed(1)}%`);

    expect(totalSuccessful).toBeGreaterThanOrEqual(3); // At least initial emails should succeed
    expect(connectionCount).toBeGreaterThanOrEqual(2); // Should have made multiple connection attempts

    smtpClient.close();
  } finally {
    server.close();
  }
});

tap.test('CREL-04: Recovery from Server Restart', async () => {
  console.log('\n💀 Testing recovery from server restart...');
  
  // Start first server instance
  let server1 = net.createServer(socket => {
    console.log('  [Server1] Connection established');
    socket.write('220 localhost SMTP Test Server\r\n');
    
    socket.on('data', (data) => {
      const lines = data.toString().split('\r\n');
      
      lines.forEach(line => {
        if (line.startsWith('EHLO') || line.startsWith('HELO')) {
          socket.write('250-localhost\r\n');
          socket.write('250 SIZE 10485760\r\n');
        } else if (line.startsWith('MAIL FROM:')) {
          socket.write('250 OK\r\n');
        } else if (line.startsWith('RCPT TO:')) {
          socket.write('250 OK\r\n');
        } else if (line === 'DATA') {
          socket.write('354 Send data\r\n');
        } else if (line === '.') {
          socket.write('250 OK Message accepted\r\n');
        } else if (line === 'QUIT') {
          socket.write('221 Bye\r\n');
          socket.end();
        }
      });
    });
  });

  await new Promise<void>((resolve) => {
    server1.listen(0, '127.0.0.1', () => {
      resolve();
    });
  });

  const port = (server1.address() as net.AddressInfo).port;

  try {
    console.log('  Creating client...');
    const smtpClient = createTestSmtpClient({
      host: '127.0.0.1',
      port: port,
      secure: false,
      maxConnections: 1,
      connectionTimeout: 3000
    });

    const emails = [];
    for (let i = 0; i < 6; i++) {
      emails.push(new Email({
        from: 'sender@serverrestart.test',
        to: [`recipient${i}@serverrestart.test`],
        subject: `Server Restart Recovery ${i + 1}`,
        text: `Testing server restart recovery, email ${i + 1}`
      }));
    }

    console.log('  Sending first batch of emails...');
    await smtpClient.sendMail(emails[0]);
    console.log('  ✓ Email 1 sent successfully');

    await smtpClient.sendMail(emails[1]);
    console.log('  ✓ Email 2 sent successfully');

    console.log('  Simulating server restart by closing server...');
    server1.close();
    await new Promise(resolve => setTimeout(resolve, 500));

    console.log('  Starting new server instance on same port...');
    const server2 = net.createServer(socket => {
      console.log('  [Server2] Connection established after restart');
      socket.write('220 localhost SMTP Test Server Restarted\r\n');
      
      socket.on('data', (data) => {
        const lines = data.toString().split('\r\n');
        
        lines.forEach(line => {
          if (line.startsWith('EHLO') || line.startsWith('HELO')) {
            socket.write('250-localhost\r\n');
            socket.write('250 SIZE 10485760\r\n');
          } else if (line.startsWith('MAIL FROM:')) {
            socket.write('250 OK\r\n');
          } else if (line.startsWith('RCPT TO:')) {
            socket.write('250 OK\r\n');
          } else if (line === 'DATA') {
            socket.write('354 Send data\r\n');
          } else if (line === '.') {
            socket.write('250 OK Message accepted\r\n');
          } else if (line === 'QUIT') {
            socket.write('221 Bye\r\n');
            socket.end();
          }
        });
      });
    });

    await new Promise<void>((resolve) => {
      server2.listen(port, '127.0.0.1', () => {
        resolve();
      });
    });

    console.log('  Sending emails after server restart...');
    const recoveryResults = [];
    
    for (let i = 2; i < emails.length; i++) {
      try {
        await smtpClient.sendMail(emails[i]);
        recoveryResults.push({ success: true, index: i });
        console.log(`  ✓ Email ${i + 1} sent after server recovery`);
      } catch (error) {
        recoveryResults.push({ success: false, index: i, error });
        console.log(`  ✗ Email ${i + 1} failed: ${error.message}`);
      }
    }

    const successfulRecovery = recoveryResults.filter(r => r.success).length;
    const totalSuccessful = 2 + successfulRecovery; // 2 from before restart + recovery

    console.log(`  Pre-restart emails: 2/2 successful`);
    console.log(`  Post-restart emails: ${successfulRecovery}/${recoveryResults.length} successful`);
    console.log(`  Overall success rate: ${((totalSuccessful / emails.length) * 100).toFixed(1)}%`);
    console.log(`  Server restart recovery: ${successfulRecovery > 0 ? 'Successful' : 'Failed'}`);

    expect(successfulRecovery).toBeGreaterThanOrEqual(1); // At least some emails should work after restart

    smtpClient.close();
    server2.close();
  } finally {
    // Ensure cleanup
    try {
      server1.close();
    } catch (e) { /* Already closed */ }
  }
});

tap.test('CREL-04: Error Recovery and State Management', async () => {
  console.log('\n⚠️ Testing error recovery and state management...');
  
  let errorInjectionEnabled = false;
  const server = net.createServer(socket => {
    socket.write('220 localhost SMTP Test Server\r\n');
    
    socket.on('data', (data) => {
      const lines = data.toString().split('\r\n');
      
      lines.forEach(line => {
        if (errorInjectionEnabled && line.startsWith('MAIL FROM')) {
          console.log('  [Server] Injecting error response');
          socket.write('550 Simulated server error\r\n');
          return;
        }
        
        if (line.startsWith('EHLO') || line.startsWith('HELO')) {
          socket.write('250-localhost\r\n');
          socket.write('250 SIZE 10485760\r\n');
        } else if (line.startsWith('MAIL FROM:')) {
          socket.write('250 OK\r\n');
        } else if (line.startsWith('RCPT TO:')) {
          socket.write('250 OK\r\n');
        } else if (line === 'DATA') {
          socket.write('354 Send data\r\n');
        } else if (line === '.') {
          socket.write('250 OK Message accepted\r\n');
        } else if (line === 'QUIT') {
          socket.write('221 Bye\r\n');
          socket.end();
        } else if (line === 'RSET') {
          socket.write('250 OK\r\n');
        }
      });
    });
  });

  await new Promise<void>((resolve) => {
    server.listen(0, '127.0.0.1', () => {
      resolve();
    });
  });

  const port = (server.address() as net.AddressInfo).port;

  try {
    console.log('  Creating client with error handling...');
    const smtpClient = createTestSmtpClient({
      host: '127.0.0.1',
      port: port,
      secure: false,
      maxConnections: 1,
      connectionTimeout: 3000
    });

    const emails = [];
    for (let i = 0; i < 6; i++) {
      emails.push(new Email({
        from: 'sender@exception.test',
        to: [`recipient${i}@exception.test`],
        subject: `Error Recovery Test ${i + 1}`,
        text: `Testing error recovery, email ${i + 1}`
      }));
    }

    console.log('  Phase 1: Sending emails normally...');
    await smtpClient.sendMail(emails[0]);
    console.log('  ✓ Email 1 sent successfully');

    await smtpClient.sendMail(emails[1]);
    console.log('  ✓ Email 2 sent successfully');

    console.log('  Phase 2: Enabling error injection...');
    errorInjectionEnabled = true;

    console.log('  Sending emails with error injection...');
    const recoveryResults = [];
    
    for (let i = 2; i < 4; i++) {
      try {
        await smtpClient.sendMail(emails[i]);
        recoveryResults.push({ success: true, index: i });
        console.log(`  ✓ Email ${i + 1} sent despite errors`);
      } catch (error) {
        recoveryResults.push({ success: false, index: i, error });
        console.log(`  ✗ Email ${i + 1} failed: ${error.message}`);
      }
    }

    console.log('  Phase 3: Disabling error injection...');
    errorInjectionEnabled = false;

    console.log('  Sending final emails (recovery validation)...');
    for (let i = 4; i < emails.length; i++) {
      try {
        await smtpClient.sendMail(emails[i]);
        recoveryResults.push({ success: true, index: i });
        console.log(`  ✓ Email ${i + 1} sent after recovery`);
      } catch (error) {
        recoveryResults.push({ success: false, index: i, error });
        console.log(`  ✗ Email ${i + 1} failed: ${error.message}`);
      }
    }

    const successful = recoveryResults.filter(r => r.success).length;
    const totalSuccessful = 2 + successful; // 2 initial + recovery phase

    console.log(`  Pre-error emails: 2/2 successful`);
    console.log(`  Error/recovery phase emails: ${successful}/${recoveryResults.length} successful`);
    console.log(`  Total success rate: ${((totalSuccessful / emails.length) * 100).toFixed(1)}%`);
    console.log(`  Error recovery: ${successful >= recoveryResults.length - 2 ? 'Effective' : 'Partial'}`);

    expect(totalSuccessful).toBeGreaterThanOrEqual(4); // At least initial + some recovery

    smtpClient.close();
  } finally {
    server.close();
  }
});

tap.test('CREL-04: Resource Management During Issues', async () => {
  console.log('\n🧠 Testing resource management during connection issues...');
  
  let memoryBefore = process.memoryUsage();
  
  const server = net.createServer(socket => {
    socket.write('220 localhost SMTP Test Server\r\n');
    
    socket.on('data', (data) => {
      const lines = data.toString().split('\r\n');
      
      lines.forEach(line => {
        if (line.startsWith('EHLO') || line.startsWith('HELO')) {
          socket.write('250-localhost\r\n');
          socket.write('250 SIZE 10485760\r\n');
        } else if (line.startsWith('MAIL FROM:')) {
          socket.write('250 OK\r\n');
        } else if (line.startsWith('RCPT TO:')) {
          socket.write('250 OK\r\n');
        } else if (line === 'DATA') {
          socket.write('354 Send data\r\n');
        } else if (line === '.') {
          socket.write('250 OK Message accepted\r\n');
        } else if (line === 'QUIT') {
          socket.write('221 Bye\r\n');
          socket.end();
        }
      });
    });
  });

  await new Promise<void>((resolve) => {
    server.listen(0, '127.0.0.1', () => {
      resolve();
    });
  });

  const port = (server.address() as net.AddressInfo).port;

  try {
    console.log('  Creating client for resource management test...');
    const smtpClient = createTestSmtpClient({
      host: '127.0.0.1',
      port: port,
      secure: false,
      maxConnections: 5,
      maxMessages: 100
    });

    console.log('  Creating emails with various content types...');
    const emails = [
      new Email({
        from: 'sender@resource.test',
        to: ['recipient1@resource.test'],
        subject: 'Resource Test - Normal',
        text: 'Normal email content'
      }),
      new Email({
        from: 'sender@resource.test',
        to: ['recipient2@resource.test'],
        subject: 'Resource Test - Large Content',
        text: 'X'.repeat(50000) // Large content
      }),
      new Email({
        from: 'sender@resource.test',
        to: ['recipient3@resource.test'],
        subject: 'Resource Test - Unicode',
        text: '🎭🎪🎨🎯🎲🎸🎺🎻🎼🎵🎶🎷'.repeat(100)
      })
    ];

    console.log('  Sending emails and monitoring resource usage...');
    const results = [];
    
    for (let i = 0; i < emails.length; i++) {
      console.log(`  Testing email ${i + 1} (${emails[i].subject.split(' - ')[1]})...`);
      
      try {
        // Monitor memory usage before sending
        const memBefore = process.memoryUsage();
        console.log(`    Memory before: ${Math.round(memBefore.heapUsed / 1024 / 1024)}MB`);
        
        await smtpClient.sendMail(emails[i]);
        
        const memAfter = process.memoryUsage();
        console.log(`    Memory after: ${Math.round(memAfter.heapUsed / 1024 / 1024)}MB`);
        
        const memIncrease = memAfter.heapUsed - memBefore.heapUsed;
        console.log(`    Memory increase: ${Math.round(memIncrease / 1024)}KB`);
        
        results.push({ 
          success: true, 
          index: i,
          memoryIncrease: memIncrease
        });
        console.log(`    ✓ Email ${i + 1} sent successfully`);
        
      } catch (error) {
        results.push({ success: false, index: i, error });
        console.log(`    ✗ Email ${i + 1} failed: ${error.message}`);
      }

      // Force garbage collection if available
      if (global.gc) {
        global.gc();
      }
      
      await new Promise(resolve => setTimeout(resolve, 100));
    }

    const successful = results.filter(r => r.success).length;
    const totalMemoryIncrease = results.reduce((sum, r) => sum + (r.memoryIncrease || 0), 0);
    
    console.log(`  Resource management: ${successful}/${emails.length} emails processed`);
    console.log(`  Total memory increase: ${Math.round(totalMemoryIncrease / 1024)}KB`);
    console.log(`  Resource efficiency: ${((successful / emails.length) * 100).toFixed(1)}%`);

    expect(successful).toBeGreaterThanOrEqual(2); // Most emails should succeed
    expect(totalMemoryIncrease).toBeLessThan(100 * 1024 * 1024); // Less than 100MB increase

    smtpClient.close();
  } finally {
    server.close();
  }
});

tap.test('CREL-04: Test Summary', async () => {
  console.log('\n✅ CREL-04: Crash Recovery Reliability Tests completed');
  console.log('💥 All connection recovery scenarios tested successfully');
});

tap.start();