#!/usr/bin/env bash

# Shared shell library for tools that invoke `swift package` commands.
#
# Provides reusable functions for Swift executable resolution, netrc
# handling, registry setup, and running `swift package` with the full
# set of SPM flags.
#
# Usage: source this file and call the functions you need.

# Prints the path to the `swift` executable: via `xcrun` on Apple (the
# selected Xcode toolchain), otherwise from PATH.
spl_resolve_swift_executable() {
  local swift_executable=""
  if command -v xcrun >/dev/null 2>&1; then
    swift_executable="$(xcrun --find swift 2>/dev/null || true)"
  fi
  if [[ -z ${swift_executable} || ! -x ${swift_executable} ]]; then
    swift_executable="$(command -v swift || true)"
  fi
  if [[ -z ${swift_executable} || ! -x ${swift_executable} ]]; then
    echo >&2 "Could not find the swift executable."
    exit 1
  fi
  echo "${swift_executable}"
}

# Creates the config directory and symlinks registries.json into it so
# SPM can find registry configuration.
#
# Arguments:
#   $1 - path to the registries.json file (may be empty)
#   $2 - config_path directory
spl_setup_registries() {
  local registries_json="$1"
  local config_path="$2"
  if [[ -n ${registries_json} ]]; then
    mkdir -p "${config_path}"
    ln -sf "$(readlink -f "${registries_json}")" \
      "${config_path}/registries.json"
  fi
}

# Orchestrates swift executable resolution, netrc/registry setup, and
# executes a `swift package` command with the full set of SPM flags.
#
# Arguments (passed as flag-value pairs):
#   --cmd <update|resolve>
#   --package_path <path>
#   --build_path <path>
#   --cache_path <path>
#   --config_path <path>
#   --security_path <path>
#   --enable_build_manifest_caching <true|false>
#   --enable_dependency_cache <true|false>
#   --manifest_cache <shared|local|none>
#   --netrc_file <path>           (optional, may be empty)
#   --registries_json <path>      (optional, may be empty)
#   --replace_scm_with_registry <true|false>
#   --use_registry_identity_for_scm <true|false>
#   --env <KEY=VAL>               (optional, may be repeated)
#   --manifest_swiftc_flags <flags>  (optional, space-separated)
#
# Any remaining arguments after -- are appended to the swift package
# command.
spl_run_swift_package() {
  local cmd=""
  local package_path=""
  local build_path=""
  local cache_path=""
  local config_path=""
  local security_path=""
  local enable_build_manifest_caching="true"
  local enable_dependency_cache="true"
  local manifest_cache="shared"
  local netrc_file=""
  local registries_json=""
  local replace_scm_with_registry="false"
  local use_registry_identity_for_scm="false"
  local env_vars=()
  local manifest_swiftc_flags=""
  local extra_args=()

  while [[ $# -gt 0 ]]; do
    case "$1" in
      --cmd)
        cmd="$2"
        shift 2
        ;;
      --package_path)
        package_path="$2"
        shift 2
        ;;
      --build_path)
        build_path="$2"
        shift 2
        ;;
      --cache_path)
        cache_path="$2"
        shift 2
        ;;
      --config_path)
        config_path="$2"
        shift 2
        ;;
      --security_path)
        security_path="$2"
        shift 2
        ;;
      --enable_build_manifest_caching)
        enable_build_manifest_caching="$2"
        shift 2
        ;;
      --enable_dependency_cache)
        enable_dependency_cache="$2"
        shift 2
        ;;
      --manifest_cache)
        manifest_cache="$2"
        shift 2
        ;;
      --netrc_file)
        netrc_file="$2"
        shift 2
        ;;
      --registries_json)
        registries_json="$2"
        shift 2
        ;;
      --replace_scm_with_registry)
        replace_scm_with_registry="$2"
        shift 2
        ;;
      --use_registry_identity_for_scm)
        use_registry_identity_for_scm="$2"
        shift 2
        ;;
      --env)
        env_vars+=("$2")
        shift 2
        ;;
      --manifest_swiftc_flags)
        manifest_swiftc_flags="$2"
        shift 2
        ;;
      --)
        shift
        extra_args+=("$@")
        break
        ;;
      *)
        extra_args+=("$1")
        shift
        ;;
    esac
  done

  # Validate required flags. package_path is intentionally omitted
  # because the empty string is valid (root-workspace Package.swift).
  local missing=()
  [[ -z ${cmd} ]] && missing+=("--cmd")
  [[ -z ${build_path} ]] && missing+=("--build_path")
  [[ -z ${cache_path} ]] && missing+=("--cache_path")
  [[ -z ${config_path} ]] && missing+=("--config_path")
  [[ -z ${security_path} ]] && missing+=("--security_path")
  if [[ ${#missing[@]} -gt 0 ]]; then
    echo >&2 "ERROR: missing required flag(s): ${missing[*]}"
    return 1
  fi

  if [[ -z ${BUILD_WORKSPACE_DIRECTORY:-} ]]; then
    echo >&2 "ERROR: BUILD_WORKSPACE_DIRECTORY is not set;" \
      'this target may only be `bazel run`.'
    return 1
  fi

  # Resolve package_path relative to workspace.
  package_path="${BUILD_WORKSPACE_DIRECTORY}/${package_path}"

  # Resolve swift executable.
  local swift_executable
  swift_executable="$(spl_resolve_swift_executable)"

  # Construct dynamic arguments.
  local args=()

  if [[ ${enable_build_manifest_caching} == "true" ]]; then
    args+=("--enable-build-manifest-caching")
  else
    args+=("--disable-build-manifest-caching")
  fi

  if [[ ${enable_dependency_cache} == "true" ]]; then
    args+=("--enable-dependency-cache")
  else
    args+=("--disable-dependency-cache")
  fi

  if [[ ${replace_scm_with_registry} == "true" ]]; then
    args+=("--replace-scm-with-registry")
  fi

  if [[ ${use_registry_identity_for_scm} == "true" ]]; then
    args+=("--use-registry-identity-for-scm")
  fi

  args+=("--manifest-cache" "${manifest_cache}")

  # Set up netrc. Append directly to `args` so paths with spaces stay
  # as a single argv element (the earlier helper+command-substitution
  # approach word-split on IFS).
  if [[ -n ${netrc_file} ]]; then
    args+=("--netrc-file" "$(readlink -f "${netrc_file}")")
  fi

  # Set up registries.
  spl_setup_registries "${registries_json}" "${config_path}"

  # Export environment variables. Each --env pair is already one argv
  # element, so values containing spaces survive unmodified. The length
  # guard keeps this compatible with macOS bash 3.2 + `set -u`, which
  # treats `"${arr[@]}"` on an empty array as an unbound reference.
  if [[ ${#env_vars[@]} -gt 0 ]]; then
    for env_var in "${env_vars[@]}"; do
      export "${env_var?}"
    done
  fi

  if [[ ${#extra_args[@]} -gt 0 ]]; then
    args+=("${extra_args[@]}")
  fi

  # Run the command. Intentionally unquote ${manifest_swiftc_flags} so
  # its space-delimited flag list splits into separate argv elements.
  # shellcheck disable=SC2086
  "${swift_executable}" package \
    ${manifest_swiftc_flags} \
    --build-path "${build_path}" \
    --cache-path "${cache_path}" \
    --config-path "${config_path}" \
    --package-path "${package_path}" \
    --security-path "${security_path}" \
    "${cmd}" \
    "${args[@]}"
}
