import { tap, expect } from '@git.zone/tstest/tapbundle';
import * as net from 'net';
import { startTestServer, stopTestServer } from '../../helpers/server.loader.js';

const TEST_PORT = 2525;

let testServer;
const TEST_TIMEOUT = 10000;

tap.test('prepare server', async () => {
  testServer = await startTestServer({ port: TEST_PORT });
  await new Promise(resolve => setTimeout(resolve, 100));
});

tap.test('CMD-01: EHLO Command - server responds with proper capabilities', async (tools) => {
  const done = tools.defer();
  
  const socket = net.createConnection({
    host: 'localhost',
    port: TEST_PORT,
    timeout: TEST_TIMEOUT
  });
  
  let receivedData = '';
  let currentStep = 'connecting';
  
  socket.on('data', (data) => {
    receivedData += data.toString();
    
    if (currentStep === 'connecting' && receivedData.includes('220')) {
      currentStep = 'ehlo';
      receivedData = ''; // Clear buffer
      socket.write('EHLO test.example.com\r\n');
    } else if (currentStep === 'ehlo' && receivedData.includes('250 ')) {
      // Parse response - only lines that start with 250
      const lines = receivedData.split('\r\n')
        .filter(line => line.startsWith('250'))
        .filter(line => line.length > 0);
      
      // Check for required ESMTP extensions
      const capabilities = lines.map(line => line.substring(4).trim());
      console.log('📋 Server capabilities:', capabilities);
      
      // Verify essential capabilities
      expect(capabilities.some(cap => cap.includes('SIZE'))).toBeTruthy();
      expect(capabilities.some(cap => cap.includes('8BITMIME'))).toBeTruthy();
      
      // The last line should be "250 " (without hyphen)
      const lastLine = lines[lines.length - 1];
      expect(lastLine.startsWith('250 ')).toBeTruthy();
      
      currentStep = 'quit';
      receivedData = ''; // Clear buffer
      socket.write('QUIT\r\n');
    } else if (currentStep === 'quit' && receivedData.includes('221')) {
      socket.destroy();
      done.resolve();
    }
  });
  
  socket.on('error', (error) => {
    done.reject(error);
  });
  
  socket.on('timeout', () => {
    socket.destroy();
    done.reject(new Error(`Connection timeout at step: ${currentStep}`));
  });
  
  await done.promise;
});

tap.test('CMD-01: EHLO with invalid hostname - server handles gracefully', async (tools) => {
  const done = tools.defer();
  
  const socket = net.createConnection({
    host: 'localhost',
    port: TEST_PORT,
    timeout: TEST_TIMEOUT
  });
  
  let receivedData = '';
  let currentStep = 'connecting';
  let testIndex = 0;
  
  const invalidHostnames = [
    '',                    // Empty hostname
    ' ',                  // Whitespace only
    'invalid..hostname',  // Double dots
    '.invalid',          // Leading dot
    'invalid.',          // Trailing dot
    'very-long-hostname-that-exceeds-reasonable-limits-' + 'x'.repeat(200)
  ];
  
  socket.on('data', (data) => {
    receivedData += data.toString();
    
    if (currentStep === 'connecting' && receivedData.includes('220')) {
      currentStep = 'testing';
      receivedData = ''; // Clear buffer
      console.log(`Testing invalid hostname: "${invalidHostnames[testIndex]}"`);
      socket.write(`EHLO ${invalidHostnames[testIndex]}\r\n`);
    } else if (currentStep === 'testing' && (receivedData.includes('250') || receivedData.includes('5'))) {
      // Server should either accept with warning or reject with 5xx
      expect(receivedData).toMatch(/^(250|5\d\d)/);
      
      testIndex++;
      if (testIndex < invalidHostnames.length) {
        currentStep = 'reset';
        receivedData = ''; // Clear buffer
        socket.write('RSET\r\n');
      } else {
        socket.write('QUIT\r\n');
        setTimeout(() => {
          socket.destroy();
          done.resolve();
        }, 100);
      }
    } else if (currentStep === 'reset' && receivedData.includes('250')) {
      currentStep = 'testing';
      receivedData = ''; // Clear buffer
      console.log(`Testing invalid hostname: "${invalidHostnames[testIndex]}"`);
      socket.write(`EHLO ${invalidHostnames[testIndex]}\r\n`);
    }
  });
  
  socket.on('error', (error) => {
    done.reject(error);
  });
  
  socket.on('timeout', () => {
    socket.destroy();
    done.reject(new Error(`Connection timeout at step: ${currentStep}`));
  });
  
  await done.promise;
});

tap.test('CMD-01: EHLO command pipelining - multiple EHLO commands', async (tools) => {
  const done = tools.defer();
  
  const socket = net.createConnection({
    host: 'localhost',
    port: TEST_PORT,
    timeout: TEST_TIMEOUT
  });
  
  let receivedData = '';
  let currentStep = 'connecting';
  
  socket.on('data', (data) => {
    receivedData += data.toString();
    
    if (currentStep === 'connecting' && receivedData.includes('220')) {
      currentStep = 'first_ehlo';
      receivedData = ''; // Clear buffer
      socket.write('EHLO first.example.com\r\n');
    } else if (currentStep === 'first_ehlo' && receivedData.includes('250 ')) {
      currentStep = 'second_ehlo';
      receivedData = ''; // Clear buffer
      // Second EHLO (should reset session)
      socket.write('EHLO second.example.com\r\n');
    } else if (currentStep === 'second_ehlo' && receivedData.includes('250 ')) {
      currentStep = 'mail_from';
      receivedData = ''; // Clear buffer
      // Verify session was reset by trying MAIL FROM
      socket.write('MAIL FROM:<test@example.com>\r\n');
    } else if (currentStep === 'mail_from' && receivedData.includes('250')) {
      socket.write('QUIT\r\n');
      setTimeout(() => {
        socket.destroy();
        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;
});

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

export default tap.start();