import { tap, expect } from '@git.zone/tstest/tapbundle';
import * as net from 'net';
import * as fs from 'fs';
import * as path from 'path';
import { startTestServer, stopTestServer } from '../../helpers/server.loader.ts'
import type { ITestServer } from '../../helpers/server.loader.js';
const TEST_PORT = 2525;
const SAMPLE_FILES_DIR = path.join(process.cwd(), '.nogit', 'sample-files');

let testServer: ITestServer;

// Helper function to read and encode files
function readFileAsBase64(filePath: string): string {
  try {
    const fileContent = fs.readFileSync(filePath);
    return fileContent.toString('base64');
  } catch (err) {
    console.error(`Failed to read file ${filePath}:`, err);
    return '';
  }
}

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

tap.test('Attachment Handling - Multiple file types', async (tools) => {
  const done = tools.defer();
  
  const socket = net.createConnection({
    host: 'localhost',
    port: TEST_PORT,
    timeout: 30000
  });
  
  let dataBuffer = '';
  let step = 'greeting';
  let completed = false;
  
  socket.on('data', (data) => {
    if (completed) return;
    
    dataBuffer += data.toString();
    console.log('Server response:', data.toString());
    
    if (step === 'greeting' && dataBuffer.includes('220 ')) {
      step = 'ehlo';
      socket.write('EHLO testclient\r\n');
      dataBuffer = '';
    } else if (step === 'ehlo' && dataBuffer.includes('250')) {
      step = 'mail';
      socket.write('MAIL FROM:<sender@example.com>\r\n');
      dataBuffer = '';
    } else if (step === 'mail' && dataBuffer.includes('250')) {
      step = 'rcpt';
      socket.write('RCPT TO:<recipient@example.com>\r\n');
      dataBuffer = '';
    } else if (step === 'rcpt' && dataBuffer.includes('250')) {
      step = 'data';
      socket.write('DATA\r\n');
      dataBuffer = '';
    } else if (step === 'data' && dataBuffer.includes('354')) {
      const boundary = 'attachment-test-boundary-12345';
      
      // Create various attachments
      const textAttachment = 'This is a text attachment content.\nIt has multiple lines.\nAnd special chars: åäö';
      const jsonAttachment = JSON.stringify({
        name: 'test',
        data: [1, 2, 3],
        unicode: 'ñoño',
        special: '∑∆≈'
      }, null, 2);
      
      // Read real files from sample directory
      const sampleImage = readFileAsBase64(path.join(SAMPLE_FILES_DIR, '003-pdflatex-image/image.jpg'));
      const minimalPdf = readFileAsBase64(path.join(SAMPLE_FILES_DIR, '001-trivial/minimal-document.pdf'));
      const multiPagePdf = readFileAsBase64(path.join(SAMPLE_FILES_DIR, '004-pdflatex-4-pages/pdflatex-4-pages.pdf'));
      const pdfWithAttachment = readFileAsBase64(path.join(SAMPLE_FILES_DIR, '025-attachment/with-attachment.pdf'));
      
      const email = [
        `From: sender@example.com`,
        `To: recipient@example.com`,
        `Subject: Attachment Handling Test - Multiple Types`,
        `Date: ${new Date().toUTCString()}`,
        `Message-ID: <attachment-test-${Date.now()}@example.com>`,
        `MIME-Version: 1.0`,
        `Content-Type: multipart/mixed; boundary="${boundary}"`,
        '',
        'This is a multi-part message with various attachments.',
        '',
        `--${boundary}`,
        `Content-Type: text/plain; charset=utf-8`,
        '',
        'This email tests attachment handling capabilities.',
        'The server should properly process all attached files.',
        '',
        `--${boundary}`,
        `Content-Type: text/plain; charset=utf-8`,
        `Content-Disposition: attachment; filename="document.txt"`,
        `Content-Transfer-Encoding: 7bit`,
        '',
        textAttachment,
        '',
        `--${boundary}`,
        `Content-Type: application/json; charset=utf-8`,
        `Content-Disposition: attachment; filename="data.json"`,
        '',
        jsonAttachment,
        '',
        `--${boundary}`,
        `Content-Type: image/jpeg`,
        `Content-Disposition: attachment; filename="sample-image.jpg"`,
        `Content-Transfer-Encoding: base64`,
        '',
        sampleImage,
        '',
        `--${boundary}`,
        `Content-Type: application/octet-stream`,
        `Content-Disposition: attachment; filename="binary.bin"`,
        `Content-Transfer-Encoding: base64`,
        '',
        Buffer.from('Binary file content with null bytes\0\0\0').toString('base64'),
        '',
        `--${boundary}`,
        `Content-Type: text/csv`,
        `Content-Disposition: attachment; filename="spreadsheet.csv"`,
        '',
        'Name,Age,Country',
        'Alice,25,Sweden',
        'Bob,30,Norway',
        'Charlie,35,Denmark',
        '',
        `--${boundary}`,
        `Content-Type: application/xml; charset=utf-8`,
        `Content-Disposition: attachment; filename="config.xml"`,
        '',
        '<?xml version="1.0" encoding="UTF-8"?>',
        '<config>',
        '  <setting name="test">value</setting>',
        '  <unicode>ñoño ∑∆≈</unicode>',
        '</config>',
        '',
        `--${boundary}`,
        `Content-Type: application/pdf`,
        `Content-Disposition: attachment; filename="minimal-document.pdf"`,
        `Content-Transfer-Encoding: base64`,
        '',
        minimalPdf,
        '',
        `--${boundary}`,
        `Content-Type: application/pdf`,
        `Content-Disposition: attachment; filename="multi-page-document.pdf"`,
        `Content-Transfer-Encoding: base64`,
        '',
        multiPagePdf,
        '',
        `--${boundary}`,
        `Content-Type: application/pdf`,
        `Content-Disposition: attachment; filename="pdf-with-embedded-attachment.pdf"`,
        `Content-Transfer-Encoding: base64`,
        '',
        pdfWithAttachment,
        '',
        `--${boundary}`,
        `Content-Type: text/html; charset=utf-8`,
        `Content-Disposition: attachment; filename="webpage.html"`,
        '',
        '<!DOCTYPE html>',
        '<html><head><title>Test</title></head>',
        '<body><h1>HTML Attachment</h1><p>Content with <em>markup</em></p></body>',
        '</html>',
        '',
        `--${boundary}--`,
        '.',
        ''
      ].join('\r\n');
      
      console.log('Sending email with 10 different attachment types including real PDFs');
      socket.write(email);
      dataBuffer = '';
      step = 'sent';
    } else if (step === 'sent' && dataBuffer.includes('250 ') && dataBuffer.includes('message queued')) {
      if (!completed) {
        completed = true;
        console.log('Email with multiple attachments accepted successfully');
        expect(true).toEqual(true);
        
        socket.write('QUIT\r\n');
        socket.end();
        done.resolve();
      }
    }
  });
  
  socket.on('error', (err) => {
    console.error('Socket error:', err);
    done.reject(err);
  });
  
  await done.promise;
});

