#!/usr/bin/env bun
/**
 * Redis/Dragonfly Connection Monitor - Simplified Version
 * 
 * Shows active connections grouped by client name with basic stats
 * 
 * Usage:
 *   bun run scripts/get-redis-connections.ts           # Show connections once
 *   bun run scripts/get-redis-connections.ts --monitor # Monitor in real-time
 */

import Redis from 'ioredis';

interface ClientInfo {
  id: string;
  name?: string;
  addr: string;
  age: string;
  idle: string;
  cmd: string;
  tot?: string;
  fd?: string;
  flags?: string;
  db?: string;
  sub?: string;
  psub?: string;
  multi?: string;
  qbuf?: string;
  qbufFree?: string;
  obl?: string;
  oll?: string;
  omem?: string;
  events?: string;
  [key: string]: string | undefined; // Index signature for dynamic properties
}

interface GroupedConnections {
  [clientName: string]: {
    count: number;
    connections: ClientInfo[];
    totalCommands: number;
    avgIdleTime: number;
  };
}

class RedisConnectionMonitor {
  private redis: Redis;
  
  constructor() {
    this.redis = new Redis({
      host: process.env.DRAGONFLY_HOST || 'localhost',
      port: parseInt(process.env.DRAGONFLY_PORT || '6379'),
      password: process.env.DRAGONFLY_PASSWORD || undefined,
      db: parseInt(process.env.DRAGONFLY_DATABASE || '0'),
      maxRetriesPerRequest: 3,
      connectTimeout: 10000,
      lazyConnect: true,
    });
  }

  /**
   * Parse CLIENT LIST output into structured data
   */
  private parseClientList(clientListOutput: string): ClientInfo[] {
    const lines = clientListOutput.trim().split('\n');
    const clients: ClientInfo[] = [];

    for (const line of lines) {
      if (!line.trim()) continue;
      
      const client: ClientInfo = {
        id: '',
        addr: '',
        age: '',
        idle: '',
        cmd: '',
      };

      // Parse key=value pairs
      const pairs = line.split(' ');
      for (const pair of pairs) {
        const [key, value] = pair.split('=');
        if (key && value !== undefined) {
          client[key] = value;
        }
      }

      clients.push(client);
    }

    return clients;
  }

  /**
   * Clean up client names by removing timestamp/number suffixes
   */
  private cleanClientName(rawName: string): string {
    if (!rawName || rawName === 'unnamed') {
      return rawName;
    }

    // Remove timestamp/number suffixes (like BATCH-PROCESSOR-1749573709724)
    // Pattern: Remove dash followed by numbers at the end
    let cleanName = rawName.replace(/-\d+$/, '');
    
    // Also handle patterns like NAME---1-1- (RedisInsight style)
    cleanName = cleanName.replace(/---\d+-\d+-?$/, '');
    
    // Convert to a more readable format
    cleanName = cleanName
      .replace(/-/g, ' ')
      .toLowerCase()
      .replace(/\b\w/g, l => l.toUpperCase());

    return cleanName;
  }

