{ lib
, runtimeShell
, srcOnly
, writeTextFile
, writeShellScript
, path
, bubblewrap
, coreutils
, curl
, jq
, mitm-cache
, nix
, openssl
, procps
, python3
}:

lib.makeOverridable
({ pkg, pname, attrPath, bwrapFlags, data, silent, useBwrap }:
let
  keep = [ "MITM_CACHE_HOST" "MITM_CACHE_PORT" "MITM_CACHE_ADDRESS" "MITM_CACHE_CA" "MITM_CACHE_CERT_DIR" ];
  gradleScript = writeShellScript "gradle-commands.sh" ''
    set -eo pipefail
    export http_proxy="$MITM_CACHE_ADDRESS"
    export https_proxy="$MITM_CACHE_ADDRESS"
    export SSL_CERT_FILE="$MITM_CACHE_CA"
    export NIX_SSL_CERT_FILE="$MITM_CACHE_CA"
    export GRADLE_USER_HOME="$(${coreutils}/bin/mktemp -d)"
    export IN_GRADLE_UPDATE_DEPS=1
    trap "${coreutils}/bin/rm -rf '$GRADLE_USER_HOME'" SIGINT SIGTERM ERR EXIT
    cd "$(${coreutils}/bin/mktemp -d)"
    ${coreutils}/bin/mkdir out
    export out="$PWD/out"
    trap "${coreutils}/bin/rm -rf '$PWD'" SIGINT SIGTERM ERR EXIT
    source "$stdenv/setup"
    phases="''${prePhases[*]:-} unpackPhase patchPhase ''${preConfigurePhases[*]:-} configurePhase gradleUpdateScript" genericBuild
  '';
  source = srcOnly (pkg.overrideAttrs (old: {
    mitmCache = "";
    gradleInitScript = ./init-deps.gradle;
  }));
  sourceDrvPath = builtins.unsafeDiscardOutputDependency source.drvPath;
  nixShellKeep = lib.concatMapStringsSep " " (x: "--keep ${x}") keep;
in
writeTextFile {
  name = "fetch-deps.sh";
  executable = true;
  # see pkgs/common-updater/combinators.nix
  derivationArgs.passthru =
    { supportedFeatures = lib.optional silent "silent"; }
    // lib.optionalAttrs (attrPath != null) { inherit attrPath; };
  text = ''
    #!${runtimeShell}
    set -eo pipefail
    export PATH="${lib.makeBinPath [
      bubblewrap coreutils curl jq mitm-cache openssl
      procps python3.pkgs.ephemeral-port-reserve
    ]}:$PATH"
    outPath="${
      # if this is an absolute path in nix store, use path relative to the store path
      if lib.hasPrefix "${builtins.storeDir}/" (toString data)
      then builtins.concatStringsSep "/" (lib.drop 1 (lib.splitString "/" (lib.removePrefix "${builtins.storeDir}/" (toString data))))
      # if this is an absolute path anywhere else, just use that path
      else if lib.hasPrefix "/" (toString data)
      then toString data
      # otherwise, use a path relative to the package
      else "${dirOf pkg.meta.position}/${data}"
    }"

    pushd "$(mktemp -d)" >/dev/null
    MITM_CACHE_DIR="$PWD"
    trap "rm -rf '$MITM_CACHE_DIR'" SIGINT SIGTERM ERR EXIT
    openssl genrsa -out ca.key 2048
    openssl req -x509 -new -nodes -key ca.key -sha256 -days 1 -out ca.cer -subj "/C=AL/ST=a/L=a/O=a/OU=a/CN=example.org"
    export MITM_CACHE_HOST=127.0.0.1
    export MITM_CACHE_PORT="''${mitmCachePort:-$(ephemeral-port-reserve "$MITM_CACHE_HOST")}"
    export MITM_CACHE_ADDRESS="$MITM_CACHE_HOST:$MITM_CACHE_PORT"
    # forget all redirects - this makes the lockfiles predictable
    # not only does this strip CDN URLs, but it also improves security - since the redirects aren't
    # stored in the lockfile, a malicious actor can't change the redirect URL stored in the lockfile
    mitm-cache \
      -l"$MITM_CACHE_ADDRESS" \
      record \
      --reject '\.(md5|sha(1|256|512:?):?)$' \
      --forget-redirects-from '.*' \
      --record-text '/maven-metadata\.xml$' >/dev/null 2>/dev/null &
    MITM_CACHE_PID="$!"
    # wait for mitm-cache to fully start
    for i in {0..20}; do
      ps -p "$MITM_CACHE_PID" >/dev/null || (echo "Failed to start mitm-cache" && exit 1)
      curl -so/dev/null "$MITM_CACHE_ADDRESS" && break
      [[ "$i" -eq 20 ]] && (echo "Failed to start mitm-cache" && exit 1)
      sleep 0.5
    done
    trap "kill '$MITM_CACHE_PID'" SIGINT SIGTERM ERR EXIT
    export MITM_CACHE_CERT_DIR="$PWD"
    export MITM_CACHE_CA="$MITM_CACHE_CERT_DIR/ca.cer"
    popd >/dev/null
    useBwrap="''${USE_BWRAP:-${toString useBwrap}}"
    if [ -n "$useBwrap" ]; then
      # bwrap isn't necessary, it's only used to prevent messy build scripts from touching ~
      bwrap \
        --unshare-all --share-net --clearenv --chdir / --setenv HOME /homeless-shelter \
        --tmpfs /home --bind /tmp /tmp --ro-bind /nix /nix --ro-bind /run /run --proc /proc --dev /dev  \
        --ro-bind ${toString path} ${toString path} --bind "$MITM_CACHE_CERT_DIR" "$MITM_CACHE_CERT_DIR" \
        ${builtins.concatStringsSep " " (map (x: "--setenv ${x} \"\$${x}\"") keep)} \
        --setenv NIX_BUILD_SHELL bash ${bwrapFlags} ''${BWRAP_FLAGS:-} \
        -- ${nix}/bin/nix-shell --pure --run ${gradleScript} ${nixShellKeep} ${sourceDrvPath}
    else
      NIX_BUILD_SHELL=bash nix-shell --pure --run ${gradleScript} ${nixShellKeep} ${sourceDrvPath}
    fi${lib.optionalString silent " >&2"}
    kill -s SIGINT "$MITM_CACHE_PID"
    for i in {0..20}; do
      # check for valid json
      if jq -e 1 "$MITM_CACHE_DIR/out.json" >/dev/null 2>/dev/null; then
        exec ${python3.interpreter} ${./compress-deps-json.py} "$MITM_CACHE_DIR/out.json" "$outPath"
      fi
      sleep 1
    done
    exit 1
  '';
})