tap.test('Attachment Handling - Large attachment', async (tools) => {
  const done = tools.defer();
  
  const socket = net.createConnection({
    host: 'localhost',
    port: TEST_PORT,
    timeout: 30000
  });
  
  let dataBuffer = '';
  let step = 'greeting';
  let completed = false;
  
  socket.on('data', (data) => {
    if (completed) return;
    
    dataBuffer += data.toString();
    console.log('Server response:', data.toString());
    
    if (step === 'greeting' && dataBuffer.includes('220 ')) {
      step = 'ehlo';
      socket.write('EHLO testclient\r\n');
      dataBuffer = '';
    } else if (step === 'ehlo' && dataBuffer.includes('250')) {
      step = 'mail';
      socket.write('MAIL FROM:<sender@example.com>\r\n');
      dataBuffer = '';
    } else if (step === 'mail' && dataBuffer.includes('250')) {
      step = 'rcpt';
      socket.write('RCPT TO:<recipient@example.com>\r\n');
      dataBuffer = '';
    } else if (step === 'rcpt' && dataBuffer.includes('250')) {
      step = 'data';
      socket.write('DATA\r\n');
      dataBuffer = '';
    } else if (step === 'data' && dataBuffer.includes('354')) {
      const boundary = 'large-attachment-boundary';
      
      // Use a real large PDF file
      const largePdf = readFileAsBase64(path.join(SAMPLE_FILES_DIR, '009-pdflatex-geotopo/GeoTopo.pdf'));
      const largePdfSize = Buffer.from(largePdf, 'base64').length;
      console.log(`Large PDF size: ${(largePdfSize / 1024).toFixed(2)}KB`);
      
      const email = [
        `From: sender@example.com`,
        `To: recipient@example.com`,
        `Subject: Large Attachment Test`,
        `Date: ${new Date().toUTCString()}`,
        `Message-ID: <large-attach-${Date.now()}@example.com>`,
        `MIME-Version: 1.0`,
        `Content-Type: multipart/mixed; boundary="${boundary}"`,
        '',
        `--${boundary}`,
        `Content-Type: text/plain`,
        '',
        'This email contains a large attachment.',
        '',
        `--${boundary}`,
        `Content-Type: application/pdf`,
        `Content-Disposition: attachment; filename="large-geotopo.pdf"`,
        `Content-Transfer-Encoding: base64`,
        '',
        largePdf,
        '',
        `--${boundary}--`,
        '.',
        ''
      ].join('\r\n');
      
      console.log(`Sending email with large PDF attachment (${(largePdfSize / 1024).toFixed(2)}KB)`);
      socket.write(email);
      dataBuffer = '';
      step = 'sent';
    } else if (step === 'sent' && (dataBuffer.includes('250 ') || dataBuffer.includes('552 '))) {
      if (!completed) {
        completed = true;
        const accepted = dataBuffer.includes('250');
        const rejected = dataBuffer.includes('552'); // Size exceeded
        
        console.log(`Large attachment: ${accepted ? 'accepted' : 'rejected (size limit)'}`);
        expect(accepted || rejected).toEqual(true);
        
        socket.write('QUIT\r\n');
        socket.end();
        done.resolve();
      }
    }
  });
  
  socket.on('error', (err) => {
    console.error('Socket error:', err);
    done.reject(err);
  });
  
  await done.promise;
});