  /**
   * Group connections by client name/type
   */
  private groupConnectionsByName(clients: ClientInfo[]): GroupedConnections {
    const grouped: GroupedConnections = {};

    for (const client of clients) {
      // Determine client name - use 'name' field if set, otherwise infer from other properties
      let clientName = client.name || 'unnamed';
      
      // Clean up the client name by removing timestamps/numbers
      if (clientName && clientName !== 'unnamed') {
        clientName = this.cleanClientName(clientName);
      }
      
      // If no name is set, try to infer from the connection characteristics
      if (clientName === 'unnamed' || !clientName) {
        if (client.flags?.includes('M')) {
          clientName = 'Master Connection';
        } else if (client.flags?.includes('S')) {
          clientName = 'Slave Connection';
        } else if (client.flags?.includes('P')) {
          clientName = 'PubSub Connection';
        } else if (client.cmd === 'monitor') {
          clientName = 'Monitor Connection';
        } else if (client.addr.includes('127.0.0.1') || client.addr.includes('localhost')) {
          clientName = 'Local Client';
        } else if (client.addr.includes('172.') || client.addr.includes('192.168.')) {
          clientName = 'Docker Network Client';
        } else {
          clientName = `Client ${client.addr.split(':')[0]}`;
        }
      }

      if (!grouped[clientName]) {
        grouped[clientName] = {
          count: 0,
          connections: [],
          totalCommands: 0,
          avgIdleTime: 0,
        };
      }

      grouped[clientName].count++;
      grouped[clientName].connections.push(client);
      
      // Parse command count if available
      const cmdCount = client.tot ? parseInt(client.tot) : 0;
      grouped[clientName].totalCommands += cmdCount;
    }

    // Calculate average idle times
    for (const groupName in grouped) {
      const group = grouped[groupName];
      const totalIdle = group.connections.reduce((sum, client) => {
        return sum + parseInt(client.idle || '0');
      }, 0);
      group.avgIdleTime = group.count > 0 ? Math.round(totalIdle / group.count) : 0;
    }

    return grouped;
  }

  /**
   * Format connection details for display
   */
  private formatConnectionDetails(client: ClientInfo): string {
    const details = [
      `ID: ${client.id}`,
      `Address: ${client.addr}`,
      `Age: ${client.age}s`,
      `Idle: ${client.idle}s`,
    ];
    
    if (client.tot) {
      details.push(`Total Commands: ${client.tot}`);
    }
    
    return details.join(', ');
  }

  /**
   * Get and display all connections grouped by name
   */
  async getConnectionsGroupedByName(): Promise<void> {
    try {
      console.log('🔍 Connecting to Dragonfly...');
      
      // Check if already connected, if not connect
      if (this.redis.status !== 'ready') {
        await this.redis.connect();
      }
      
      console.log('📡 Fetching client connections...');
      const clientListOutput = await this.redis.client('LIST') as string;
      
      if (!clientListOutput) {
        console.log('❌ No client information available');
        return;
      }

      const clients = this.parseClientList(clientListOutput);
      const grouped = this.groupConnectionsByName(clients);

      // Get server and memory info first
      const info = await this.redis.info('server');
      const memoryInfo = await this.redis.info('memory');
      
      // Parse server info
      const infoLines = info.split('\n');
      let version = '', uptime = '', connectedClients = '';
      for (const line of infoLines) {
        if (line.includes('dragonfly_version')) version = line.split(':')[1]?.trim() || '';
        if (line.includes('uptime_in_seconds')) {
          const seconds = parseInt(line.split(':')[1]?.trim() || '0');
          const days = Math.floor(seconds / 86400);
          const hours = Math.floor((seconds % 86400) / 3600);
          uptime = `${days}d ${hours}h`;
        }
        if (line.includes('connected_clients')) connectedClients = line.split(':')[1]?.trim() || '';
      }
      
      // Parse memory info
      const memoryLines = memoryInfo.split('\n');
      let usedMemory = '', maxMemory = '', memoryPercent = '';
      for (const line of memoryLines) {
        if (line.includes('used_memory_human')) usedMemory = line.split(':')[1]?.trim() || '';
        if (line.includes('maxmemory_human')) maxMemory = line.split(':')[1]?.trim() || '';
        if (line.includes('used_memory_percentage')) memoryPercent = line.split(':')[1]?.trim() || '';
      }

      console.log('\n🎯 DRAGONFLY CONNECTION SUMMARY');
      console.log('═'.repeat(60));
      console.log(`🔗 Connections: ${clients.length} active (${Object.keys(grouped).length} groups) | 🖥️  Version: ${version} | ⏱️  Uptime: ${uptime}`);
      console.log(`💾 Memory: ${usedMemory}${maxMemory ? ` / ${maxMemory}` : ''}${memoryPercent ? ` (${memoryPercent}%)` : ''}`);
      console.log('');

      // Sort groups by count (most connections first)
      const sortedGroups = Object.entries(grouped).sort((a, b) => b[1].count - a[1].count);

      // Display grouped connections
      for (const [groupName, group] of sortedGroups) {
        console.log(`📂 ${groupName.toUpperCase()} (${group.count} connections, ${group.totalCommands} commands, ${group.avgIdleTime}s avg idle)`);
        
        for (const client of group.connections) {
          console.log(`   ├─ ${this.formatConnectionDetails(client)}`);
        }
      }

    } catch (error) {
      console.error('❌ Error connecting to Dragonfly:', error);
      console.log('\n💡 Troubleshooting:');
      console.log('   - Make sure Dragonfly Docker container is running');
      console.log('   - Check if port 6379 is accessible');
      console.log('   - Verify environment variables if using custom connection settings');
      console.log('   - Run: docker ps | grep dragonfly');
    } finally {
      // Only disconnect if we're not in monitoring mode
      if (this.redis.status === 'ready') {
        await this.redis.disconnect();
      }
    }
  }

