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 { EmailValidator } from '../../../ts/mail/core/classes.emailvalidator.js';

let testServer: ITestServer;
let smtpClient: SmtpClient;

tap.test('setup test SMTP server', async () => {
  testServer = await startTestServer({
    port: 2550,
    tlsEnabled: false,
    authRequired: false
  });
  expect(testServer).toBeTruthy();
  expect(testServer.port).toBeGreaterThan(0);
  
  smtpClient = createSmtpClient({
    host: testServer.hostname,
    port: testServer.port,
    secure: false,
    connectionTimeout: 5000
  });
});

tap.test('CCMD-10: Email address validation', async () => {
  // Test email address validation which is what VRFY conceptually does
  const validator = new EmailValidator();
  
  const testAddresses = [
    { address: 'user@example.com', expected: true },
    { address: 'postmaster@example.com', expected: true },
    { address: 'admin@example.com', expected: true },
    { address: 'user.name+tag@example.com', expected: true },
    { address: 'test@sub.domain.example.com', expected: true },
    { address: 'invalid@', expected: false },
    { address: '@example.com', expected: false },
    { address: 'not-an-email', expected: false },
    { address: '', expected: false },
    { address: 'user@', expected: false }
  ];
  
  console.log('Testing email address validation (VRFY equivalent):\n');
  
  for (const test of testAddresses) {
    const isValid = validator.isValidFormat(test.address);
    expect(isValid).toEqual(test.expected);
    console.log(`Address: "${test.address}" - Valid: ${isValid} (expected: ${test.expected})`);
  }
  
  // Test sending to valid addresses
  const validEmail = new Email({
    from: 'sender@example.com',
    to: ['user@example.com'],
    subject: 'Address validation test',
    text: 'Testing address validation'
  });
  
  await smtpClient.sendMail(validEmail);
  console.log('\nEmail sent successfully to validated address');
});

tap.test('CCMD-10: Multiple recipient handling (EXPN equivalent)', async () => {
  // Test multiple recipients which is conceptually similar to mailing list expansion
  
  console.log('Testing multiple recipient handling (EXPN equivalent):\n');
  
  // Create email with multiple recipients (like a mailing list)
  const multiRecipientEmail = new Email({
    from: 'sender@example.com',
    to: [
      'user1@example.com',
      'user2@example.com',
      'user3@example.com'
    ],
    cc: [
      'cc1@example.com',
      'cc2@example.com'
    ],
    bcc: [
      'bcc1@example.com'
    ],
    subject: 'Multi-recipient test (mailing list)',
    text: 'Testing email distribution to multiple recipients'
  });
  
  const toAddresses = multiRecipientEmail.getToAddresses();
  const ccAddresses = multiRecipientEmail.getCcAddresses();
  const bccAddresses = multiRecipientEmail.getBccAddresses();
  
  console.log(`To recipients: ${toAddresses.length}`);
  toAddresses.forEach(addr => console.log(`  - ${addr}`));
  
  console.log(`\nCC recipients: ${ccAddresses.length}`);
  ccAddresses.forEach(addr => console.log(`  - ${addr}`));
  
  console.log(`\nBCC recipients: ${bccAddresses.length}`);
  bccAddresses.forEach(addr => console.log(`  - ${addr}`));
  
  console.log(`\nTotal recipients: ${toAddresses.length + ccAddresses.length + bccAddresses.length}`);
  
  // Send the email
  await smtpClient.sendMail(multiRecipientEmail);
  console.log('\nEmail sent successfully to all recipients');
});

tap.test('CCMD-10: Email addresses with display names', async () => {
  // Test email addresses with display names (full names)
  
  console.log('Testing email addresses with display names:\n');
  
  const fullNameTests = [
    { from: '"John Doe" <john@example.com>', expectedAddress: 'john@example.com' },
    { from: '"Smith, John" <john.smith@example.com>', expectedAddress: 'john.smith@example.com' },
    { from: 'Mary Johnson <mary@example.com>', expectedAddress: 'mary@example.com' },
    { from: '<bob@example.com>', expectedAddress: 'bob@example.com' }
  ];
  
  for (const test of fullNameTests) {
    const email = new Email({
      from: test.from,
      to: ['recipient@example.com'],
      subject: 'Display name test',
      text: `Testing from: ${test.from}`
    });
    
    const fromAddress = email.getFromAddress();
    console.log(`Full: "${test.from}"`);
    console.log(`Extracted: "${fromAddress}"`);
    expect(fromAddress).toEqual(test.expectedAddress);
    
    await smtpClient.sendMail(email);
    console.log('Email sent successfully\n');
  }
});

