/**
 * SwarmHack Node.js API
 * Programmatic interface for running SwarmHack scans
 */

const { spawn } = require('child_process');
const path = require('path');
const fs = require('fs');
const os = require('os');

/**
 * Get path to SwarmHack binary
 */
function getBinaryPath() {
  const platform = os.platform();
  const arch = os.arch();
  const binaryName = platform === 'win32' ? 'swarmhack.exe' : 'swarmhack';

  const locations = [
    path.join(__dirname, 'native', `${platform}-${arch}`, binaryName),
    path.join(__dirname, 'native', binaryName),
    path.join(__dirname, '..', binaryName),
  ];

  for (const loc of locations) {
    if (fs.existsSync(loc)) {
      return loc;
    }
  }

  throw new Error(`SwarmHack binary not found for ${platform}-${arch}`);
}

/**
 * Get path to bundled config file
 */
function getConfigPath() {
  // Check for local config first (user's project)
  const localConfig = path.join(process.cwd(), 'config', 'swarmhack.yaml');
  if (fs.existsSync(localConfig)) {
    return localConfig;
  }

  // Use bundled config from npm package
  const bundledConfig = path.join(__dirname, 'config', 'swarmhack.yaml');
  if (fs.existsSync(bundledConfig)) {
    return bundledConfig;
  }

  return null;
}

/**
 * Run SwarmHack with arguments
 * @param {string[]} args - Command line arguments
 * @param {object} options - Spawn options
 * @param {boolean} options.useConfig - Whether to use bundled config (default: true)
 * @returns {Promise<{code: number, stdout: string, stderr: string}>}
 */
function run(args = [], options = {}) {
  return new Promise((resolve, reject) => {
    const binaryPath = getBinaryPath();
    const stdout = [];
    const stderr = [];

    // Add config flag if not already specified and useConfig is true (default)
    let finalArgs = args;
    if (options.useConfig !== false && !args.includes('--config') && !args.includes('-c')) {
      const configPath = getConfigPath();
      if (configPath) {
        finalArgs = ['--config', configPath, ...args];
      }
    }

    const child = spawn(binaryPath, finalArgs, {
      ...options,
      env: { ...process.env, ...options.env },
    });

    child.stdout?.on('data', (data) => stdout.push(data));
    child.stderr?.on('data', (data) => stderr.push(data));

    child.on('close', (code) => {
      resolve({
        code: code || 0,
        stdout: Buffer.concat(stdout).toString(),
        stderr: Buffer.concat(stderr).toString(),
      });
    });

    child.on('error', reject);
  });
}

/**
 * Run a security scan
 * @param {object} config - Scan configuration
 * @param {string} config.target - Target URL
 * @param {string[]} config.agents - Agents to run (sqli, xss, etc.)
 * @param {string} config.customer - Prancer customer ID
 * @param {string} config.token - Prancer API token
 * @param {string} config.output - Output file path
 * @returns {Promise<object>} - Scan results
 */
async function scan(config) {
  const args = ['spawn'];

  if (config.target) args.push('--target', config.target);
  if (config.agents) args.push('--agents', config.agents.join(','));
  if (config.customer) args.push('--customer', config.customer);
  if (config.token) args.push('--token', config.token);
  if (config.output) args.push('--output', config.output);
  if (config.timeout) args.push('--timeout', String(config.timeout));

  args.push('--format', 'json');

  const result = await run(args);

  if (result.code !== 0) {
    throw new Error(`Scan failed: ${result.stderr || result.stdout}`);
  }

  // Try to parse JSON output
  try {
    return JSON.parse(result.stdout);
  } catch {
    return { raw: result.stdout };
  }
}

/**
 * Get SwarmHack version
 */
async function version() {
  const result = await run(['--version']);
  return result.stdout.trim();
}

/**
 * Check if SwarmHack is installed and working
 */
async function doctor() {
  try {
    const binaryPath = getBinaryPath();
    const result = await run(['doctor']);
    return {
      installed: true,
      binaryPath,
      healthy: result.code === 0,
      output: result.stdout,
    };
  } catch (err) {
    return {
      installed: false,
      error: err.message,
    };
  }
}

module.exports = {
  run,
  scan,
  version,
  doctor,
  getBinaryPath,
  getConfigPath,
};