tap.test('Attachment Handling - Inline vs attachment disposition', async (tools) => {
  const done = tools.defer();
  
  const socket = net.createConnection({
    host: 'localhost',
    port: TEST_PORT,
    timeout: 30000
  });
  
  let dataBuffer = '';
  let step = 'greeting';
  let completed = false;
  
  socket.on('data', (data) => {
    if (completed) return;
    
    dataBuffer += data.toString();
    console.log('Server response:', data.toString());
    
    if (step === 'greeting' && dataBuffer.includes('220 ')) {
      step = 'ehlo';
      socket.write('EHLO testclient\r\n');
      dataBuffer = '';
    } else if (step === 'ehlo' && dataBuffer.includes('250')) {
      step = 'mail';
      socket.write('MAIL FROM:<sender@example.com>\r\n');
      dataBuffer = '';
    } else if (step === 'mail' && dataBuffer.includes('250')) {
      step = 'rcpt';
      socket.write('RCPT TO:<recipient@example.com>\r\n');
      dataBuffer = '';
    } else if (step === 'rcpt' && dataBuffer.includes('250')) {
      step = 'data';
      socket.write('DATA\r\n');
      dataBuffer = '';
    } else if (step === 'data' && dataBuffer.includes('354')) {
      const boundary = 'inline-attachment-boundary';
      
      const email = [
        `From: sender@example.com`,
        `To: recipient@example.com`,
        `Subject: Inline vs Attachment Test`,
        `Date: ${new Date().toUTCString()}`,
        `Message-ID: <inline-test-${Date.now()}@example.com>`,
        `MIME-Version: 1.0`,
        `Content-Type: multipart/related; boundary="${boundary}"`,
        '',
        `--${boundary}`,
        `Content-Type: text/html`,
        '',
        '<html><body>',
        '<p>This email has inline images:</p>',
        '<img src="cid:image1">',
        '<img src="cid:image2">',
        '</body></html>',
        '',
        `--${boundary}`,
        `Content-Type: image/png`,
        `Content-ID: <image1>`,
        `Content-Disposition: inline; filename="inline1.png"`,
        `Content-Transfer-Encoding: base64`,
        '',
        readFileAsBase64(path.join(SAMPLE_FILES_DIR, '008-reportlab-inline-image/smile.png')) || 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
        '',
        `--${boundary}`,
        `Content-Type: image/png`,
        `Content-ID: <image2>`,
        `Content-Disposition: inline; filename="inline2.png"`,
        `Content-Transfer-Encoding: base64`,
        '',
        readFileAsBase64(path.join(SAMPLE_FILES_DIR, '019-grayscale-image/page-0-X0.png')) || 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==',
        '',
        `--${boundary}`,
        `Content-Type: application/pdf`,
        `Content-Disposition: attachment; filename="document.pdf"`,
        `Content-Transfer-Encoding: base64`,
        '',
        readFileAsBase64(path.join(SAMPLE_FILES_DIR, '013-reportlab-overlay/reportlab-overlay.pdf')) || 'JVBERi0xLjQKJcOkw7zDtsOVDQo=',
        '',
        `--${boundary}--`,
        '.',
        ''
      ].join('\r\n');
      
      socket.write(email);
      dataBuffer = '';
      step = 'sent';
    } else if (step === 'sent' && dataBuffer.includes('250 ') && dataBuffer.includes('message queued')) {
      if (!completed) {
        completed = true;
        console.log('Email with inline and attachment dispositions accepted');
        expect(true).toEqual(true);
        
        socket.write('QUIT\r\n');
        socket.end();
        done.resolve();
      }
    }
  });
  
  socket.on('error', (err) => {
    console.error('Socket error:', err);
    done.reject(err);
  });
  
  await done.promise;
});