tap.test('CCMD-10: Email validation security', async () => {
  // Test security aspects of email validation
  
  console.log('Testing email validation security considerations:\n');
  
  // Test common system/role addresses that should be handled carefully
  const systemAddresses = [
    'root@example.com',
    'admin@example.com',
    'administrator@example.com',
    'webmaster@example.com',
    'hostmaster@example.com',
    'abuse@example.com',
    'postmaster@example.com',
    'noreply@example.com'
  ];
  
  const validator = new EmailValidator();
  
  console.log('Checking if addresses are role accounts:');
  for (const addr of systemAddresses) {
    const validationResult = await validator.validate(addr, { checkRole: true, checkMx: false });
    console.log(`  ${addr}: ${validationResult.details?.role ? 'Role account' : 'Not a role account'} (format valid: ${validationResult.details?.formatValid})`);
  }
  
  // Test that we don't expose information about which addresses exist
  console.log('\nTesting information disclosure prevention:');
  
  try {
    // Try sending to a non-existent address
    const testEmail = new Email({
      from: 'sender@example.com',
      to: ['definitely-does-not-exist-12345@example.com'],
      subject: 'Test',
      text: 'Test'
    });
    
    await smtpClient.sendMail(testEmail);
    console.log('Server accepted email (does not disclose non-existence)');
  } catch (error) {
    console.log('Server rejected email:', error.message);
  }
  
  console.log('\nSecurity best practice: Servers should not disclose address existence');
});

tap.test('CCMD-10: Validation during email sending', async () => {
  // Test that validation doesn't interfere with email sending
  
  console.log('Testing validation during email transaction:\n');
  
  const validator = new EmailValidator();
  
  // Create a series of emails with validation between them
  const emails = [
    {
      from: 'sender1@example.com',
      to: ['recipient1@example.com'],
      subject: 'First email',
      text: 'Testing validation during transaction'
    },
    {
      from: 'sender2@example.com',
      to: ['recipient2@example.com', 'recipient3@example.com'],
      subject: 'Second email',
      text: 'Multiple recipients'
    },
    {
      from: '"Test User" <sender3@example.com>',
      to: ['recipient4@example.com'],
      subject: 'Third email',
      text: 'Display name test'
    }
  ];
  
  for (let i = 0; i < emails.length; i++) {
    const emailData = emails[i];
    
    // Validate addresses before sending
    console.log(`Email ${i + 1}:`);
    const fromAddr = emailData.from.includes('<') ? emailData.from.match(/<([^>]+)>/)?.[1] || emailData.from : emailData.from;
    console.log(`  From: ${emailData.from} - Valid: ${validator.isValidFormat(fromAddr)}`);
    
    for (const to of emailData.to) {
      console.log(`  To: ${to} - Valid: ${validator.isValidFormat(to)}`);
    }
    
    // Create and send email
    const email = new Email(emailData);
    await smtpClient.sendMail(email);
    console.log(`  Sent successfully\n`);
  }
  
  console.log('All emails sent successfully with validation');
});

tap.test('CCMD-10: Special characters in email addresses', async () => {
  // Test email addresses with special characters
  
  console.log('Testing email addresses with special characters:\n');
  
  const validator = new EmailValidator();
  
  const specialAddresses = [
    { address: 'user+tag@example.com', shouldBeValid: true, description: 'Plus addressing' },
    { address: 'first.last@example.com', shouldBeValid: true, description: 'Dots in local part' },
    { address: 'user_name@example.com', shouldBeValid: true, description: 'Underscore' },
    { address: 'user-name@example.com', shouldBeValid: true, description: 'Hyphen' },
    { address: '"quoted string"@example.com', shouldBeValid: true, description: 'Quoted string' },
    { address: 'user@sub.domain.example.com', shouldBeValid: true, description: 'Subdomain' },
    { address: 'user@example.co.uk', shouldBeValid: true, description: 'Multi-part TLD' },
    { address: 'user..name@example.com', shouldBeValid: false, description: 'Double dots' },
    { address: '.user@example.com', shouldBeValid: false, description: 'Leading dot' },
    { address: 'user.@example.com', shouldBeValid: false, description: 'Trailing dot' }
  ];
  
  for (const test of specialAddresses) {
    const isValid = validator.isValidFormat(test.address);
    console.log(`${test.description}:`);
    console.log(`  Address: "${test.address}"`);
    console.log(`  Valid: ${isValid} (expected: ${test.shouldBeValid})`);
    
    if (test.shouldBeValid && isValid) {
      // Try sending an email with this address
      try {
        const email = new Email({
          from: 'sender@example.com',
          to: [test.address],
          subject: 'Special character test',
          text: `Testing special characters in: ${test.address}`
        });
        
        await smtpClient.sendMail(email);
        console.log(`  Email sent successfully`);
      } catch (error) {
        console.log(`  Failed to send: ${error.message}`);
      }
    }
    console.log('');
  }
});

