import { tap, expect } from '@git.zone/tstest/tapbundle';
import * as net from 'net';
import { startTestServer, stopTestServer } from '../../helpers/server.loader.ts'
import type { ITestServer } from '../../helpers/server.loader.js';
// Test configuration
const TEST_PORT = 2525;
const TEST_TIMEOUT = 15000;

let testServer: ITestServer;

// Setup
tap.test('setup - start SMTP server', async () => {
  testServer = await startTestServer({ port: TEST_PORT });
  await new Promise(resolve => setTimeout(resolve, 1000));
});

// Test: Complete email sending flow
tap.test('Basic Email Sending - should send email through complete SMTP flow', async (tools) => {
  const done = tools.defer();
  
  const socket = net.createConnection({
    host: 'localhost',
    port: TEST_PORT,
    timeout: TEST_TIMEOUT
  });
  
  let receivedData = '';
  let currentStep = 'connecting';
  const fromAddress = 'sender@example.com';
  const toAddress = 'recipient@example.com';
  const emailContent = `Subject: Production Test Email\r\nFrom: ${fromAddress}\r\nTo: ${toAddress}\r\nDate: ${new Date().toUTCString()}\r\n\r\nThis is a test email sent during production testing.\r\nTest ID: EP-01\r\nTimestamp: ${Date.now()}\r\n`;
  
  const steps: string[] = [];
  
  socket.on('data', (data) => {
    receivedData += data.toString();
    
    if (currentStep === 'connecting' && receivedData.includes('220')) {
      steps.push('CONNECT');
      currentStep = 'ehlo';
      socket.write('EHLO test.example.com\r\n');
    } else if (currentStep === 'ehlo' && receivedData.includes('250')) {
      steps.push('EHLO');
      currentStep = 'mail_from';
      socket.write(`MAIL FROM:<${fromAddress}>\r\n`);
    } else if (currentStep === 'mail_from' && receivedData.includes('250')) {
      steps.push('MAIL FROM');
      currentStep = 'rcpt_to';
      socket.write(`RCPT TO:<${toAddress}>\r\n`);
    } else if (currentStep === 'rcpt_to' && receivedData.includes('250')) {
      steps.push('RCPT TO');
      currentStep = 'data';
      socket.write('DATA\r\n');
    } else if (currentStep === 'data' && receivedData.includes('354')) {
      steps.push('DATA');
      currentStep = 'email_content';
      socket.write(emailContent);
      socket.write('\r\n.\r\n'); // End of data marker
    } else if (currentStep === 'email_content' && receivedData.includes('250')) {
      steps.push('CONTENT');
      currentStep = 'quit';
      socket.write('QUIT\r\n');
    } else if (currentStep === 'quit' && receivedData.includes('221')) {
      steps.push('QUIT');
      socket.destroy();
      
      // Verify all steps completed
      expect(steps).toInclude('CONNECT');
      expect(steps).toInclude('EHLO');
      expect(steps).toInclude('MAIL FROM');
      expect(steps).toInclude('RCPT TO');
      expect(steps).toInclude('DATA');
      expect(steps).toInclude('CONTENT');
      expect(steps).toInclude('QUIT');
      expect(steps.length).toEqual(7);
      
      done.resolve();
    } else if (receivedData.match(/\r\n5\d{2}\s/)) {
      // Server error (5xx response codes)
      socket.destroy();
      done.reject(new Error(`Email sending failed at step ${currentStep}: ${receivedData}`));
    }
  });
  
  socket.on('error', (error) => {
    done.reject(error);
  });
  
  socket.on('timeout', () => {
    socket.destroy();
    done.reject(new Error(`Connection timeout at step: ${currentStep}`));
  });
  
  await done.promise;
});

