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 * as net from 'net';

let testServer: ITestServer;

tap.test('setup test SMTP server', async () => {
  testServer = await startTestServer({
    port: 2571,
    tlsEnabled: false,
    authRequired: false
  });
  expect(testServer).toBeTruthy();
  expect(testServer.port).toEqual(2571);
});

tap.test('CEDGE-02: Commands with extra spaces', async () => {
  // Create server that accepts commands with extra spaces
  const spaceyServer = net.createServer((socket) => {
    socket.write('220 mail.example.com ESMTP\r\n');
    let inData = false;
    
    socket.on('data', (data) => {
      const lines = data.toString().split('\r\n');
      
      lines.forEach(line => {
        if (!line && lines[lines.length - 1] === '') return; // Skip empty trailing line
        
        console.log(`Server received: "${line}"`);
        
        if (inData) {
          if (line === '.') {
            socket.write('250 Message accepted\r\n');
            inData = false;
          }
          // Otherwise it's email data, ignore
        } else if (line.match(/^EHLO\s+/i)) {
          socket.write('250-mail.example.com\r\n');
          socket.write('250 OK\r\n');
        } else if (line.match(/^MAIL\s+FROM:/i)) {
          socket.write('250 OK\r\n');
        } else if (line.match(/^RCPT\s+TO:/i)) {
          socket.write('250 OK\r\n');
        } else if (line === 'DATA') {
          socket.write('354 Start mail input\r\n');
          inData = true;
        } else if (line === 'QUIT') {
          socket.write('221 Bye\r\n');
          socket.end();
        } else if (line) {
          socket.write('500 Command not recognized\r\n');
        }
      });
    });
  });

  await new Promise<void>((resolve) => {
    spaceyServer.listen(0, '127.0.0.1', () => resolve());
  });

  const spaceyPort = (spaceyServer.address() as net.AddressInfo).port;

  const smtpClient = createSmtpClient({
    host: '127.0.0.1',
    port: spaceyPort,
    secure: false,
    connectionTimeout: 5000,
    debug: true
  });

  const verified = await smtpClient.verify();
  expect(verified).toBeTrue();

  const email = new Email({
    from: 'sender@example.com',
    to: ['recipient@example.com'],
    subject: 'Test with extra spaces',
    text: 'Testing command formatting'
  });

  const result = await smtpClient.sendMail(email);
  expect(result.success).toBeTrue();
  console.log('✅ Server handled commands with extra spaces');

  await smtpClient.close();
  spaceyServer.close();
});

tap.test('CEDGE-02: Mixed case commands', async () => {
  // Create server that accepts mixed case commands
  const mixedCaseServer = net.createServer((socket) => {
    socket.write('220 mail.example.com ESMTP\r\n');
    let inData = false;
    
    socket.on('data', (data) => {
      const lines = data.toString().split('\r\n');
      
      lines.forEach(line => {
        if (!line && lines[lines.length - 1] === '') return;
        
        const upperLine = line.toUpperCase();
        console.log(`Server received: "${line}"`);
        
        if (inData) {
          if (line === '.') {
            socket.write('250 Message accepted\r\n');
            inData = false;
          }
        } else if (upperLine.startsWith('EHLO')) {
          socket.write('250-mail.example.com\r\n');
          socket.write('250 8BITMIME\r\n');
        } else if (upperLine.startsWith('MAIL FROM:')) {
          socket.write('250 OK\r\n');
        } else if (upperLine.startsWith('RCPT TO:')) {
          socket.write('250 OK\r\n');
        } else if (upperLine === 'DATA') {
          socket.write('354 Start mail input\r\n');
          inData = true;
        } else if (upperLine === 'QUIT') {
          socket.write('221 Bye\r\n');
          socket.end();
        }
      });
    });
  });

  await new Promise<void>((resolve) => {
    mixedCaseServer.listen(0, '127.0.0.1', () => resolve());
  });

  const mixedPort = (mixedCaseServer.address() as net.AddressInfo).port;

  const smtpClient = createSmtpClient({
    host: '127.0.0.1',
    port: mixedPort,
    secure: false,
    connectionTimeout: 5000,
    debug: true
  });

  const verified = await smtpClient.verify();
  expect(verified).toBeTrue();
  console.log('✅ Server accepts mixed case commands');

  await smtpClient.close();
  mixedCaseServer.close();
});

