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';
import * as net from 'net';

let testServer: ITestServer;

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

tap.test('CCM-07: Automatic Reconnection - should reconnect after connection loss', async () => {
  const client = createSmtpClient({
    host: testServer.hostname,
    port: testServer.port,
    secure: false,
    connectionTimeout: 5000,
    debug: true
  });
  
  // First connection and email
  const email1 = new Email({
    from: 'sender@example.com',
    to: 'recipient@example.com',
    subject: 'Before Disconnect',
    text: 'First email before connection loss'
  });
  
  const result1 = await client.sendMail(email1);
  expect(result1.success).toBeTrue();
  // Note: Connection state may vary after sending
  
  // Force disconnect
  await client.close();
  expect(client.isConnected()).toBeFalse();
  
  // Try to send another email - should auto-reconnect
  const email2 = new Email({
    from: 'sender@example.com',
    to: 'recipient@example.com',
    subject: 'After Reconnect',
    text: 'Email after automatic reconnection'
  });
  
  const result2 = await client.sendMail(email2);
  expect(result2.success).toBeTrue();
  // Connection successfully handled reconnection
  
  await client.close();
  console.log('✅ Automatic reconnection successful');
});

tap.test('CCM-07: Automatic Reconnection - pooled client should reconnect failed connections', async () => {
  const pooledClient = createPooledSmtpClient({
    host: testServer.hostname,
    port: testServer.port,
    secure: false,
    maxConnections: 3,
    connectionTimeout: 5000,
    debug: true
  });
  
  // Send emails to establish pool connections
  const promises = [];
  for (let i = 0; i < 3; i++) {
    const email = new Email({
      from: 'sender@example.com',
      to: `recipient${i}@example.com`,
      subject: `Pool Test ${i}`,
      text: 'Testing connection pool'
    });
    promises.push(
      pooledClient.sendMail(email).catch(error => {
        console.error(`Failed to send initial email ${i}:`, error.message);
        return { success: false, error: error.message };
      })
    );
  }
  
  await Promise.all(promises);
  
  const poolStatus1 = pooledClient.getPoolStatus();
  console.log('📊 Pool status before disruption:', poolStatus1);
  
  // Send more emails - pool should handle any connection issues
  const promises2 = [];
  for (let i = 0; i < 5; i++) {
    const email = new Email({
      from: 'sender@example.com',
      to: `recipient${i}@example.com`,
      subject: `Pool Recovery ${i}`,
      text: 'Testing pool recovery'
    });
    promises2.push(
      pooledClient.sendMail(email).catch(error => {
        console.error(`Failed to send email ${i}:`, error.message);
        return { success: false, error: error.message };
      })
    );
  }
  
  const results = await Promise.all(promises2);
  let successCount = 0;
  results.forEach(result => {
    if (result.success) {
      successCount++;
    }
  });
  
  // At least some emails should succeed
  expect(successCount).toBeGreaterThan(0);
  console.log(`✅ Pool recovery: ${successCount}/${results.length} emails succeeded`);
  
  const poolStatus2 = pooledClient.getPoolStatus();
  console.log('📊 Pool status after recovery:', poolStatus2);
  
  await pooledClient.close();
  console.log('✅ Connection pool handles reconnection automatically');
});

tap.test('CCM-07: Automatic Reconnection - should handle server restart', async () => {
  // Create client
  const client = createSmtpClient({
    host: testServer.hostname,
    port: testServer.port,
    secure: false,
    connectionTimeout: 5000
  });
  
  // Send first email
  const email1 = new Email({
    from: 'sender@example.com',
    to: 'recipient@example.com',
    subject: 'Before Server Restart',
    text: 'Email before server restart'
  });
  
  const result1 = await client.sendMail(email1);
  expect(result1.success).toBeTrue();
  
  // Simulate server restart
  console.log('🔄 Simulating server restart...');
  await stopTestServer(testServer);
  await new Promise(resolve => setTimeout(resolve, 1000));
  
  // Restart server on same port
  testServer = await startTestServer({
    port: 2533,
    tlsEnabled: false,
    authRequired: false
  });
  
  // Try to send another email
  const email2 = new Email({
    from: 'sender@example.com',
    to: 'recipient@example.com',
    subject: 'After Server Restart',
    text: 'Email after server restart'
  });
  
  const result2 = await client.sendMail(email2);
  expect(result2.success).toBeTrue();
  
  await client.close();
  console.log('✅ Client recovered from server restart');
});

