import { tap, expect } from '@git.zone/tstest/tapbundle';
import * as smartproxy from '../ts/index.js';
import type { IRouteConfig } from '../ts/proxies/smart-proxy/models/route-types.js';
import * as net from 'net';

tap.test('route security should block connections from unauthorized IPs', async () => {
  // Create a target server that should never receive connections
  let targetServerConnections = 0;
  const targetServer = net.createServer((socket) => {
    targetServerConnections++;
    console.log('Target server received connection - this should not happen!');
    socket.write('ERROR: This connection should have been blocked');
    socket.end();
  });
  
  await new Promise<void>((resolve) => {
    targetServer.listen(9990, '127.0.0.1', () => {
      console.log('Target server listening on port 9990');
      resolve();
    });
  });
  
  // Create proxy with restrictive security at route level
  const routes: IRouteConfig[] = [{
    name: 'secure-route',
    match: {
      ports: 9991
    },
    action: {
      type: 'forward',
      target: {
        host: '127.0.0.1',
        port: 9990
      }
    },
    security: {
      // Only allow a non-existent IP
      ipAllowList: ['192.168.99.99']
    }
  }];
  
  const proxy = new smartproxy.SmartProxy({
    enableDetailedLogging: true,
    routes: routes
  });
  
  
  await proxy.start();
  console.log('Proxy started on port 9991');
  
  // Wait a moment to ensure server is fully ready
  await new Promise(resolve => setTimeout(resolve, 100));
  
  // Try to connect from localhost (should be blocked)
  const client = new net.Socket();
  const events: string[] = [];
  
  const result = await new Promise<string>((resolve) => {
    let resolved = false;
    
    client.on('connect', () => {
      console.log('Client connected (TCP handshake succeeded)');
      events.push('connected');
      // Send initial data to trigger routing
      client.write('test');
    });
    
    client.on('data', (data) => {
      console.log('Client received data:', data.toString());
      events.push('data');
      if (!resolved) {
        resolved = true;
        resolve('data');
      }
    });
    
    client.on('error', (err: any) => {
      console.log('Client error:', err.code);
      events.push('error');
      if (!resolved) {
        resolved = true;
        resolve('error');
      }
    });
    
    client.on('close', () => {
      console.log('Client connection closed by server');
      events.push('closed');
      if (!resolved) {
        resolved = true;
        resolve('closed');
      }
    });
    
    setTimeout(() => {
      if (!resolved) {
        resolved = true;
        resolve('timeout');
      }
    }, 2000);
    
    console.log('Attempting connection from 127.0.0.1...');
    client.connect(9991, '127.0.0.1');
  });
  
  console.log('Connection result:', result);
  console.log('Events:', events);
  
  // The connection might be closed before or after TCP handshake
  // What matters is that the target server never receives a connection
  console.log('Test passed: Connection was properly blocked by security');
  
  // Target server should not have received any connections
  expect(targetServerConnections).toEqual(0);
  
  // Clean up
  client.destroy();
  await proxy.stop();
  await new Promise<void>((resolve) => {
    targetServer.close(() => resolve());
  });
});

tap.test('route security with block list should work', async () => {
  // Create a target server
  let targetServerConnections = 0;
  const targetServer = net.createServer((socket) => {
    targetServerConnections++;
    socket.write('Hello from target');
    socket.end();
  });
  
  await new Promise<void>((resolve) => {
    targetServer.listen(9992, '127.0.0.1', () => resolve());
  });
  
  // Create proxy with security at route level (not action level)
  const routes: IRouteConfig[] = [{
    name: 'secure-route-level',
    match: {
      ports: 9993
    },
    action: {
      type: 'forward',
      target: {
        host: '127.0.0.1',
        port: 9992
      }
    },
    security: {  // Security at route level, not action level
      ipBlockList: ['127.0.0.1', '::1', '::ffff:127.0.0.1']
    }
  }];
  
  const proxy = new smartproxy.SmartProxy({
    enableDetailedLogging: true,
    routes: routes
  });
  
  await proxy.start();
  
  // Try to connect (should be blocked)
  const client = new net.Socket();
  const events: string[] = [];
  
  const result = await new Promise<string>((resolve) => {
    let resolved = false;
    const timeout = setTimeout(() => {
      if (!resolved) {
        resolved = true;
        resolve('timeout');
      }
    }, 2000);
    
    client.on('connect', () => {
      console.log('Client connected to block list test');
      events.push('connected');
      // Send initial data to trigger routing
      client.write('test');
    });
    
    client.on('error', () => {
      events.push('error');
      if (!resolved) {
        resolved = true;
        clearTimeout(timeout);
        resolve('error');
      }
    });
    
    client.on('close', () => {
      events.push('closed');
      if (!resolved) {
        resolved = true;
        clearTimeout(timeout);
        resolve('closed');
      }
    });
    
    client.connect(9993, '127.0.0.1');
  });
  
  // Should connect then be immediately closed by security
  expect(events).toContain('connected');
  expect(events).toContain('closed');
  expect(result).toEqual('closed');
  expect(targetServerConnections).toEqual(0);
  
  // Clean up
  client.destroy();
  await proxy.stop();
  await new Promise<void>((resolve) => {
    targetServer.close(() => resolve());
  });
});

tap.test('route without security should allow all connections', async () => {
  // Create echo server
  const echoServer = net.createServer((socket) => {
    socket.on('data', (data) => {
      socket.write(data);
    });
  });
  
  await new Promise<void>((resolve) => {
    echoServer.listen(9994, '127.0.0.1', () => resolve());
  });
  
  // Create proxy without security
  const routes: IRouteConfig[] = [{
    name: 'open-route',
    match: {
      ports: 9995
    },
    action: {
      type: 'forward',
      target: {
        host: '127.0.0.1',
        port: 9994
      }
    }
    // No security defined
  }];
  
  const proxy = new smartproxy.SmartProxy({
    enableDetailedLogging: false,
    routes: routes
  });
  
  await proxy.start();
  
  // Connect and test echo
  const client = new net.Socket();
  await new Promise<void>((resolve) => {
    client.connect(9995, '127.0.0.1', () => resolve());
  });
  
  // Send data and verify echo
  const testData = 'Hello World';
  client.write(testData);
  
  const response = await new Promise<string>((resolve) => {
    client.once('data', (data) => {
      resolve(data.toString());
    });
    setTimeout(() => resolve(''), 2000);
  });
  
  expect(response).toEqual(testData);
  
  // Clean up
  client.destroy();
  await proxy.stop();
  await new Promise<void>((resolve) => {
    echoServer.close(() => resolve());
  });
});

export default tap.start();