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

let testServer: ITestServer;

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

tap.test('CCM-06: Connection Timeout - should timeout on unresponsive server', async () => {
  const startTime = Date.now();
  
  const timeoutClient = createSmtpClient({
    host: testServer.hostname,
    port: 9999, // Non-existent port
    secure: false,
    connectionTimeout: 2000, // 2 second timeout
    debug: true
  });
  
  // verify() returns false on connection failure, doesn't throw
  const verified = await timeoutClient.verify();
  const duration = Date.now() - startTime;
  
  expect(verified).toBeFalse();
  expect(duration).toBeLessThan(3000); // Should timeout within 3s
  
  console.log(`✅ Connection timeout after ${duration}ms`);
});

tap.test('CCM-06: Connection Timeout - should handle slow server response', async () => {
  // Create a mock slow server
  const slowServer = net.createServer((socket) => {
    // Accept connection but delay response
    setTimeout(() => {
      socket.write('220 Slow server ready\r\n');
    }, 3000); // 3 second delay
  });
  
  await new Promise<void>((resolve) => {
    slowServer.listen(2533, () => resolve());
  });
  
  const startTime = Date.now();
  
  const slowClient = createSmtpClient({
    host: 'localhost',
    port: 2533,
    secure: false,
    connectionTimeout: 1000, // 1 second timeout
    debug: true
  });
  
  // verify() should return false when server is too slow
  const verified = await slowClient.verify();
  const duration = Date.now() - startTime;
  
  expect(verified).toBeFalse();
  // Note: actual timeout might be longer due to system defaults
  console.log(`✅ Slow server timeout after ${duration}ms`);
  
  slowServer.close();
  await new Promise(resolve => setTimeout(resolve, 100));
});

tap.test('CCM-06: Connection Timeout - should respect socket timeout during data transfer', async () => {
  const socketTimeoutClient = createSmtpClient({
    host: testServer.hostname,
    port: testServer.port,
    secure: false,
    connectionTimeout: 5000,
    socketTimeout: 10000, // 10 second socket timeout
    debug: true
  });
  
  await socketTimeoutClient.verify();
  
  // Send a normal email
  const email = new Email({
    from: 'sender@example.com',
    to: 'recipient@example.com',
    subject: 'Socket Timeout Test',
    text: 'Testing socket timeout configuration'
  });
  
  const result = await socketTimeoutClient.sendMail(email);
  expect(result.success).toBeTrue();
  
  await socketTimeoutClient.close();
  console.log('✅ Socket timeout configuration applied');
});

tap.test('CCM-06: Connection Timeout - should handle timeout during TLS handshake', async () => {
  // Create a server that accepts connections but doesn't complete TLS
  const badTlsServer = net.createServer((socket) => {
    // Accept connection but don't respond to TLS
    socket.on('data', () => {
      // Do nothing - simulate hung TLS handshake
    });
  });
  
  await new Promise<void>((resolve) => {
    badTlsServer.listen(2534, () => resolve());
  });
  
  const startTime = Date.now();
  
  const tlsTimeoutClient = createSmtpClient({
    host: 'localhost',
    port: 2534,
    secure: true, // Try TLS
    connectionTimeout: 2000,
    tls: {
      rejectUnauthorized: false
    }
  });
  
  // verify() should return false when TLS handshake times out
  const verified = await tlsTimeoutClient.verify();
  const duration = Date.now() - startTime;
  
  expect(verified).toBeFalse();
  // Note: actual timeout might be longer due to system defaults
  console.log(`✅ TLS handshake timeout after ${duration}ms`);
  
  badTlsServer.close();
  await new Promise(resolve => setTimeout(resolve, 100));
});

tap.test('CCM-06: Connection Timeout - should not timeout on successful quick connection', async () => {
  const quickClient = createSmtpClient({
    host: testServer.hostname,
    port: testServer.port,
    secure: false,
    connectionTimeout: 30000, // Very long timeout
    debug: true
  });
  
  const startTime = Date.now();
  
  const isConnected = await quickClient.verify();
  const duration = Date.now() - startTime;
  
  expect(isConnected).toBeTrue();
  expect(duration).toBeLessThan(5000); // Should connect quickly
  
  await quickClient.close();
  console.log(`✅ Quick connection established in ${duration}ms`);
});

tap.test('CCM-06: Connection Timeout - should handle timeout during authentication', async () => {
  // Start auth server
  const authServer = await startTestServer({
    port: 2535,
    authRequired: true
  });
  
  // Create mock auth that delays
  const authTimeoutClient = createSmtpClient({
    host: authServer.hostname,
    port: authServer.port,
    secure: false,
    connectionTimeout: 5000,
    socketTimeout: 1000, // Very short socket timeout
    auth: {
      user: 'testuser',
      pass: 'testpass'
    }
  });
  
  try {
    await authTimeoutClient.verify();
    // If this succeeds, auth was fast enough
    await authTimeoutClient.close();
    console.log('✅ Authentication completed within timeout');
  } catch (error) {
    console.log('✅ Authentication timeout handled');
  }
  
  await stopTestServer(authServer);
});

tap.test('CCM-06: Connection Timeout - should apply different timeouts for different operations', async () => {
  const multiTimeoutClient = createSmtpClient({
    host: testServer.hostname,
    port: testServer.port,
    secure: false,
    connectionTimeout: 5000,  // Connection establishment
    socketTimeout: 30000,     // Data operations
    debug: true
  });
  
  // Connection should be quick
  const connectStart = Date.now();
  await multiTimeoutClient.verify();
  const connectDuration = Date.now() - connectStart;
  
  expect(connectDuration).toBeLessThan(5000);
  
  // Send email with potentially longer operation
  const email = new Email({
    from: 'sender@example.com',
    to: 'recipient@example.com',
    subject: 'Multi-timeout Test',
    text: 'Testing different timeout values',
    attachments: [{
      filename: 'test.txt',
      content: Buffer.from('Test content'),
      contentType: 'text/plain'
    }]
  });
  
  const sendStart = Date.now();
  const result = await multiTimeoutClient.sendMail(email);
  const sendDuration = Date.now() - sendStart;
  
  expect(result.success).toBeTrue();
  console.log(`✅ Different timeouts applied: connect=${connectDuration}ms, send=${sendDuration}ms`);
  
  await multiTimeoutClient.close();
});

tap.test('CCM-06: Connection Timeout - should retry after timeout with pooled connections', async () => {
  const retryClient = createSmtpClient({
    host: testServer.hostname,
    port: testServer.port,
    secure: false,
    pool: true,
    maxConnections: 2,
    connectionTimeout: 5000,
    debug: true
  });
  
  // First connection should succeed
  const email1 = new Email({
    from: 'sender@example.com',
    to: 'recipient@example.com',
    subject: 'Pre-timeout Email',
    text: 'Before any timeout'
  });
  
  const result1 = await retryClient.sendMail(email1);
  expect(result1.success).toBeTrue();
  
  // Pool should handle connection management
  const poolStatus = retryClient.getPoolStatus();
  console.log('📊 Pool status:', poolStatus);
  
  await retryClient.close();
  console.log('✅ Connection pool handles timeouts gracefully');
});

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

export default tap.start();