import { expect, tap } from '@git.zone/tstest/tapbundle';
import * as net from 'net';
import { SmartProxy } from '../ts/index.js';
import type { IRouteConfig } from '../ts/index.js';

let proxy: SmartProxy;

tap.test('setup socket handler test', async () => {
  // Create a simple socket handler route
  const routes: IRouteConfig[] = [{
    name: 'echo-handler',
    match: { 
      ports: 9999
      // No domains restriction - matches all connections
    },
    action: {
      type: 'socket-handler',
      socketHandler: (socket, context) => {
        console.log('Socket handler called');
        // Simple echo server
        socket.write('ECHO SERVER\n');
        socket.on('data', (data) => {
          console.log('Socket handler received data:', data.toString());
          socket.write(`ECHO: ${data}`);
        });
        socket.on('error', (err) => {
          console.error('Socket error:', err);
        });
      }
    }
  }];
  
  proxy = new SmartProxy({
    routes,
    enableDetailedLogging: false
  });
  
  await proxy.start();
});

tap.test('should handle socket with custom function', async () => {
  const client = new net.Socket();
  let response = '';
  
  await new Promise<void>((resolve, reject) => {
    client.connect(9999, 'localhost', () => {
      console.log('Client connected to proxy');
      resolve();
    });
    
    client.on('error', reject);
  });
  
  // Collect data
  client.on('data', (data) => {
    console.log('Client received:', data.toString());
    response += data.toString();
  });
  
  // Wait a bit for connection to stabilize
  await new Promise(resolve => setTimeout(resolve, 50));
  
  // Send test data
  console.log('Sending test data...');
  client.write('Hello World\n');
  
  // Wait for response
  await new Promise(resolve => setTimeout(resolve, 200));
  
  console.log('Total response:', response);
  expect(response).toContain('ECHO SERVER');
  expect(response).toContain('ECHO: Hello World');
  
  client.destroy();
});

tap.test('should handle async socket handler', async () => {
  // Update route with async handler
  await proxy.updateRoutes([{
    name: 'async-handler',
    match: { ports: 9999 },
    action: {
      type: 'socket-handler',
      socketHandler: async (socket, context) => {
        // Set up data handler first
        socket.on('data', async (data) => {
          console.log('Async handler received:', data.toString());
          // Simulate async processing
          await new Promise(resolve => setTimeout(resolve, 10));
          const processed = `PROCESSED: ${data.toString().trim().toUpperCase()}\n`;
          console.log('Sending:', processed);
          socket.write(processed);
        });
        
        // Then simulate async operation
        await new Promise(resolve => setTimeout(resolve, 10));
        socket.write('ASYNC READY\n');
      }
    }
  }]);
  
  const client = new net.Socket();
  let response = '';
  
  // Collect data
  client.on('data', (data) => {
    response += data.toString();
  });
  
  await new Promise<void>((resolve, reject) => {
    client.connect(9999, 'localhost', () => {
      // Send initial data to trigger the handler
      client.write('test data\n');
      resolve();
    });
    
    client.on('error', reject);
  });
  
  // Wait for async processing
  await new Promise(resolve => setTimeout(resolve, 200));
  
  console.log('Final response:', response);
  expect(response).toContain('ASYNC READY');
  expect(response).toContain('PROCESSED: TEST DATA');
  
  client.destroy();
});

tap.test('should handle errors in socket handler', async () => {
  // Update route with error-throwing handler
  await proxy.updateRoutes([{
    name: 'error-handler',
    match: { ports: 9999 },
    action: {
      type: 'socket-handler',
      socketHandler: (socket, context) => {
        throw new Error('Handler error');
      }
    }
  }]);
  
  const client = new net.Socket();
  let connectionClosed = false;
  
  client.on('close', () => {
    connectionClosed = true;
  });
  
  await new Promise<void>((resolve, reject) => {
    client.connect(9999, 'localhost', () => {
      // Connection established - send data to trigger handler
      client.write('trigger\n');
      resolve();
    });
    
    client.on('error', () => {
      // Ignore client errors - we expect the connection to be closed
    });
  });
  
  // Wait a bit
  await new Promise(resolve => setTimeout(resolve, 100));
  
  // Socket should be closed due to handler error
  expect(connectionClosed).toEqual(true);
});

tap.test('cleanup', async () => {
  await proxy.stop();
});

export default tap.start();