tap.test('CEDGE-02: Commands with missing parameters', async () => {
  // Create server that handles incomplete commands
  const incompleteServer = net.createServer((socket) => {
    socket.write('220 mail.example.com ESMTP\r\n');
    
    socket.on('data', (data) => {
      const lines = data.toString().split('\r\n');
      
      lines.forEach(line => {
        if (!line && lines[lines.length - 1] === '') return;
        
        console.log(`Server received: "${line}"`);
        
        if (line.startsWith('EHLO')) {
          socket.write('250 OK\r\n');
        } else if (line === 'MAIL FROM:' || line === 'MAIL FROM') {
          // Missing email address
          socket.write('501 Syntax error in parameters\r\n');
        } else if (line === 'RCPT TO:' || line === 'RCPT TO') {
          // Missing recipient
          socket.write('501 Syntax error in parameters\r\n');
        } else if (line === 'QUIT') {
          socket.write('221 Bye\r\n');
          socket.end();
        } else if (line) {
          socket.write('500 Command not recognized\r\n');
        }
      });
    });
  });

  await new Promise<void>((resolve) => {
    incompleteServer.listen(0, '127.0.0.1', () => resolve());
  });

  const incompletePort = (incompleteServer.address() as net.AddressInfo).port;

  const smtpClient = createSmtpClient({
    host: '127.0.0.1',
    port: incompletePort,
    secure: false,
    connectionTimeout: 5000,
    debug: true
  });

  // This should succeed as the client sends proper commands
  const verified = await smtpClient.verify();
  expect(verified).toBeTrue();
  console.log('✅ Client sends properly formatted commands');

  await smtpClient.close();
  incompleteServer.close();
});

tap.test('CEDGE-02: Commands with extra parameters', async () => {
  // Create server that handles commands with extra parameters
  const extraParamsServer = net.createServer((socket) => {
    socket.write('220 mail.example.com ESMTP\r\n');
    let inData = false;
    
    socket.on('data', (data) => {
      const lines = data.toString().split('\r\n');
      
      lines.forEach(line => {
        if (!line && lines[lines.length - 1] === '') return;
        
        console.log(`Server received: "${line}"`);
        
        if (inData) {
          if (line === '.') {
            socket.write('250 Message accepted\r\n');
            inData = false;
          }
        } else if (line.startsWith('EHLO')) {
          // Accept EHLO with any parameter
          socket.write('250-mail.example.com\r\n');
          socket.write('250-SIZE 10240000\r\n');
          socket.write('250 8BITMIME\r\n');
        } else if (line.match(/^MAIL FROM:.*SIZE=/i)) {
          // Accept SIZE parameter
          socket.write('250 OK\r\n');
        } else if (line.startsWith('MAIL FROM:')) {
          socket.write('250 OK\r\n');
        } else if (line.startsWith('RCPT TO:')) {
          socket.write('250 OK\r\n');
        } else if (line === 'DATA') {
          socket.write('354 Start mail input\r\n');
          inData = true;
        } else if (line === 'QUIT') {
          socket.write('221 Bye\r\n');
          socket.end();
        }
      });
    });
  });

  await new Promise<void>((resolve) => {
    extraParamsServer.listen(0, '127.0.0.1', () => resolve());
  });

  const extraPort = (extraParamsServer.address() as net.AddressInfo).port;

  const smtpClient = createSmtpClient({
    host: '127.0.0.1',
    port: extraPort,
    secure: false,
    connectionTimeout: 5000,
    debug: true
  });

  const email = new Email({
    from: 'sender@example.com',
    to: ['recipient@example.com'],
    subject: 'Test with parameters',
    text: 'Testing extra parameters'
  });

  const result = await smtpClient.sendMail(email);
  expect(result.success).toBeTrue();
  console.log('✅ Server handled commands with extra parameters');

  await smtpClient.close();
  extraParamsServer.close();
});

