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

const TEST_PORT = 2525;
let testServer: ITestServer;

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

tap.test('TLS Certificate Validation - STARTTLS certificate check', async (tools) => {
  const done = tools.defer();
  
  const socket = net.createConnection({
    host: 'localhost',
    port: TEST_PORT,
    timeout: 30000
  });
  
  let dataBuffer = '';
  let step = 'greeting';
  
  socket.on('data', (data) => {
    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')) {
      const supportsStarttls = dataBuffer.toLowerCase().includes('starttls');
      console.log('STARTTLS supported:', supportsStarttls);
      
      if (supportsStarttls) {
        step = 'starttls';
        socket.write('STARTTLS\r\n');
        dataBuffer = '';
      } else {
        console.log('STARTTLS not supported, testing plain connection');
        socket.write('QUIT\r\n');
        socket.end();
        done.resolve();
      }
    } else if (step === 'starttls' && dataBuffer.includes('220')) {
      console.log('Ready to start TLS');
      
      // Upgrade to TLS
      const tlsOptions = {
        socket: socket,
        rejectUnauthorized: false, // For self-signed certificates in testing
        requestCert: true
      };
      
      const tlsSocket = tls.connect(tlsOptions);
      
      tlsSocket.on('secureConnect', () => {
        console.log('TLS connection established');
        
        // Get certificate information
        const cert = tlsSocket.getPeerCertificate();
        console.log('Certificate present:', !!cert);
        
        if (cert && Object.keys(cert).length > 0) {
          console.log('Certificate subject:', cert.subject);
          console.log('Certificate issuer:', cert.issuer);
          console.log('Certificate valid from:', cert.valid_from);
          console.log('Certificate valid to:', cert.valid_to);
          
          // Check certificate validity
          const now = new Date();
          const validFrom = new Date(cert.valid_from);
          const validTo = new Date(cert.valid_to);
          const isValid = now >= validFrom && now <= validTo;
          
          console.log('Certificate currently valid:', isValid);
          expect(true).toEqual(true); // Certificate present
        }
        
        // Test EHLO over TLS
        tlsSocket.write('EHLO testclient\r\n');
      });
      
      tlsSocket.on('data', (data) => {
        const response = data.toString();
        console.log('TLS response:', response);
        
        if (response.includes('250')) {
          console.log('EHLO over TLS successful');
          expect(true).toEqual(true);
          
          tlsSocket.write('QUIT\r\n');
          tlsSocket.end();
          done.resolve();
        }
      });
      
      tlsSocket.on('error', (err) => {
        console.error('TLS error:', err);
        done.reject(err);
      });
    }
  });
  
  socket.on('error', (err) => {
    console.error('Socket error:', err);
    done.reject(err);
  });
  
  await done.promise;
});

tap.test('TLS Certificate Validation - Direct TLS connection', async (tools) => {
  const done = tools.defer();
  
  // Try connecting with TLS directly (implicit TLS)
  const tlsOptions = {
    host: 'localhost',
    port: TEST_PORT,
    rejectUnauthorized: false,
    timeout: 30000
  };
  
  const socket = tls.connect(tlsOptions);
  
  socket.on('secureConnect', () => {
    console.log('Direct TLS connection established');
    
    const cert = socket.getPeerCertificate();
    if (cert && Object.keys(cert).length > 0) {
      console.log('Certificate found on direct TLS connection');
      expect(true).toEqual(true);
    }
    
    socket.end();
    done.resolve();
  });
  
  socket.on('error', (err) => {
    // Direct TLS might not be supported, try plain connection
    console.log('Direct TLS not supported, this is expected for STARTTLS servers');
    expect(true).toEqual(true);
    done.resolve();
  });
  
  socket.on('timeout', () => {
    console.log('Direct TLS connection timeout');
    socket.destroy();
    done.resolve();
  });
  
  await done.promise;
});

tap.test('TLS Certificate Validation - Certificate verification with strict mode', async (tools) => {
  const done = tools.defer();
  
  const socket = net.createConnection({
    host: 'localhost',
    port: TEST_PORT,
    timeout: 30000
  });
  
  let dataBuffer = '';
  let step = 'greeting';
  
  socket.on('data', (data) => {
    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')) {
      if (dataBuffer.toLowerCase().includes('starttls')) {
        step = 'starttls';
        socket.write('STARTTLS\r\n');
        dataBuffer = '';
      } else {
        socket.write('QUIT\r\n');
        socket.end();
        done.resolve();
      }
    } else if (step === 'starttls' && dataBuffer.includes('220')) {
      // Try with strict certificate verification
      const tlsOptions = {
        socket: socket,
        rejectUnauthorized: true, // Strict mode
        servername: 'localhost' // For SNI
      };
      
      const tlsSocket = tls.connect(tlsOptions);
      
      tlsSocket.on('secureConnect', () => {
        console.log('TLS connection with strict verification successful');
        const authorized = tlsSocket.authorized;
        console.log('Certificate authorized:', authorized);
        
        if (!authorized) {
          console.log('Authorization error:', tlsSocket.authorizationError);
        }
        
        expect(true).toEqual(true); // Connection established
        tlsSocket.write('QUIT\r\n');
        tlsSocket.end();
        done.resolve();
      });
      
      tlsSocket.on('error', (err) => {
        console.log('Certificate verification error (expected for self-signed):', err.message);
        expect(true).toEqual(true); // Error is expected for self-signed certificates
        socket.end();
        done.resolve();
      });
    }
  });
  
  socket.on('error', (err) => {
    console.error('Socket error:', err);
    done.reject(err);
  });
  
  await done.promise;
});

tap.test('TLS Certificate Validation - Cipher suite information', async (tools) => {
  const done = tools.defer();
  
  const socket = net.createConnection({
    host: 'localhost',
    port: TEST_PORT,
    timeout: 30000
  });
  
  let dataBuffer = '';
  let step = 'greeting';
  
  socket.on('data', (data) => {
    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')) {
      if (dataBuffer.toLowerCase().includes('starttls')) {
        step = 'starttls';
        socket.write('STARTTLS\r\n');
        dataBuffer = '';
      } else {
        socket.write('QUIT\r\n');
        socket.end();
        done.resolve();
      }
    } else if (step === 'starttls' && dataBuffer.includes('220')) {
      const tlsOptions = {
        socket: socket,
        rejectUnauthorized: false
      };
      
      const tlsSocket = tls.connect(tlsOptions);
      
      tlsSocket.on('secureConnect', () => {
        console.log('TLS connection established');
        
        // Get cipher information
        const cipher = tlsSocket.getCipher();
        if (cipher) {
          console.log('Cipher name:', cipher.name);
          console.log('Cipher version:', cipher.version);
          console.log('Cipher standardName:', cipher.standardName);
        }
        
        // Get protocol version
        const protocol = tlsSocket.getProtocol();
        console.log('TLS Protocol:', protocol);
        
        // Verify modern TLS version
        expect(['TLSv1.2', 'TLSv1.3']).toContain(protocol);
        
        tlsSocket.write('QUIT\r\n');
        tlsSocket.end();
        done.resolve();
      });
      
      tlsSocket.on('error', (err) => {
        console.error('TLS error:', err);
        done.reject(err);
      });
    }
  });
  
  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();