tap.test('Attachment Handling - Filename encoding', async (tools) => {
  const done = tools.defer();
  
  const socket = net.createConnection({
    host: 'localhost',
    port: TEST_PORT,
    timeout: 30000
  });
  
  let dataBuffer = '';
  let step = 'greeting';
  let completed = false;
  
  socket.on('data', (data) => {
    if (completed) return;
    
    dataBuffer += data.toString();
    console.log('Server response:', data.toString());
    
    if (step === 'greeting' && dataBuffer.includes('220 ')) {
      step = 'ehlo';
      socket.write('EHLO testclient\r\n');
      dataBuffer = '';
    } else if (step === 'ehlo' && dataBuffer.includes('250')) {
      step = 'mail';
      socket.write('MAIL FROM:<sender@example.com>\r\n');
      dataBuffer = '';
    } else if (step === 'mail' && dataBuffer.includes('250')) {
      step = 'rcpt';
      socket.write('RCPT TO:<recipient@example.com>\r\n');
      dataBuffer = '';
    } else if (step === 'rcpt' && dataBuffer.includes('250')) {
      step = 'data';
      socket.write('DATA\r\n');
      dataBuffer = '';
    } else if (step === 'data' && dataBuffer.includes('354')) {
      const boundary = 'filename-encoding-boundary';
      
      const email = [
        `From: sender@example.com`,
        `To: recipient@example.com`,
        `Subject: Filename Encoding Test`,
        `Date: ${new Date().toUTCString()}`,
        `Message-ID: <filename-test-${Date.now()}@example.com>`,
        `MIME-Version: 1.0`,
        `Content-Type: multipart/mixed; boundary="${boundary}"`,
        '',
        `--${boundary}`,
        `Content-Type: text/plain`,
        '',
        'Testing various filename encodings.',
        '',
        `--${boundary}`,
        `Content-Type: text/plain`,
        `Content-Disposition: attachment; filename="simple.txt"`,
        '',
        'Simple ASCII filename',
        '',
        `--${boundary}`,
        `Content-Type: text/plain`,
        `Content-Disposition: attachment; filename="åäö-nordic.txt"`,
        '',
        'Nordic characters in filename',
        '',
        `--${boundary}`,
        `Content-Type: text/plain`,
        `Content-Disposition: attachment; filename*=UTF-8''%C3%A5%C3%A4%C3%B6-encoded.txt`,
        '',
        'RFC 2231 encoded filename',
        '',
        `--${boundary}`,
        `Content-Type: text/plain`,
        `Content-Disposition: attachment; filename="=?UTF-8?B?8J+YgC1lbW9qaS50eHQ=?="`,
        '',
        'MIME encoded filename with emoji',
        '',
        `--${boundary}`,
        `Content-Type: text/plain`,
        `Content-Disposition: attachment; filename="very long filename that exceeds normal limits and should be handled properly by the server.txt"`,
        '',
        'Very long filename',
        '',
        `--${boundary}--`,
        '.',
        ''
      ].join('\r\n');
      
      socket.write(email);
      dataBuffer = '';
      step = 'sent';
    } else if (step === 'sent' && dataBuffer.includes('250 ') && dataBuffer.includes('message queued')) {
      if (!completed) {
        completed = true;
        console.log('Email with various filename encodings accepted');
        expect(true).toEqual(true);
        
        socket.write('QUIT\r\n');
        socket.end();
        done.resolve();
      }
    }
  });
  
  socket.on('error', (err) => {
    console.error('Socket error:', err);
    done.reject(err);
  });
  
  await done.promise;
});