  /**
   * Monitor connections in real-time
   */
  async monitorConnections(intervalSeconds: number = 5): Promise<void> {
    console.log(`🔄 Starting real-time monitoring (refresh every ${intervalSeconds}s)`);
    console.log('Press Ctrl+C to stop...\n');

    const monitor = async () => {
      try {
        console.clear();
        console.log(`🕐 Last updated: ${new Date().toLocaleTimeString()}\n`);
        await this.getConnectionsGroupedByName();
      } catch (error) {
        console.error('❌ Monitoring error:', error);
        console.log('Retrying in next interval...');
      }
    };

    // Initial run
    await monitor();

    // Set up interval
    const interval = setInterval(monitor, intervalSeconds * 1000);

    // Handle graceful shutdown
    process.on('SIGINT', async () => {
      clearInterval(interval);
      console.log('\n👋 Monitoring stopped');
      await this.redis.disconnect();
      process.exit(0);
    });
  }
}

// Main execution
async function main() {
  const args = process.argv.slice(2);
  const monitor = new RedisConnectionMonitor();

  if (args.includes('--monitor') || args.includes('-m')) {
    const intervalArg = args.find(arg => arg.startsWith('--interval='));
    const interval = intervalArg ? parseInt(intervalArg.split('=')[1]) : 5;
    await monitor.monitorConnections(interval);
  } else {
    await monitor.getConnectionsGroupedByName();
  }
}

// Help text
if (process.argv.includes('--help') || process.argv.includes('-h')) {
  console.log(`
🐉 Redis/Dragonfly Connection Monitor

Usage:
  bun run scripts/get-redis-connections.ts [options]

Options:
  --help, -h              Show this help message
  --monitor, -m           Monitor connections in real-time
  --interval=N            Set monitoring refresh interval in seconds (default: 5)

Examples:
  bun run scripts/get-redis-connections.ts                    # One-time connection list
  bun run scripts/get-redis-connections.ts --monitor          # Real-time monitoring
  bun run scripts/get-redis-connections.ts -m --interval=10   # Monitor every 10 seconds

Environment Variables:
  DRAGONFLY_HOST          Dragonfly host (default: localhost)
  DRAGONFLY_PORT          Dragonfly port (default: 6379)
  DRAGONFLY_PASSWORD      Dragonfly password (if auth enabled)
  DRAGONFLY_DATABASE      Database number (default: 0)

Features:
  ✨ Groups connections by clean names (removes timestamp suffixes)
  📊 Shows connection statistics and command counts
  🔄 Real-time monitoring mode available
  🎨 Color-coded output with connection details
`);
  process.exit(0);
}

// Run the script
console.log('🚀 Starting Redis connection monitor...');
main().catch((error) => {
  console.error('❌ Script error:', error);
  process.exit(1);
});
