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

const TEST_PORT = 2525;

let testServer;

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

tap.test('ERR-06: Malformed MIME handling - Invalid boundary', async (tools) => {
  const done = tools.defer();
  const socket = net.createConnection({
    host: 'localhost',
    port: TEST_PORT,
    timeout: 30000
  });

  socket.on('connect', async () => {
    try {
      // Read greeting
      await new Promise<void>((resolve) => {
        socket.once('data', () => resolve());
      });

      // Send EHLO
      socket.write('EHLO testhost\r\n');
      
      await new Promise<void>((resolve) => {
        let data = '';
        const handleData = (chunk: Buffer) => {
          data += chunk.toString();
          if (data.includes('250 ') && data.includes('\r\n')) {
            socket.removeListener('data', handleData);
            resolve();
          }
        };
        socket.on('data', handleData);
      });

      // Send MAIL FROM
      socket.write('MAIL FROM:<sender@example.com>\r\n');
      
      await new Promise<void>((resolve) => {
        socket.once('data', (chunk) => {
          const response = chunk.toString();
          expect(response).toInclude('250');
          resolve();
        });
      });

      // Send RCPT TO
      socket.write('RCPT TO:<recipient@example.com>\r\n');
      
      await new Promise<void>((resolve) => {
        socket.once('data', (chunk) => {
          const response = chunk.toString();
          expect(response).toInclude('250');
          resolve();
        });
      });

      // Send DATA
      socket.write('DATA\r\n');
      
      const dataResponse = await new Promise<string>((resolve) => {
        socket.once('data', (chunk) => {
          resolve(chunk.toString());
        });
      });

      expect(dataResponse).toInclude('354');

      // Send malformed MIME with invalid boundary
      const malformedMime = [
        'From: sender@example.com',
        'To: recipient@example.com',
        'Subject: Malformed MIME Test',
        'MIME-Version: 1.0',
        'Content-Type: multipart/mixed; boundary=invalid-boundary',
        '',
        '--invalid-boundary',
        'Content-Type: text/plain',
        'Content-Transfer-Encoding: invalid-encoding',
        '',
        'This is malformed MIME content.',
        '--invalid-boundary',
        'Content-Type: application/octet-stream',
        'Content-Disposition: attachment; filename="malformed.txt',  // Missing closing quote
        '',
        'Malformed attachment content without proper boundary.',
        '--invalid-boundary--missing-final-boundary',  // Malformed closing boundary
        '.',
        ''
      ].join('\r\n');

      socket.write(malformedMime);

      const response = await new Promise<string>((resolve) => {
        socket.once('data', (chunk) => {
          resolve(chunk.toString());
        });
      });

      // Server should either:
      // 1. Accept the message (250) - tolerant handling
      // 2. Reject with error (550/552) - strict MIME validation
      // 3. Return temporary failure (4xx) - processing error
      const validResponse = response.includes('250') || 
                           response.includes('550') || 
                           response.includes('552') ||
                           response.includes('451') ||
                           response.includes('mime') ||
                           response.includes('malformed');

      console.log('Malformed MIME response:', response.substring(0, 100));
      expect(validResponse).toEqual(true);

      socket.write('QUIT\r\n');
      socket.end();
      done.resolve();
    } catch (error) {
      socket.end();
      done.reject(error);
    }
  });

  socket.on('error', (error) => {
    done.reject(error);
  });
});

