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

tap.test('stuck connection cleanup - verify connections to hanging backends are cleaned up', async (tools) => {
  console.log('\n=== Stuck Connection Cleanup Test ===');
  console.log('Purpose: Verify that connections to backends that accept but never respond are cleaned up');
  
  // Create a hanging backend that accepts connections but never responds
  let backendConnections = 0;
  const hangingBackend = net.createServer((socket) => {
    backendConnections++;
    console.log(`Hanging backend: Connection ${backendConnections} received`);
    // Accept the connection but never send any data back
    // This simulates a hung backend service
  });
  
  await new Promise<void>((resolve) => {
    hangingBackend.listen(9997, () => {
      console.log('✓ Hanging backend started on port 9997');
      resolve();
    });
  });
  
  // Create proxy that forwards to hanging backend
  const proxy = new SmartProxy({
    routes: [{
      name: 'to-hanging-backend',
      match: { ports: 8589 },
      action: {
        type: 'forward',
        target: { host: 'localhost', port: 9997 }
      }
    }],
    keepAlive: true,
    enableDetailedLogging: false,
    inactivityTimeout: 5000, // 5 second inactivity check interval for faster testing
  });
  
  await proxy.start();
  console.log('✓ Proxy started on port 8589');
  
  // Create connections that will get stuck
  console.log('\n--- Creating connections to hanging backend ---');
  const clients: net.Socket[] = [];
  
  for (let i = 0; i < 5; i++) {
    const client = net.connect(8589, 'localhost');
    clients.push(client);
    
    await new Promise<void>((resolve) => {
      client.on('connect', () => {
        console.log(`Client ${i} connected`);
        // Send data that will never get a response
        client.write(`GET / HTTP/1.1\r\nHost: localhost\r\n\r\n`);
        resolve();
      });
      
      client.on('error', (err) => {
        console.log(`Client ${i} error: ${err.message}`);
        resolve();
      });
    });
  }
  
  // Wait a moment for connections to establish
  await plugins.smartdelay.delayFor(1000);
  
  // Check initial connection count
  const initialCount = (proxy as any).connectionManager.getConnectionCount();
  console.log(`\nInitial connection count: ${initialCount}`);
  expect(initialCount).toEqual(5);
  
  // Get connection details
  const connections = (proxy as any).connectionManager.getConnections();
  let stuckCount = 0;
  
  for (const [id, record] of connections) {
    if (record.bytesReceived > 0 && record.bytesSent === 0) {
      stuckCount++;
      console.log(`Stuck connection ${id}: received=${record.bytesReceived}, sent=${record.bytesSent}`);
    }
  }
  
  console.log(`Stuck connections found: ${stuckCount}`);
  expect(stuckCount).toEqual(5);
  
  // Wait for inactivity check to run (it checks every 30s by default, but we set it to 5s)
  console.log('\n--- Waiting for stuck connection detection (65 seconds) ---');
  console.log('Note: Stuck connections are cleaned up after 60 seconds with no response');
  
  // Speed up time by manually triggering inactivity check after simulating time passage
  // First, age the connections by updating their timestamps
  const now = Date.now();
  for (const [id, record] of connections) {
    // Simulate that these connections are 61 seconds old
    record.incomingStartTime = now - 61000;
    record.lastActivity = now - 61000;
  }
  
  // Manually trigger inactivity check
  console.log('Manually triggering inactivity check...');
  (proxy as any).connectionManager.performOptimizedInactivityCheck();
  
  // Wait for cleanup to complete
  await plugins.smartdelay.delayFor(1000);
  
  // Check connection count after cleanup
  const afterCleanupCount = (proxy as any).connectionManager.getConnectionCount();
  console.log(`\nConnection count after cleanup: ${afterCleanupCount}`);
  
  // Verify termination stats
  const stats = (proxy as any).connectionManager.getTerminationStats();
  console.log('\nTermination stats:', stats);
  
  // All connections should be cleaned up as "stuck_no_response"
  expect(afterCleanupCount).toEqual(0);
  
  // The termination reason might be under incoming or general stats
  const stuckCleanups = (stats.incoming.stuck_no_response || 0) + 
                       (stats.outgoing?.stuck_no_response || 0);
  console.log(`Stuck cleanups detected: ${stuckCleanups}`);
  expect(stuckCleanups).toBeGreaterThan(0);
  
  // Verify clients were disconnected
  let closedClients = 0;
  for (const client of clients) {
    if (client.destroyed) {
      closedClients++;
    }
  }
  console.log(`Closed clients: ${closedClients}/5`);
  expect(closedClients).toEqual(5);
  
  // Cleanup
  console.log('\n--- Cleanup ---');
  await proxy.stop();
  hangingBackend.close();
  
  console.log('✓ Test complete: Stuck connections are properly detected and cleaned up');
});

tap.start();