'use strict';
const { execFileSync } = require('child_process');

/**
 * Get current listening TCP ports for the current user.
 * Uses lsof on macOS, ss on Linux.
 * @returns {Set<number>} Set of port numbers
 */
function getListeningPorts() {
  try {
    const platform = process.platform;
    let output;
    if (platform === 'darwin') {
      // lsof: get listening TCP ports for current user
      output = execFileSync('lsof', ['-i', 'TCP', '-sTCP:LISTEN', '-nP', '-Fn'], {
        timeout: 5000, encoding: 'utf8',
      });
      // Parse lsof -Fn output: lines starting with 'n' have addresses like n*:3000 or n127.0.0.1:8080
      const ports = new Set();
      for (const line of output.split('\n')) {
        if (line.startsWith('n')) {
          const match = line.match(/:(\d+)$/);
          if (match) ports.add(parseInt(match[1]));
        }
      }
      return ports;
    } else {
      // Linux: use ss
      output = execFileSync('ss', ['-tlnp'], { timeout: 5000, encoding: 'utf8' });
      const ports = new Set();
      for (const line of output.split('\n')) {
        const match = line.match(/:(\d+)\s/);
        if (match) ports.add(parseInt(match[1]));
      }
      return ports;
    }
  } catch {
    return new Set();
  }
}

/**
 * Detect new ports that appeared after a command ran.
 * @param {Set<number>} before - Ports before command
 * @param {Set<number>} after - Ports after command
 * @returns {number[]} New ports
 */
function diffPorts(before, after) {
  const newPorts = [];
  for (const port of after) {
    if (!before.has(port)) newPorts.push(port);
  }
  return newPorts.sort((a, b) => a - b);
}

/**
 * Check if a shell command looks like it might start a server.
 * @param {string} command
 * @returns {boolean}
 */
function isLikelyServerCommand(command) {
  const patterns = [
    /\b(dev|start|serve|server|preview|watch)\b/i,
    /\bnpm\s+run\s+(dev|start|serve|preview)\b/,
    /\byarn\s+(dev|start|serve|preview)\b/,
    /\bpnpm\s+(dev|start|serve|preview)\b/,
    /\bbun\s+run\s+(dev|start|serve|preview)\b/,
    /\bpython\s+.*manage\.py\s+runserver\b/,
    /\bflask\s+run\b/,
    /\buvicorn\b/,
    /\bgunicorn\b/,
    /\brails\s+s(erver)?\b/,
    /\bnode\s+.*server/i,
    /\bnpx\s+(vite|next|astro|remix)\b/,
    /\.listen\s*\(/,
    /&\s*$/,  // backgrounded command
  ];
  return patterns.some(p => p.test(command));
}

module.exports = { getListeningPorts, diffPorts, isLikelyServerCommand };