tap.test('CEDGE-02: Invalid command sequences', async () => {
  // Create server that enforces command sequence
  const sequenceServer = net.createServer((socket) => {
    socket.write('220 mail.example.com ESMTP\r\n');
    let state = 'GREETING';
    
    socket.on('data', (data) => {
      const lines = data.toString().split('\r\n');
      
      lines.forEach(line => {
        if (!line && lines[lines.length - 1] === '') return;
        
        console.log(`Server received: "${line}" in state ${state}`);
        
        if (state === 'DATA' && line !== '.') {
          // In DATA state, ignore everything except the terminating period
          return;
        }
        
        if (line.startsWith('EHLO') || line.startsWith('HELO')) {
          state = 'READY';
          socket.write('250 OK\r\n');
        } else if (line.startsWith('MAIL FROM:')) {
          if (state !== 'READY') {
            socket.write('503 Bad sequence of commands\r\n');
          } else {
            state = 'MAIL';
            socket.write('250 OK\r\n');
          }
        } else if (line.startsWith('RCPT TO:')) {
          if (state !== 'MAIL' && state !== 'RCPT') {
            socket.write('503 Bad sequence of commands\r\n');
          } else {
            state = 'RCPT';
            socket.write('250 OK\r\n');
          }
        } else if (line === 'DATA') {
          if (state !== 'RCPT') {
            socket.write('503 Bad sequence of commands\r\n');
          } else {
            state = 'DATA';
            socket.write('354 Start mail input\r\n');
          }
        } else if (line === '.' && state === 'DATA') {
          state = 'READY';
          socket.write('250 Message accepted\r\n');
        } else if (line === 'QUIT') {
          socket.write('221 Bye\r\n');
          socket.end();
        } else if (line === 'RSET') {
          state = 'READY';
          socket.write('250 OK\r\n');
        }
      });
    });
  });

  await new Promise<void>((resolve) => {
    sequenceServer.listen(0, '127.0.0.1', () => resolve());
  });

  const sequencePort = (sequenceServer.address() as net.AddressInfo).port;

  const smtpClient = createSmtpClient({
    host: '127.0.0.1',
    port: sequencePort,
    secure: false,
    connectionTimeout: 5000,
    debug: true
  });

  // Client should handle proper command sequencing
  const email = new Email({
    from: 'sender@example.com',
    to: ['recipient@example.com'],
    subject: 'Test sequence',
    text: 'Testing command sequence'
  });

  const result = await smtpClient.sendMail(email);
  expect(result.success).toBeTrue();
  console.log('✅ Client maintains proper command sequence');

  await smtpClient.close();
  sequenceServer.close();
});

tap.test('CEDGE-02: Malformed email addresses', async () => {
  // Test how client handles various email formats
  const emailServer = net.createServer((socket) => {
    socket.write('220 mail.example.com ESMTP\r\n');
    let inData = false;
    
    socket.on('data', (data) => {
      const lines = data.toString().split('\r\n');
      
      lines.forEach(line => {
        if (!line && lines[lines.length - 1] === '') return;
        
        console.log(`Server received: "${line}"`);
        
        if (inData) {
          if (line === '.') {
            socket.write('250 Message accepted\r\n');
            inData = false;
          }
        } else if (line.startsWith('EHLO')) {
          socket.write('250 OK\r\n');
        } else if (line.startsWith('MAIL FROM:')) {
          // Accept any sender format
          socket.write('250 OK\r\n');
        } else if (line.startsWith('RCPT TO:')) {
          // Accept any recipient format
          socket.write('250 OK\r\n');
        } else if (line === 'DATA') {
          socket.write('354 Start mail input\r\n');
          inData = true;
        } else if (line === 'QUIT') {
          socket.write('221 Bye\r\n');
          socket.end();
        }
      });
    });
  });

  await new Promise<void>((resolve) => {
    emailServer.listen(0, '127.0.0.1', () => resolve());
  });

  const emailPort = (emailServer.address() as net.AddressInfo).port;

  const smtpClient = createSmtpClient({
    host: '127.0.0.1',
    port: emailPort,
    secure: false,
    connectionTimeout: 5000,
    debug: true
  });

  // Test with properly formatted email
  const email = new Email({
    from: 'sender@example.com',
    to: ['recipient@example.com'],
    subject: 'Test email formats',
    text: 'Testing email address handling'
  });

  const result = await smtpClient.sendMail(email);
  expect(result.success).toBeTrue();
  console.log('✅ Client properly formats email addresses');

  await smtpClient.close();
  emailServer.close();
});

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

export default tap.start();