// Test: Send email with attachments (MIME)
tap.test('Basic Email Sending - should send email with MIME attachment', async (tools) => {
  const done = tools.defer();
  
  const socket = net.createConnection({
    host: 'localhost',
    port: TEST_PORT,
    timeout: TEST_TIMEOUT
  });
  
  let receivedData = '';
  let currentStep = 'connecting';
  const fromAddress = 'sender@example.com';
  const toAddress = 'recipient@example.com';
  const boundary = '----=_Part_0_1234567890';
  
  const emailContent = `Subject: Email with Attachment\r\nFrom: ${fromAddress}\r\nTo: ${toAddress}\r\nMIME-Version: 1.0\r\nContent-Type: multipart/mixed; boundary="${boundary}"\r\n\r\n--${boundary}\r\nContent-Type: text/plain; charset=UTF-8\r\n\r\nThis email contains an attachment.\r\n\r\n--${boundary}\r\nContent-Type: text/plain; name="test.txt"\r\nContent-Disposition: attachment; filename="test.txt"\r\nContent-Transfer-Encoding: base64\r\n\r\nVGhpcyBpcyBhIHRlc3QgZmlsZS4=\r\n\r\n--${boundary}--\r\n`;
  
  socket.on('data', (data) => {
    receivedData += data.toString();
    
    if (currentStep === 'connecting' && receivedData.includes('220')) {
      currentStep = 'ehlo';
      socket.write('EHLO test.example.com\r\n');
    } else if (currentStep === 'ehlo' && receivedData.includes('250')) {
      currentStep = 'mail_from';
      socket.write(`MAIL FROM:<${fromAddress}>\r\n`);
    } else if (currentStep === 'mail_from' && receivedData.includes('250')) {
      currentStep = 'rcpt_to';
      socket.write(`RCPT TO:<${toAddress}>\r\n`);
    } else if (currentStep === 'rcpt_to' && receivedData.includes('250')) {
      currentStep = 'data';
      socket.write('DATA\r\n');
    } else if (currentStep === 'data' && receivedData.includes('354')) {
      currentStep = 'email_content';
      socket.write(emailContent);
      socket.write('\r\n.\r\n'); // End of data marker
    } else if (currentStep === 'email_content' && receivedData.includes('250')) {
      socket.write('QUIT\r\n');
      setTimeout(() => {
        socket.destroy();
        expect(receivedData).toInclude('250');
        done.resolve();
      }, 100);
    }
  });
  
  socket.on('error', (error) => {
    done.reject(error);
  });
  
  socket.on('timeout', () => {
    socket.destroy();
    done.reject(new Error(`Connection timeout at step: ${currentStep}`));
  });
  
  await done.promise;
});

// Test: Send HTML email
tap.test('Basic Email Sending - should send HTML email', async (tools) => {
  const done = tools.defer();
  
  const socket = net.createConnection({
    host: 'localhost',
    port: TEST_PORT,
    timeout: TEST_TIMEOUT
  });
  
  let receivedData = '';
  let currentStep = 'connecting';
  const fromAddress = 'sender@example.com';
  const toAddress = 'recipient@example.com';
  const boundary = '----=_Part_0_987654321';
  
  const emailContent = `Subject: HTML Email Test\r\nFrom: ${fromAddress}\r\nTo: ${toAddress}\r\nMIME-Version: 1.0\r\nContent-Type: multipart/alternative; boundary="${boundary}"\r\n\r\n--${boundary}\r\nContent-Type: text/plain; charset=UTF-8\r\n\r\nThis is the plain text version.\r\n\r\n--${boundary}\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n<html><body><h1>HTML Email</h1><p>This is the <strong>HTML</strong> version.</p></body></html>\r\n\r\n--${boundary}--\r\n`;
  
  socket.on('data', (data) => {
    receivedData += data.toString();
    
    if (currentStep === 'connecting' && receivedData.includes('220')) {
      currentStep = 'ehlo';
      socket.write('EHLO test.example.com\r\n');
    } else if (currentStep === 'ehlo' && receivedData.includes('250')) {
      currentStep = 'mail_from';
      socket.write(`MAIL FROM:<${fromAddress}>\r\n`);
    } else if (currentStep === 'mail_from' && receivedData.includes('250')) {
      currentStep = 'rcpt_to';
      socket.write(`RCPT TO:<${toAddress}>\r\n`);
    } else if (currentStep === 'rcpt_to' && receivedData.includes('250')) {
      currentStep = 'data';
      socket.write('DATA\r\n');
    } else if (currentStep === 'data' && receivedData.includes('354')) {
      currentStep = 'email_content';
      socket.write(emailContent);
      socket.write('\r\n.\r\n'); // End of data marker
    } else if (currentStep === 'email_content' && receivedData.includes('250')) {
      socket.write('QUIT\r\n');
      setTimeout(() => {
        socket.destroy();
        expect(receivedData).toInclude('250');
        done.resolve();
      }, 100);
    }
  });
  
  socket.on('error', (error) => {
    done.reject(error);
  });
  
  socket.on('timeout', () => {
    socket.destroy();
    done.reject(new Error(`Connection timeout at step: ${currentStep}`));
  });
  
  await done.promise;
});

