import { tap, expect } from '@git.zone/tstest/tapbundle';
import * as plugins from '../ts/plugins.js';
import * as paths from '../ts/paths.js';
import { smtpClientMod } from '../ts/mail/delivery/index.js';
import type { ISmtpClientOptions, SmtpClient } from '../ts/mail/delivery/smtpclient/index.js';
import { Email } from '../ts/mail/core/classes.email.js';

/**
 * Tests for the SMTP client class
 */
tap.test('verify SMTP client initialization', async () => {
  // Create test configuration
  const options: ISmtpClientOptions = {
    host: 'smtp.example.com',
    port: 587,
    secure: false,
    connectionTimeout: 10000,
    domain: 'test.example.com'
  };
  
  // Create SMTP client instance
  const smtpClient = smtpClientMod.createSmtpClient(options);
  
  // Verify instance was created correctly
  expect(smtpClient).toBeTruthy();
  expect(smtpClient.isConnected()).toBeFalsy(); // Should start disconnected
});

tap.test('test SMTP client configuration update', async () => {
  // Create test configuration
  const options: ISmtpClientOptions = {
    host: 'smtp.example.com',
    port: 587,
    secure: false
  };
  
  // Create SMTP client instance
  const smtpClient = smtpClientMod.createSmtpClient(options);
  
  // Update configuration
  smtpClient.updateOptions({
    host: 'new-smtp.example.com',
    port: 465,
    secure: true
  });
  
  // Can't directly test private fields, but we can verify it doesn't throw
  expect(() => smtpClient.updateOptions({
    tls: {
      rejectUnauthorized: false
    }
  })).not.toThrow();
});

// Mocked SMTP server for testing
class MockSmtpServer {
  private responses: Map<string, string>;
  
  constructor() {
    this.responses = new Map();
    
    // Default responses
    this.responses.set('connect', '220 smtp.example.com ESMTP ready');
    this.responses.set('EHLO', '250-smtp.example.com\r\n250-PIPELINING\r\n250-SIZE 10240000\r\n250-STARTTLS\r\n250-AUTH PLAIN LOGIN\r\n250 HELP');
    this.responses.set('MAIL FROM', '250 OK');
    this.responses.set('RCPT TO', '250 OK');
    this.responses.set('DATA', '354 Start mail input; end with <CRLF>.<CRLF>');
    this.responses.set('data content', '250 OK: message accepted');
    this.responses.set('QUIT', '221 Bye');
  }
  
  public setResponse(command: string, response: string): void {
    this.responses.set(command, response);
  }
  
  public getResponse(command: string): string {
    if (command.startsWith('MAIL FROM')) {
      return this.responses.get('MAIL FROM') || '250 OK';
    } else if (command.startsWith('RCPT TO')) {
      return this.responses.get('RCPT TO') || '250 OK';
    } else if (command.startsWith('EHLO') || command.startsWith('HELO')) {
      return this.responses.get('EHLO') || '250 OK';
    } else if (command === 'DATA') {
      return this.responses.get('DATA') || '354 Start mail input; end with <CRLF>.<CRLF>';
    } else if (command.includes('Content-Type')) {
      return this.responses.get('data content') || '250 OK: message accepted';
    } else if (command === 'QUIT') {
      return this.responses.get('QUIT') || '221 Bye';
    }
    
    return this.responses.get(command) || '250 OK';
  }
}

/**
 * This test validates the SMTP client public interface
 */
tap.test('verify SMTP client email delivery functionality with mock', async () => {
  // Create a test email
  const testEmail = new Email({
    from: 'sender@example.com',
    to: ['recipient@example.com'],
    subject: 'Test Email',
    text: 'This is a test email'
  });
  
  // Create SMTP client options
  const options: ISmtpClientOptions = {
    host: 'smtp.example.com',
    port: 587,
    secure: false,
    domain: 'test.example.com',
    auth: {
      user: 'testuser',
      pass: 'testpass'
    }
  };
  
  // Create SMTP client instance
  const smtpClient = smtpClientMod.createSmtpClient(options);
  
  // Test public methods exist and have correct signatures
  expect(typeof smtpClient.sendMail).toEqual('function');
  expect(typeof smtpClient.verify).toEqual('function');
  expect(typeof smtpClient.isConnected).toEqual('function');
  expect(typeof smtpClient.getPoolStatus).toEqual('function');
  expect(typeof smtpClient.updateOptions).toEqual('function');
  expect(typeof smtpClient.close).toEqual('function');
  
  // Test connection status before any operation
  expect(smtpClient.isConnected()).toBeFalsy();
  
  // Test pool status
  const poolStatus = smtpClient.getPoolStatus();
  expect(poolStatus).toBeTruthy();
  expect(typeof poolStatus.active).toEqual('number');
  expect(typeof poolStatus.idle).toEqual('number');
  expect(typeof poolStatus.total).toEqual('number');
  
  // Since we can't connect to a real server, we'll skip the actual send test
  // and just verify the client was created correctly
  expect(smtpClient).toBeTruthy();
});

tap.test('test SMTP client error handling with mock', async () => {
  // Create SMTP client instance
  const smtpClient = smtpClientMod.createSmtpClient({
    host: 'smtp.example.com',
    port: 587,
    secure: false
  });
  
  // Test with valid email (Email class might allow any string)
  const testEmail = new Email({
    from: 'sender@example.com',
    to: ['recipient@example.com'],
    subject: 'Test Email',
    text: 'This is a test email'
  });
  
  // Test event listener methods
  const mockListener = () => {};
  smtpClient.on('test-event', mockListener);
  smtpClient.off('test-event', mockListener);
  
  // Test update options
  smtpClient.updateOptions({
    auth: {
      user: 'newuser',
      pass: 'newpass'
    }
  });
  
  // Verify client is still functional
  expect(smtpClient.isConnected()).toBeFalsy();
  
  // Test close on a non-connected client
  await smtpClient.close();
  expect(smtpClient.isConnected()).toBeFalsy();
});

// Final clean-up test
tap.test('clean up after tests', async () => {
  // No-op - just to make sure everything is cleaned up properly
});

tap.test('stop', async () => {
  await tap.stopForcefully();
});

export default tap.start();