tap.test('ERR-06: Malformed MIME handling - Missing headers', async (tools) => {
  const done = tools.defer();
  const socket = net.createConnection({
    host: 'localhost',
    port: TEST_PORT,
    timeout: 30000
  });

  socket.on('connect', async () => {
    try {
      // Read greeting
      await new Promise<void>((resolve) => {
        socket.once('data', () => resolve());
      });

      // Send EHLO
      socket.write('EHLO testhost\r\n');
      
      await new Promise<void>((resolve) => {
        let data = '';
        const handleData = (chunk: Buffer) => {
          data += chunk.toString();
          if (data.includes('250 ') && data.includes('\r\n')) {
            socket.removeListener('data', handleData);
            resolve();
          }
        };
        socket.on('data', handleData);
      });

      // Send MAIL FROM
      socket.write('MAIL FROM:<sender@example.com>\r\n');
      
      await new Promise<void>((resolve) => {
        socket.once('data', (chunk) => {
          const response = chunk.toString();
          expect(response).toInclude('250');
          resolve();
        });
      });

      // Send RCPT TO
      socket.write('RCPT TO:<recipient@example.com>\r\n');
      
      await new Promise<void>((resolve) => {
        socket.once('data', (chunk) => {
          const response = chunk.toString();
          expect(response).toInclude('250');
          resolve();
        });
      });

      // Send DATA
      socket.write('DATA\r\n');
      
      const dataResponse = await new Promise<string>((resolve) => {
        socket.once('data', (chunk) => {
          resolve(chunk.toString());
        });
      });

      expect(dataResponse).toInclude('354');

      // Send MIME with missing required headers
      const malformedMime = [
        'Subject: Missing MIME headers',
        'Content-Type: multipart/mixed',  // Missing boundary parameter
        '',
        '--boundary',
        // Missing Content-Type for part
        '',
        'This part has no Content-Type header.',
        '--boundary',
        'Content-Type: text/plain',
        // Missing blank line between headers and body
        'This part has no separator line.',
        '--boundary--',
        '.',
        ''
      ].join('\r\n');

      socket.write(malformedMime);

      const response = await new Promise<string>((resolve) => {
        socket.once('data', (chunk) => {
          resolve(chunk.toString());
        });
      });

      // Server should handle this gracefully
      const validResponse = response.includes('250') || 
                           response.includes('550') || 
                           response.includes('552') ||
                           response.includes('451');

      console.log('Missing headers response:', response.substring(0, 100));
      expect(validResponse).toEqual(true);

      socket.write('QUIT\r\n');
      socket.end();
      done.resolve();
    } catch (error) {
      socket.end();
      done.reject(error);
    }
  });

  socket.on('error', (error) => {
    done.reject(error);
  });
});

tap.test('ERR-06: Malformed MIME handling - Nested multipart errors', async (tools) => {
  const done = tools.defer();
  const socket = net.createConnection({
    host: 'localhost',
    port: TEST_PORT,
    timeout: 30000
  });

  socket.on('connect', async () => {
    try {
      // Read greeting
      await new Promise<void>((resolve) => {
        socket.once('data', () => resolve());
      });

      // Send EHLO
      socket.write('EHLO testhost\r\n');
      
      await new Promise<void>((resolve) => {
        let data = '';
        const handleData = (chunk: Buffer) => {
          data += chunk.toString();
          if (data.includes('250 ') && data.includes('\r\n')) {
            socket.removeListener('data', handleData);
            resolve();
          }
        };
        socket.on('data', handleData);
      });

      // Send MAIL FROM
      socket.write('MAIL FROM:<sender@example.com>\r\n');
      
      await new Promise<void>((resolve) => {
        socket.once('data', (chunk) => {
          const response = chunk.toString();
          expect(response).toInclude('250');
          resolve();
        });
      });

      // Send RCPT TO
      socket.write('RCPT TO:<recipient@example.com>\r\n');
      
      await new Promise<void>((resolve) => {
        socket.once('data', (chunk) => {
          const response = chunk.toString();
          expect(response).toInclude('250');
          resolve();
        });
      });

      // Send DATA
      socket.write('DATA\r\n');
      
      const dataResponse = await new Promise<string>((resolve) => {
        socket.once('data', (chunk) => {
          resolve(chunk.toString());
        });
      });

      expect(dataResponse).toInclude('354');

      // Send deeply nested multipart with errors
      const malformedMime = [
        'From: sender@example.com',
        'To: recipient@example.com',
        'Subject: Nested multipart errors',
        'MIME-Version: 1.0',
        'Content-Type: multipart/mixed; boundary="outer"',
        '',
        '--outer',
        'Content-Type: multipart/alternative; boundary="inner"',
        '',
        '--inner',
        'Content-Type: multipart/related; boundary="nested"',  // Too deeply nested
        '',
        '--nested',
        'Content-Type: text/plain',
        'Content-Transfer-Encoding: base64',
        '',
        'NOT-VALID-BASE64-CONTENT!!!',  // Invalid base64
        '--nested',  // Missing closing --
        '--inner--',  // Improper nesting
        '--outer',  // Missing part content
        '--outer--',
        '.',
        ''
      ].join('\r\n');

      socket.write(malformedMime);

      const response = await new Promise<string>((resolve) => {
        socket.once('data', (chunk) => {
          resolve(chunk.toString());
        });
      });

      // Server should handle complex MIME errors gracefully
      const validResponse = response.includes('250') || 
                           response.includes('550') || 
                           response.includes('552') ||
                           response.includes('451');

      console.log('Nested multipart response:', response.substring(0, 100));
      expect(validResponse).toEqual(true);

      socket.write('QUIT\r\n');
      socket.end();
      done.resolve();
    } catch (error) {
      socket.end();
      done.reject(error);
    }
  });

  socket.on('error', (error) => {
    done.reject(error);
  });
});

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

export default tap.start();