// Test: Send email with custom headers
tap.test('Basic Email Sending - should send email with custom headers', async (tools) => {
  const done = tools.defer();
  
  const socket = net.createConnection({
    host: 'localhost',
    port: TEST_PORT,
    timeout: TEST_TIMEOUT
  });
  
  let receivedData = '';
  let currentStep = 'connecting';
  const fromAddress = 'sender@example.com';
  const toAddress = 'recipient@example.com';
  
  const emailContent = `Subject: Custom Headers Test\r\nFrom: ${fromAddress}\r\nTo: ${toAddress}\r\nX-Custom-Header: CustomValue\r\nX-Priority: 1\r\nX-Mailer: SMTP Test Suite\r\nReply-To: noreply@example.com\r\nOrganization: Test Organization\r\n\r\nThis email contains custom headers.\r\n`;
  
  socket.on('data', (data) => {
    receivedData += data.toString();
    
    if (currentStep === 'connecting' && receivedData.includes('220')) {
      currentStep = 'ehlo';
      socket.write('EHLO test.example.com\r\n');
    } else if (currentStep === 'ehlo' && receivedData.includes('250')) {
      currentStep = 'mail_from';
      socket.write(`MAIL FROM:<${fromAddress}>\r\n`);
    } else if (currentStep === 'mail_from' && receivedData.includes('250')) {
      currentStep = 'rcpt_to';
      socket.write(`RCPT TO:<${toAddress}>\r\n`);
    } else if (currentStep === 'rcpt_to' && receivedData.includes('250')) {
      currentStep = 'data';
      socket.write('DATA\r\n');
    } else if (currentStep === 'data' && receivedData.includes('354')) {
      currentStep = 'email_content';
      socket.write(emailContent);
      socket.write('\r\n.\r\n'); // End of data marker
    } else if (currentStep === 'email_content' && receivedData.includes('250')) {
      socket.write('QUIT\r\n');
      setTimeout(() => {
        socket.destroy();
        expect(receivedData).toInclude('250');
        done.resolve();
      }, 100);
    }
  });
  
  socket.on('error', (error) => {
    done.reject(error);
  });
  
  socket.on('timeout', () => {
    socket.destroy();
    done.reject(new Error(`Connection timeout at step: ${currentStep}`));
  });
  
  await done.promise;
});

// Test: Minimal email (only required headers)
tap.test('Basic Email Sending - should send minimal email', async (tools) => {
  const done = tools.defer();
  
  const socket = net.createConnection({
    host: 'localhost',
    port: TEST_PORT,
    timeout: TEST_TIMEOUT
  });
  
  let receivedData = '';
  let currentStep = 'connecting';
  const fromAddress = 'sender@example.com';
  const toAddress = 'recipient@example.com';
  
  // Minimal email - just a body, no headers
  const emailContent = 'This is a minimal email with no headers.\r\n';
  
  socket.on('data', (data) => {
    receivedData += data.toString();
    
    if (currentStep === 'connecting' && receivedData.includes('220')) {
      currentStep = 'ehlo';
      socket.write('EHLO test.example.com\r\n');
    } else if (currentStep === 'ehlo' && receivedData.includes('250')) {
      currentStep = 'mail_from';
      socket.write(`MAIL FROM:<${fromAddress}>\r\n`);
    } else if (currentStep === 'mail_from' && receivedData.includes('250')) {
      currentStep = 'rcpt_to';
      socket.write(`RCPT TO:<${toAddress}>\r\n`);
    } else if (currentStep === 'rcpt_to' && receivedData.includes('250')) {
      currentStep = 'data';
      socket.write('DATA\r\n');
    } else if (currentStep === 'data' && receivedData.includes('354')) {
      currentStep = 'email_content';
      socket.write(emailContent);
      socket.write('\r\n.\r\n'); // End of data marker
    } else if (currentStep === 'email_content' && receivedData.includes('250')) {
      socket.write('QUIT\r\n');
      setTimeout(() => {
        socket.destroy();
        expect(receivedData).toInclude('250');
        done.resolve();
      }, 100);
    }
  });
  
  socket.on('error', (error) => {
    done.reject(error);
  });
  
  socket.on('timeout', () => {
    socket.destroy();
    done.reject(new Error(`Connection timeout at step: ${currentStep}`));
  });
  
  await done.promise;
});

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

// Start the test
export default tap.start();