tap.test('Attachment Handling - Empty and malformed attachments', async (tools) => {
  const done = tools.defer();
  
  const socket = net.createConnection({
    host: 'localhost',
    port: TEST_PORT,
    timeout: 30000
  });
  
  let dataBuffer = '';
  let step = 'greeting';
  let completed = false;
  
  socket.on('data', (data) => {
    if (completed) return;
    
    dataBuffer += data.toString();
    console.log('Server response:', data.toString());
    
    if (step === 'greeting' && dataBuffer.includes('220 ')) {
      step = 'ehlo';
      socket.write('EHLO testclient\r\n');
      dataBuffer = '';
    } else if (step === 'ehlo' && dataBuffer.includes('250')) {
      step = 'mail';
      socket.write('MAIL FROM:<sender@example.com>\r\n');
      dataBuffer = '';
    } else if (step === 'mail' && dataBuffer.includes('250')) {
      step = 'rcpt';
      socket.write('RCPT TO:<recipient@example.com>\r\n');
      dataBuffer = '';
    } else if (step === 'rcpt' && dataBuffer.includes('250')) {
      step = 'data';
      socket.write('DATA\r\n');
      dataBuffer = '';
    } else if (step === 'data' && dataBuffer.includes('354')) {
      const boundary = 'malformed-boundary';
      
      const email = [
        `From: sender@example.com`,
        `To: recipient@example.com`,
        `Subject: Empty and Malformed Attachments`,
        `Date: ${new Date().toUTCString()}`,
        `Message-ID: <malformed-${Date.now()}@example.com>`,
        `MIME-Version: 1.0`,
        `Content-Type: multipart/mixed; boundary="${boundary}"`,
        '',
        `--${boundary}`,
        `Content-Type: text/plain`,
        '',
        'Testing empty and malformed attachments.',
        '',
        `--${boundary}`,
        `Content-Type: application/octet-stream`,
        `Content-Disposition: attachment; filename="empty.dat"`,
        '',
        '', // Empty attachment
        `--${boundary}`,
        `Content-Type: text/plain`,
        `Content-Disposition: attachment`, // Missing filename
        '',
        'Attachment without filename',
        '',
        `--${boundary}`,
        `Content-Type: application/pdf`,
        `Content-Disposition: attachment; filename="broken.pdf"`,
        `Content-Transfer-Encoding: base64`,
        '',
        'NOT-VALID-BASE64-@#$%', // Invalid base64
        '',
        `--${boundary}`,
        `Content-Disposition: attachment; filename="no-content-type.txt"`, // Missing Content-Type
        '',
        'Attachment without Content-Type header',
        '',
        `--${boundary}--`,
        '.',
        ''
      ].join('\r\n');
      
      socket.write(email);
      dataBuffer = '';
      step = 'sent';
    } else if (step === 'sent' && (dataBuffer.includes('250 ') || dataBuffer.includes('550 '))) {
      if (!completed) {
        completed = true;
        const result = dataBuffer.includes('250') ? 'accepted' : 'rejected';
        console.log(`Email with malformed attachments ${result}`);
        expect(true).toEqual(true);
        
        socket.write('QUIT\r\n');
        socket.end();
        done.resolve();
      }
    }
  });
  
  socket.on('error', (err) => {
    console.error('Socket error:', err);
    done.reject(err);
  });
  
  await done.promise;
});

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

export default tap.start();