import { tap, expect } from '@push.rocks/tapbundle';
import * as plugins from '../ts/plugins.js';
import * as paths from '../ts/paths.js';

// Import the components we want to test
import { EmailValidator } from '../ts/mail/core/classes.emailvalidator.js';
import { TemplateManager } from '../ts/mail/core/classes.templatemanager.js';
import { Email } from '../ts/mail/core/classes.email.js';

// Ensure test directories exist
paths.ensureDirectories();

tap.test('EmailValidator - should validate email formats correctly', async (tools) => {
  const validator = new EmailValidator();
  
  // Test valid email formats
  expect(validator.isValidFormat('user@example.com')).toBeTrue();
  expect(validator.isValidFormat('firstname.lastname@example.com')).toBeTrue();
  expect(validator.isValidFormat('user+tag@example.com')).toBeTrue();
  
  // Test invalid email formats
  expect(validator.isValidFormat('user@')).toBeFalse();
  expect(validator.isValidFormat('@example.com')).toBeFalse();
  expect(validator.isValidFormat('user@example')).toBeFalse();
  expect(validator.isValidFormat('user.example.com')).toBeFalse();
});

tap.test('EmailValidator - should perform comprehensive validation', async (tools) => {
  const validator = new EmailValidator();
  
  // Test basic validation (syntax-only)
  const basicResult = await validator.validate('user@example.com', { checkSyntaxOnly: true });
  expect(basicResult.isValid).toBeTrue();
  expect(basicResult.details.formatValid).toBeTrue();
  
  // We can't reliably test MX validation in all environments, but the function should run
  const mxResult = await validator.validate('user@example.com', { checkMx: true });
  expect(typeof mxResult.isValid).toEqual('boolean');
  expect(typeof mxResult.hasMx).toEqual('boolean');
});

tap.test('EmailValidator - should detect invalid emails', async (tools) => {
  const validator = new EmailValidator();
  
  const invalidResult = await validator.validate('invalid@@example.com', { checkSyntaxOnly: true });
  expect(invalidResult.isValid).toBeFalse();
  expect(invalidResult.details.formatValid).toBeFalse();
});

tap.test('TemplateManager - should register and retrieve templates', async (tools) => {
  const templateManager = new TemplateManager({
    from: 'test@example.com'
  });
  
  // Register a custom template
  templateManager.registerTemplate({
    id: 'test-template',
    name: 'Test Template',
    description: 'A test template',
    from: 'test@example.com',
    subject: 'Test Subject: {{name}}',
    bodyHtml: '<p>Hello, {{name}}!</p>',
    bodyText: 'Hello, {{name}}!',
    category: 'test'
  });
  
  // Get the template back
  const template = templateManager.getTemplate('test-template');
  expect(template).toBeTruthy();
  expect(template.id).toEqual('test-template');
  expect(template.subject).toEqual('Test Subject: {{name}}');
  
  // List templates
  const templates = templateManager.listTemplates();
  expect(templates.length > 0).toBeTrue();
  expect(templates.some(t => t.id === 'test-template')).toBeTrue();
});

tap.test('TemplateManager - should create smartmail from template', async (tools) => {
  const templateManager = new TemplateManager({
    from: 'test@example.com'
  });
  
  // Register a template
  templateManager.registerTemplate({
    id: 'welcome-test',
    name: 'Welcome Test',
    description: 'A welcome test template',
    from: 'welcome@example.com',
    subject: 'Welcome, {{name}}!',
    bodyHtml: '<p>Hello, {{name}}! Welcome to our service.</p>',
    bodyText: 'Hello, {{name}}! Welcome to our service.',
    category: 'test'
  });
  
  // Create smartmail from template
  const smartmail = await templateManager.createSmartmail('welcome-test', {
    name: 'John Doe'
  });
  
  expect(smartmail).toBeTruthy();
  expect(smartmail.options.from).toEqual('welcome@example.com');
  expect(smartmail.getSubject()).toEqual('Welcome, John Doe!');
  expect(smartmail.getBody(true).indexOf('Hello, John Doe!') > -1).toBeTrue();
});

