import { exec, spawn } from 'node:child_process';
import { promisify } from 'node:util';

const execAsync = promisify(exec);
async function executeCommand(command, args = [], options = {}) {
  try {
    const fullCommand = buildCommand(command, args);
    const execOptions = {
      cwd: options.cwd,
      env: { ...process.env, ...options.env },
      timeout: options.timeout,
      shell: typeof options.shell === "string" ? options.shell : options.shell ? "/bin/sh" : void 0,
      encoding: options.encoding || "utf8",
      maxBuffer: options.maxBuffer || 1024 * 1024 * 10
      // 10MB default
    };
    const { stdout, stderr } = await execAsync(fullCommand, execOptions);
    return {
      success: true,
      stdout: stdout.toString().trim(),
      stderr: stderr.toString().trim(),
      exitCode: 0
    };
  } catch (error) {
    return {
      success: false,
      stdout: error.stdout?.toString().trim() || "",
      stderr: error.stderr?.toString().trim() || "",
      exitCode: error.code || 1,
      error: error.message
    };
  }
}
function executeCommandStream(command, args = [], options = {}) {
  return new Promise((resolve) => {
    const spawnOptions = {
      cwd: options.cwd,
      env: { ...process.env, ...options.env },
      shell: options.shell !== false
    };
    const child = spawn(command, args, spawnOptions);
    let stdout = "";
    let stderr = "";
    child.stdout?.on("data", (data) => {
      const text = data.toString();
      stdout += text;
      if (options.onStdout) {
        options.onStdout(text);
      }
    });
    child.stderr?.on("data", (data) => {
      const text = data.toString();
      stderr += text;
      if (options.onStderr) {
        options.onStderr(text);
      }
    });
    child.on("close", (code) => {
      resolve({
        success: code === 0,
        stdout: stdout.trim(),
        stderr: stderr.trim(),
        exitCode: code || 0,
        error: code !== 0 ? `Command exited with code ${code}` : void 0
      });
    });
    child.on("error", (error) => {
      resolve({
        success: false,
        stdout: stdout.trim(),
        stderr: stderr.trim(),
        exitCode: 1,
        error: error.message
      });
    });
    if (options.timeout) {
      setTimeout(() => {
        child.kill();
        resolve({
          success: false,
          stdout: stdout.trim(),
          stderr: stderr.trim(),
          exitCode: 1,
          error: `Command timed out after ${options.timeout}ms`
        });
      }, options.timeout);
    }
  });
}
function buildCommand(command, args) {
  const escapedArgs = args.map(escapeArgument);
  return [command, ...escapedArgs].join(" ");
}
function escapeArgument(arg) {
  if (/[\s"'`$&|;<>(){}[\]\\]/.test(arg)) {
    const escaped = arg.replace(/"/g, '\\"');
    return `"${escaped}"`;
  }
  return arg;
}
async function commandExists(command) {
  const isWindows = process.platform === "win32";
  const checkCommand = isWindows ? "where" : "which";
  try {
    const result = await executeCommand(checkCommand, [command]);
    return result.success;
  } catch {
    return false;
  }
}
async function getCommandPath(command) {
  const isWindows = process.platform === "win32";
  const checkCommand = isWindows ? "where" : "which";
  try {
    const result = await executeCommand(checkCommand, [command]);
    if (result.success) {
      return result.stdout.split("\n")[0].trim();
    }
    return null;
  } catch {
    return null;
  }
}
function parseVersion(output) {
  const patterns = [
    /version\s+v?(\d+\.\d+\.\d+)/i,
    /v?(\d+\.\d+\.\d+)/,
    /(\d+\.\d+\.\d+)/
  ];
  for (const pattern of patterns) {
    const match = output.match(pattern);
    if (match) {
      return match[1];
    }
  }
  return null;
}
async function getCommandVersion(command, versionFlag = "--version") {
  try {
    const result = await executeCommand(command, [versionFlag]);
    if (result.success) {
      return parseVersion(result.stdout || result.stderr);
    }
    return null;
  } catch {
    return null;
  }
}
async function executeCommandSequence(commands) {
  const results = [];
  for (const cmd of commands) {
    const result = await executeCommand(
      cmd.command,
      cmd.args || [],
      cmd.options || {}
    );
    results.push(result);
    if (!result.success) {
      break;
    }
  }
  return results;
}
async function executeCommandParallel(commands) {
  const promises = commands.map(
    (cmd) => executeCommand(cmd.command, cmd.args || [], cmd.options || {})
  );
  return Promise.all(promises);
}

export { escapeArgument as a, buildCommand as b, commandExists as c, executeCommandParallel as d, executeCommand as e, executeCommandSequence as f, executeCommandStream as g, getCommandPath as h, getCommandVersion as i, parseVersion as p };
