import { tap, expect } from '@git.zone/tstest/tapbundle';
import { BounceManager, BounceType, BounceCategory } from '../ts/mail/core/classes.bouncemanager.js';
import { Email } from '../ts/mail/core/classes.email.js';

/**
 * Test the BounceManager class
 */
tap.test('BounceManager - should be instantiable', async () => {
  const bounceManager = new BounceManager();
  expect(bounceManager).toBeTruthy();
});

tap.test('BounceManager - should process basic bounce categories', async () => {
  const bounceManager = new BounceManager();
  
  // Test hard bounce detection
  const hardBounce = await bounceManager.processBounce({
    recipient: 'invalid@example.com',
    sender: 'sender@example.com',
    smtpResponse: 'user unknown',
    domain: 'example.com'
  });
  
  expect(hardBounce.bounceCategory).toEqual(BounceCategory.HARD);
  
  // Test soft bounce detection
  const softBounce = await bounceManager.processBounce({
    recipient: 'valid@example.com',
    sender: 'sender@example.com',
    smtpResponse: 'server unavailable',
    domain: 'example.com'
  });
  
  expect(softBounce.bounceCategory).toEqual(BounceCategory.SOFT);
  
  // Test auto-response detection
  const autoResponse = await bounceManager.processBounce({
    recipient: 'away@example.com',
    sender: 'sender@example.com',
    smtpResponse: 'auto-reply: out of office',
    domain: 'example.com'
  });
  
  expect(autoResponse.bounceCategory).toEqual(BounceCategory.AUTO_RESPONSE);
});

tap.test('BounceManager - should add and check suppression list entries', async () => {
  const bounceManager = new BounceManager();
  
  // Add to suppression list permanently
  bounceManager.addToSuppressionList('permanent@example.com', 'Test hard bounce', undefined);
  
  // Add to suppression list temporarily (5 seconds)
  const expireTime = Date.now() + 5000;
  bounceManager.addToSuppressionList('temporary@example.com', 'Test soft bounce', expireTime);
  
  // Check suppression status
  expect(bounceManager.isEmailSuppressed('permanent@example.com')).toEqual(true);
  expect(bounceManager.isEmailSuppressed('temporary@example.com')).toEqual(true);
  expect(bounceManager.isEmailSuppressed('notsuppressed@example.com')).toEqual(false);
  
  // Get suppression info
  const info = bounceManager.getSuppressionInfo('permanent@example.com');
  expect(info).toBeTruthy();
  expect(info.reason).toEqual('Test hard bounce');
  expect(info.expiresAt).toBeUndefined();

  // Verify temporary suppression info
  const tempInfo = bounceManager.getSuppressionInfo('temporary@example.com');
  expect(tempInfo).toBeTruthy();
  expect(tempInfo.reason).toEqual('Test soft bounce');
  expect(tempInfo.expiresAt).toEqual(expireTime);
  
  // Wait for expiration (6 seconds)
  await new Promise(resolve => setTimeout(resolve, 6000));
  
  // Verify permanent suppression is still active
  expect(bounceManager.isEmailSuppressed('permanent@example.com')).toEqual(true);
  
  // Verify temporary suppression has expired
  expect(bounceManager.isEmailSuppressed('temporary@example.com')).toEqual(false);
});

tap.test('BounceManager - should process SMTP failures correctly', async () => {
  const bounceManager = new BounceManager();
  
  const result = await bounceManager.processSmtpFailure(
    'recipient@example.com',
    '550 5.1.1 User unknown',
    {
      sender: 'sender@example.com',
      statusCode: '550'
    }
  );
  
  expect(result.bounceType).toEqual(BounceType.INVALID_RECIPIENT);
  expect(result.bounceCategory).toEqual(BounceCategory.HARD);
  
  // Check that the email was added to the suppression list
  expect(bounceManager.isEmailSuppressed('recipient@example.com')).toEqual(true);
});

tap.test('BounceManager - should process bounce emails correctly', async () => {
  const bounceManager = new BounceManager();
  
  // Create a mock bounce email
  const bounceEmail = new Email({
    from: 'mailer-daemon@example.com',
    subject: 'Mail delivery failed: returning message to sender',
    text: `
      This message was created automatically by mail delivery software.
      
      A message that you sent could not be delivered to one or more of its recipients.
      The following address(es) failed:
      
      recipient@example.com
      mailbox is full
      
      ------ This is a copy of the message, including all the headers. ------
      
      Original-Recipient: rfc822;recipient@example.com
      Final-Recipient: rfc822;recipient@example.com
      Status: 5.2.2
      diagnostic-code: smtp; 552 5.2.2 Mailbox full
    `,
    to: 'sender@example.com' // Bounce emails are typically sent back to the original sender
  });
  
  const result = await bounceManager.processBounceEmail(bounceEmail);
  
  expect(result).toBeTruthy();
  expect(result.bounceType).toEqual(BounceType.MAILBOX_FULL);
  expect(result.bounceCategory).toEqual(BounceCategory.HARD);
  expect(result.recipient).toEqual('recipient@example.com');
});

tap.test('BounceManager - should handle retries for soft bounces', async () => {
  const bounceManager = new BounceManager({
    retryStrategy: {
      maxRetries: 2,
      initialDelay: 100, // 100ms for test
      maxDelay: 1000,
      backoffFactor: 2
    }
  });
  
  // First attempt
  const result1 = await bounceManager.processBounce({
    recipient: 'retry@example.com',
    sender: 'sender@example.com',
    bounceType: BounceType.SERVER_UNAVAILABLE,
    bounceCategory: BounceCategory.SOFT,
    domain: 'example.com'
  });
  
  // Email should be suppressed temporarily
  expect(bounceManager.isEmailSuppressed('retry@example.com')).toEqual(true);
  expect(result1.retryCount).toEqual(1);
  expect(result1.nextRetryTime).toBeGreaterThan(Date.now());
  
  // Second attempt
  const result2 = await bounceManager.processBounce({
    recipient: 'retry@example.com',
    sender: 'sender@example.com',
    bounceType: BounceType.SERVER_UNAVAILABLE,
    bounceCategory: BounceCategory.SOFT,
    domain: 'example.com',
    retryCount: 1
  });
  
  expect(result2.retryCount).toEqual(2);
  
  // Third attempt (should convert to hard bounce)
  const result3 = await bounceManager.processBounce({
    recipient: 'retry@example.com',
    sender: 'sender@example.com',
    bounceType: BounceType.SERVER_UNAVAILABLE,
    bounceCategory: BounceCategory.SOFT,
    domain: 'example.com',
    retryCount: 2
  });
  
  // Should now be a hard bounce after max retries
  expect(result3.bounceCategory).toEqual(BounceCategory.HARD);
  
  // Email should be suppressed permanently
  expect(bounceManager.isEmailSuppressed('retry@example.com')).toEqual(true);
  const info = bounceManager.getSuppressionInfo('retry@example.com');
  expect(info.expiresAt).toBeUndefined(); // Permanent
});

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

export default tap.start();