tap.test('Email - should handle template variables', async (tools) => {
  // Create email with variables
  const email = new Email({
    from: 'sender@example.com',
    to: 'recipient@example.com',
    subject: 'Hello {{name}}!',
    text: 'Welcome, {{name}}! Your order #{{orderId}} has been processed.',
    html: '<p>Welcome, <strong>{{name}}</strong>! Your order #{{orderId}} has been processed.</p>',
    variables: {
      name: 'John Doe',
      orderId: '12345'
    }
  });
  
  // Test variable substitution
  expect(email.getSubjectWithVariables()).toEqual('Hello John Doe!');
  expect(email.getTextWithVariables()).toEqual('Welcome, John Doe! Your order #12345 has been processed.');
  expect(email.getHtmlWithVariables().indexOf('<strong>John Doe</strong>') > -1).toBeTrue();
  
  // Test with additional variables
  const additionalVars = {
    name: 'Jane Smith', // Override existing variable
    status: 'shipped'   // Add new variable
  };
  
  expect(email.getSubjectWithVariables(additionalVars)).toEqual('Hello Jane Smith!');
  
  // Add a new variable
  email.setVariable('trackingNumber', 'TRK123456');
  expect(email.getTextWithVariables().indexOf('12345') > -1).toBeTrue();
  
  // Update multiple variables at once
  email.setVariables({
    orderId: '67890',
    status: 'delivered'
  });
  
  expect(email.getTextWithVariables().indexOf('67890') > -1).toBeTrue();
});

tap.test('Email and Smartmail compatibility - should convert between formats', async (tools) => {
  // Create a Smartmail instance
  const smartmail = new plugins.smartmail.Smartmail({
    from: 'smartmail@example.com',
    subject: 'Test Subject',
    body: '<p>This is a test email.</p>',
    creationObjectRef: {
      orderId: '12345'
    }
  });
  
  // Add recipient and attachment
  smartmail.addRecipient('recipient@example.com');
  
  const attachment = await plugins.smartfile.SmartFile.fromString(
    'test.txt',
    'This is a test attachment',
    'utf8',
  );
  
  smartmail.addAttachment(attachment);
  
  // Convert to Email
  const resolvedSmartmail = await smartmail;
  const email = Email.fromSmartmail(resolvedSmartmail);
  
  // Verify first conversion (Smartmail to Email)
  expect(email.from).toEqual('smartmail@example.com');
  expect(email.to.indexOf('recipient@example.com') > -1).toBeTrue();
  expect(email.subject).toEqual('Test Subject');
  expect(email.html?.indexOf('This is a test email') > -1).toBeTrue();
  expect(email.attachments.length).toEqual(1);
  
  // Convert back to Smartmail
  const convertedSmartmail = await email.toSmartmail();
  
  // Verify second conversion (Email back to Smartmail) with simplified assertions
  expect(convertedSmartmail.options.from).toEqual('smartmail@example.com');
  expect(Array.isArray(convertedSmartmail.options.to)).toBeTrue();
  expect(convertedSmartmail.options.to.length).toEqual(1);
  expect(convertedSmartmail.getSubject()).toEqual('Test Subject');
  expect(convertedSmartmail.getBody(true).indexOf('This is a test email') > -1).toBeTrue();
  expect(convertedSmartmail.attachments.length).toEqual(1);
});

tap.test('Email - should validate email addresses', async (tools) => {
  // Attempt to create an email with invalid addresses
  let errorThrown = false;
  
  try {
    const email = new Email({
      from: 'invalid-email',
      to: 'recipient@example.com',
      subject: 'Test',
      text: 'Test'
    });
  } catch (error) {
    errorThrown = true;
    expect(error.message.indexOf('Invalid sender email address') > -1).toBeTrue();
  }
  
  expect(errorThrown).toBeTrue();
  
  // Attempt with invalid recipient
  errorThrown = false;
  
  try {
    const email = new Email({
      from: 'sender@example.com',
      to: 'invalid-recipient',
      subject: 'Test',
      text: 'Test'
    });
  } catch (error) {
    errorThrown = true;
    expect(error.message.indexOf('Invalid recipient email address') > -1).toBeTrue();
  }
  
  expect(errorThrown).toBeTrue();
  
  // Valid email should not throw
  let validEmail: Email;
  try {
    validEmail = new Email({
      from: 'sender@example.com',
      to: 'recipient@example.com',
      subject: 'Test',
      text: 'Test'
    });
    
    expect(validEmail).toBeTruthy();
    expect(validEmail.from).toEqual('sender@example.com');
  } catch (error) {
    expect(error === undefined).toBeTrue(); // This should not happen
  }
});

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

export default tap.start();