'use strict'

const process = require('node:process')
const debug = require('./@socketsecurity/registry/lib/debug')
const npm = require('./@socketsecurity/registry/lib/npm')
const spawn = require('./@socketsecurity/registry/lib/spawn')
const path = require('node:path')
const vendor = require('./vendor.js')
const shadowNpmPaths = require('./shadow-npm-paths.js')
const constants = require('./constants.js')

const { CLI, NPX } = constants
async function installLinks(realBinPath, binName) {
  const isNpx = binName === NPX
  // Find package manager being shadowed by this process.
  const binPath = isNpx
    ? shadowNpmPaths.getNpxBinPath()
    : shadowNpmPaths.getNpmBinPath()
  // Lazily access constants.WIN32.
  const { WIN32 } = constants
  // TODO: Is this early exit needed?
  if (WIN32 && binPath) {
    return binPath
  }
  const shadowed = isNpx
    ? shadowNpmPaths.isNpxBinPathShadowed()
    : shadowNpmPaths.isNpmBinPathShadowed()
  // Move our bin directory to front of PATH so its found first.
  if (!shadowed) {
    if (WIN32) {
      await vendor.libExports(
        // Lazily access constants.rootDistPath.
        path.join(constants.rootDistPath, `${binName}-${CLI}.js`),
        path.join(realBinPath, binName)
      )
    }
    const { env } = process
    env['PATH'] = `${realBinPath}${path.delimiter}${env['PATH']}`
  }
  return binPath
}

const { SOCKET_CLI_SAFE_BIN, SOCKET_CLI_SAFE_PROGRESS, SOCKET_IPC_HANDSHAKE } =
  constants
async function shadowBin(binName, args = process.argv.slice(2)) {
  process.exitCode = 1
  const useDebug = debug.isDebug()
  const terminatorPos = args.indexOf('--')
  const rawBinArgs = terminatorPos === -1 ? args : args.slice(0, terminatorPos)
  const progressArg =
    rawBinArgs.findLast(npm.isProgressFlag) !== '--no-progress'
  const binArgs = rawBinArgs.filter(a => !npm.isProgressFlag(a))
  const otherArgs = terminatorPos === -1 ? [] : args.slice(terminatorPos)
  const isSilent = !useDebug && !binArgs.some(npm.isLoglevelFlag)
  // The default value of loglevel is "notice". We default to "error" which is
  // two levels quieter.
  const logLevelArgs = isSilent ? ['--loglevel', 'error'] : []
  const spawnPromise = spawn.spawn(
    // Lazily access constants.execPath.
    constants.execPath,
    [
      // Lazily access constants.nodeHardenFlags.
      ...constants.nodeHardenFlags,
      // Lazily access constants.nodeNoWarningsFlags.
      ...constants.nodeNoWarningsFlags,
      // Lazily access true.
      ...[
        '--require',
        // Lazily access constants.distInstrumentWithSentryPath.
        constants.distInstrumentWithSentryPath
      ],
      '--require',
      // Lazily access constants.distShadowNpmInjectPath.
      constants.distShadowNpmInjectPath,
      // Lazily access constants.shadowBinPath.
      await installLinks(constants.shadowBinPath, binName),
      // Add '--no-progress' to fix input being swallowed by the npm spinner.
      '--no-progress',
      // Add '--loglevel=error' if a loglevel flag is not provided and the
      // SOCKET_CLI_DEBUG environment variable is not truthy.
      ...logLevelArgs,
      ...binArgs,
      ...otherArgs
    ],
    {
      // 'inherit' + 'ipc'
      stdio: [0, 1, 2, 'ipc']
    }
  )
  // See https://nodejs.org/api/all.html#all_child_process_event-exit.
  spawnPromise.process.on('exit', (code, signalName) => {
    if (signalName) {
      process.kill(process.pid, signalName)
    } else if (code !== null) {
      // eslint-disable-next-line n/no-process-exit
      process.exit(code)
    }
  })
  spawnPromise.process.send({
    [SOCKET_IPC_HANDSHAKE]: {
      [SOCKET_CLI_SAFE_BIN]: binName,
      [SOCKET_CLI_SAFE_PROGRESS]: progressArg
    }
  })
  await spawnPromise
}

module.exports = shadowBin
//# debugId=2fb167bf-d863-426f-96ab-61ad5b9e036
//# sourceMappingURL=shadow-bin.js.map