tap.test('CCMD-10: Large recipient lists', async () => {
  // Test handling of large recipient lists (similar to EXPN multi-line)
  
  console.log('Testing large recipient lists:\n');
  
  // Create email with many recipients
  const recipientCount = 20;
  const toRecipients = [];
  const ccRecipients = [];
  
  for (let i = 1; i <= recipientCount; i++) {
    if (i <= 10) {
      toRecipients.push(`user${i}@example.com`);
    } else {
      ccRecipients.push(`user${i}@example.com`);
    }
  }
  
  console.log(`Creating email with ${recipientCount} total recipients:`);
  console.log(`  To: ${toRecipients.length} recipients`);
  console.log(`  CC: ${ccRecipients.length} recipients`);
  
  const largeListEmail = new Email({
    from: 'sender@example.com',
    to: toRecipients,
    cc: ccRecipients,
    subject: 'Large distribution list test',
    text: `This email is being sent to ${recipientCount} recipients total`
  });
  
  // Show extracted addresses
  const allTo = largeListEmail.getToAddresses();
  const allCc = largeListEmail.getCcAddresses();
  
  console.log('\nExtracted addresses:');
  console.log(`To (first 3): ${allTo.slice(0, 3).join(', ')}...`);
  console.log(`CC (first 3): ${allCc.slice(0, 3).join(', ')}...`);
  
  // Send the email
  const startTime = Date.now();
  await smtpClient.sendMail(largeListEmail);
  const elapsed = Date.now() - startTime;
  
  console.log(`\nEmail sent to all ${recipientCount} recipients in ${elapsed}ms`);
  console.log(`Average: ${(elapsed / recipientCount).toFixed(2)}ms per recipient`);
});

tap.test('CCMD-10: Email validation performance', async () => {
  // Test validation performance
  
  console.log('Testing email validation performance:\n');
  
  const validator = new EmailValidator();
  const testCount = 1000;
  
  // Generate test addresses
  const testAddresses = [];
  for (let i = 0; i < testCount; i++) {
    testAddresses.push(`user${i}@example${i % 10}.com`);
  }
  
  // Time validation
  const startTime = Date.now();
  let validCount = 0;
  
  for (const address of testAddresses) {
    if (validator.isValidFormat(address)) {
      validCount++;
    }
  }
  
  const elapsed = Date.now() - startTime;
  const rate = (testCount / elapsed) * 1000;
  
  console.log(`Validated ${testCount} addresses in ${elapsed}ms`);
  console.log(`Rate: ${rate.toFixed(0)} validations/second`);
  console.log(`Valid addresses: ${validCount}/${testCount}`);
  
  // Test rapid email sending to see if there's rate limiting
  console.log('\nTesting rapid email sending:');
  
  const emailCount = 10;
  const sendStartTime = Date.now();
  let sentCount = 0;
  
  for (let i = 0; i < emailCount; i++) {
    try {
      const email = new Email({
        from: 'sender@example.com',
        to: [`recipient${i}@example.com`],
        subject: `Rate test ${i + 1}`,
        text: 'Testing rate limits'
      });
      
      await smtpClient.sendMail(email);
      sentCount++;
    } catch (error) {
      console.log(`Rate limit hit at email ${i + 1}: ${error.message}`);
      break;
    }
  }
  
  const sendElapsed = Date.now() - sendStartTime;
  const sendRate = (sentCount / sendElapsed) * 1000;
  
  console.log(`Sent ${sentCount}/${emailCount} emails in ${sendElapsed}ms`);
  console.log(`Rate: ${sendRate.toFixed(2)} emails/second`);
});

tap.test('CCMD-10: Email validation error handling', async () => {
  // Test error handling for invalid email addresses
  
  console.log('Testing email validation error handling:\n');
  
  const validator = new EmailValidator();
  
  const errorTests = [
    { address: null, description: 'Null address' },
    { address: undefined, description: 'Undefined address' },
    { address: '', description: 'Empty string' },
    { address: ' ', description: 'Whitespace only' },
    { address: '@', description: 'Just @ symbol' },
    { address: 'user@', description: 'Missing domain' },
    { address: '@domain.com', description: 'Missing local part' },
    { address: 'user@@domain.com', description: 'Double @ symbol' },
    { address: 'user@domain@com', description: 'Multiple @ symbols' },
    { address: 'user space@domain.com', description: 'Space in local part' },
    { address: 'user@domain .com', description: 'Space in domain' },
    { address: 'x'.repeat(256) + '@domain.com', description: 'Very long local part' },
    { address: 'user@' + 'x'.repeat(256) + '.com', description: 'Very long domain' }
  ];
  
  for (const test of errorTests) {
    console.log(`${test.description}:`);
    console.log(`  Input: "${test.address}"`);
    
    // Test validation
    let isValid = false;
    try {
      isValid = validator.isValidFormat(test.address as any);
    } catch (error) {
      console.log(`  Validation threw: ${error.message}`);
    }
    
    if (!isValid) {
      console.log(`  Correctly rejected as invalid`);
    } else {
      console.log(`  WARNING: Accepted as valid!`);
    }
    
    // Try to send email with invalid address
    if (test.address) {
      try {
        const email = new Email({
          from: 'sender@example.com',
          to: [test.address],
          subject: 'Error test',
          text: 'Testing invalid address'
        });
        
        await smtpClient.sendMail(email);
        console.log(`  WARNING: Email sent with invalid address!`);
      } catch (error) {
        console.log(`  Email correctly rejected: ${error.message}`);
      }
    }
    console.log('');
  }
});

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

export default tap.start();