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';

let testServer: ITestServer;

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

tap.test('CREL-01: Basic reconnection after close', async () => {
  const smtpClient = createSmtpClient({
    host: testServer.hostname,
    port: testServer.port,
    secure: false,
    connectionTimeout: 5000,
    debug: true
  });

  // First verify connection works
  const result1 = await smtpClient.verify();
  expect(result1).toBeTrue();
  console.log('Initial connection verified');

  // Close connection
  await smtpClient.close();
  console.log('Connection closed');

  // Verify again - should reconnect automatically
  const result2 = await smtpClient.verify();
  expect(result2).toBeTrue();
  console.log('Reconnection successful');

  await smtpClient.close();
});

tap.test('CREL-01: Multiple sequential connections', async () => {
  const smtpClient = createSmtpClient({
    host: testServer.hostname,
    port: testServer.port,
    secure: false,
    connectionTimeout: 5000,
    debug: true
  });

  // Send multiple emails with closes in between
  for (let i = 0; i < 3; i++) {
    const email = new Email({
      from: 'sender@example.com',
      to: ['recipient@example.com'],
      subject: `Sequential Test ${i + 1}`,
      text: 'Testing sequential connections'
    });

    const result = await smtpClient.sendMail(email);
    expect(result.success).toBeTrue();
    console.log(`Email ${i + 1} sent successfully`);

    // Close connection after each send
    await smtpClient.close();
    console.log(`Connection closed after email ${i + 1}`);
  }
});

tap.test('CREL-01: Recovery from server restart', async () => {
  const smtpClient = createSmtpClient({
    host: testServer.hostname,
    port: testServer.port,
    secure: false,
    connectionTimeout: 5000,
    debug: true
  });

  // Send first email
  const email1 = new Email({
    from: 'sender@example.com',
    to: ['recipient@example.com'],
    subject: 'Before Server Restart',
    text: 'Testing server restart recovery'
  });

  const result1 = await smtpClient.sendMail(email1);
  expect(result1.success).toBeTrue();
  console.log('First email sent successfully');

  // Simulate server restart by creating a brief interruption
  console.log('Simulating server restart...');
  
  // The SMTP client should handle the disconnection gracefully
  // and reconnect for the next operation
  
  // Wait a moment
  await new Promise(resolve => setTimeout(resolve, 1000));

  // Try to send another email
  const email2 = new Email({
    from: 'sender@example.com',
    to: ['recipient@example.com'],
    subject: 'After Server Restart',
    text: 'Testing recovery after restart'
  });

  const result2 = await smtpClient.sendMail(email2);
  expect(result2.success).toBeTrue();
  console.log('Second email sent successfully after simulated restart');

  await smtpClient.close();
});

tap.test('CREL-01: Connection pool reliability', async () => {
  const pooledClient = createSmtpClient({
    host: testServer.hostname,
    port: testServer.port,
    secure: false,
    pool: true,
    maxConnections: 3,
    maxMessages: 10,
    connectionTimeout: 5000,
    debug: true
  });

  // Send multiple emails concurrently
  const emails = Array.from({ length: 10 }, (_, i) => new Email({
    from: 'sender@example.com',
    to: [`recipient${i}@example.com`],
    subject: `Pool Test ${i}`,
    text: 'Testing connection pool'
  }));

  console.log('Sending 10 emails through connection pool...');
  
  const results = await Promise.allSettled(
    emails.map(email => pooledClient.sendMail(email))
  );
  
  const successful = results.filter(r => r.status === 'fulfilled').length;
  const failed = results.filter(r => r.status === 'rejected').length;
  
  console.log(`Pool results: ${successful} successful, ${failed} failed`);
  expect(successful).toBeGreaterThan(0);
  
  // Most should succeed
  expect(successful).toBeGreaterThanOrEqual(8);

  await pooledClient.close();
});