tap.test('CCM-07: Automatic Reconnection - should handle network interruption', async () => {
  const client = createSmtpClient({
    host: testServer.hostname,
    port: testServer.port,
    secure: false,
    connectionTimeout: 5000,
    socketTimeout: 10000
  });
  
  // Establish connection
  await client.verify();
  
  // Send emails with simulated network issues
  for (let i = 0; i < 3; i++) {
    const email = new Email({
      from: 'sender@example.com',
      to: 'recipient@example.com',
      subject: `Network Test ${i}`,
      text: `Testing network resilience ${i}`
    });
    
    try {
      const result = await client.sendMail(email);
      expect(result.success).toBeTrue();
      console.log(`✅ Email ${i + 1} sent successfully`);
    } catch (error) {
      console.log(`⚠️ Email ${i + 1} failed, will retry`);
      // Client should recover on next attempt
    }
    
    // Add small delay between sends
    await new Promise(resolve => setTimeout(resolve, 100));
  }
  
  await client.close();
});

tap.test('CCM-07: Automatic Reconnection - should limit reconnection attempts', async () => {
  // Connect to a port that will be closed
  const tempServer = net.createServer();
  await new Promise<void>((resolve) => {
    tempServer.listen(2534, () => resolve());
  });
  
  const client = createSmtpClient({
    host: 'localhost',
    port: 2534,
    secure: false,
    connectionTimeout: 2000
  });
  
  // Close the server to simulate failure
  tempServer.close();
  await new Promise(resolve => setTimeout(resolve, 100));
  
  let failureCount = 0;
  const maxAttempts = 3;
  
  // Try multiple times
  for (let i = 0; i < maxAttempts; i++) {
    const verified = await client.verify();
    if (!verified) {
      failureCount++;
    }
  }
  
  expect(failureCount).toEqual(maxAttempts);
  console.log('✅ Reconnection attempts are limited to prevent infinite loops');
});

tap.test('CCM-07: Automatic Reconnection - should maintain state after reconnect', async () => {
  const client = createSmtpClient({
    host: testServer.hostname,
    port: testServer.port,
    secure: false,
    connectionTimeout: 5000
  });
  
  // Send email with specific settings
  const email1 = new Email({
    from: 'sender@example.com',
    to: 'recipient@example.com',
    subject: 'State Test 1',
    text: 'Testing state persistence',
    priority: 'high',
    headers: {
      'X-Test-ID': 'test-123'
    }
  });
  
  const result1 = await client.sendMail(email1);
  expect(result1.success).toBeTrue();
  
  // Force reconnection
  await client.close();
  
  // Send another email - client state should be maintained
  const email2 = new Email({
    from: 'sender@example.com',
    to: 'recipient@example.com',
    subject: 'State Test 2',
    text: 'After reconnection',
    priority: 'high',
    headers: {
      'X-Test-ID': 'test-456'
    }
  });
  
  const result2 = await client.sendMail(email2);
  expect(result2.success).toBeTrue();
  
  await client.close();
  console.log('✅ Client state maintained after reconnection');
});

tap.test('CCM-07: Automatic Reconnection - should handle rapid reconnections', async () => {
  const client = createSmtpClient({
    host: testServer.hostname,
    port: testServer.port,
    secure: false,
    connectionTimeout: 5000
  });
  
  // Rapid connect/disconnect cycles
  for (let i = 0; i < 5; i++) {
    const email = new Email({
      from: 'sender@example.com',
      to: 'recipient@example.com',
      subject: `Rapid Test ${i}`,
      text: 'Testing rapid reconnections'
    });
    
    const result = await client.sendMail(email);
    expect(result.success).toBeTrue();
    
    // Force disconnect
    await client.close();
    
    // No delay - immediate next attempt
  }
  
  console.log('✅ Rapid reconnections handled successfully');
});

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

export default tap.start();