tap.test('CREL-01: Rapid connection cycling', async () => {
  const smtpClient = createSmtpClient({
    host: testServer.hostname,
    port: testServer.port,
    secure: false,
    connectionTimeout: 5000,
    debug: true
  });

  // Rapidly open and close connections
  console.log('Testing rapid connection cycling...');
  
  for (let i = 0; i < 5; i++) {
    const result = await smtpClient.verify();
    expect(result).toBeTrue();
    await smtpClient.close();
    console.log(`Cycle ${i + 1} completed`);
  }

  console.log('Rapid cycling completed successfully');
});

tap.test('CREL-01: Error recovery', async () => {
  // Test with invalid server first
  const smtpClient = createSmtpClient({
    host: 'invalid.host.local',
    port: 9999,
    secure: false,
    connectionTimeout: 1000,
    debug: true
  });

  // First attempt should fail
  const result1 = await smtpClient.verify();
  expect(result1).toBeFalse();
  console.log('Connection to invalid host failed as expected');

  // Now update to valid server (simulating failover)
  // Since we can't update options, create a new client
  const recoveredClient = createSmtpClient({
    host: testServer.hostname,
    port: testServer.port,
    secure: false,
    connectionTimeout: 5000,
    debug: true
  });

  // Should connect successfully
  const result2 = await recoveredClient.verify();
  expect(result2).toBeTrue();
  console.log('Connection to valid host succeeded');

  // Send email to verify full functionality
  const email = new Email({
    from: 'sender@example.com',
    to: ['recipient@example.com'],
    subject: 'Recovery Test',
    text: 'Testing error recovery'
  });

  const sendResult = await recoveredClient.sendMail(email);
  expect(sendResult.success).toBeTrue();
  console.log('Email sent successfully after recovery');

  await recoveredClient.close();
});

tap.test('CREL-01: Long-lived connection', async () => {
  const smtpClient = createSmtpClient({
    host: testServer.hostname,
    port: testServer.port,
    secure: false,
    connectionTimeout: 30000, // 30 second timeout
    socketTimeout: 30000,
    debug: true
  });

  console.log('Testing long-lived connection...');

  // Send emails over time
  for (let i = 0; i < 3; i++) {
    const email = new Email({
      from: 'sender@example.com',
      to: ['recipient@example.com'],
      subject: `Long-lived Test ${i + 1}`,
      text: `Email ${i + 1} over long-lived connection`
    });

    const result = await smtpClient.sendMail(email);
    expect(result.success).toBeTrue();
    console.log(`Email ${i + 1} sent at ${new Date().toISOString()}`);

    // Wait between sends
    if (i < 2) {
      await new Promise(resolve => setTimeout(resolve, 2000));
    }
  }

  console.log('Long-lived connection test completed');
  await smtpClient.close();
});

tap.test('CREL-01: Concurrent operations', async () => {
  const smtpClient = createSmtpClient({
    host: testServer.hostname,
    port: testServer.port,
    secure: false,
    pool: true,
    maxConnections: 5,
    connectionTimeout: 5000,
    debug: true
  });

  console.log('Testing concurrent operations...');

  // Mix verify and send operations
  const operations = [
    smtpClient.verify(),
    smtpClient.sendMail(new Email({
      from: 'sender@example.com',
      to: ['recipient1@example.com'],
      subject: 'Concurrent 1',
      text: 'First concurrent email'
    })),
    smtpClient.verify(),
    smtpClient.sendMail(new Email({
      from: 'sender@example.com',
      to: ['recipient2@example.com'],
      subject: 'Concurrent 2',
      text: 'Second concurrent email'
    })),
    smtpClient.verify()
  ];

  const results = await Promise.allSettled(operations);
  
  const successful = results.filter(r => r.status === 'fulfilled').length;
  console.log(`Concurrent operations: ${successful}/${results.length} successful`);
  
  expect(successful).toEqual(results.length);

  await smtpClient.close();
});

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

export default tap.start();