{
  autoPatchelfHook,
  bash,
  bbe,
  coreutils,
  cups,
  dbus,
  fetchurl,
  fuse,
  gawk,
  glib,
  lib,
  makeWrapper,
  netcat,
  p7zip,
  perl,
  stdenv,
  timetrap,
  undmg,
  util-linux,
  wayland,
  libxrandr,
  libxi,
  libxinerama,
  libxext,
  libxcomposite,
  libx11,
}:

let
  libPath = lib.concatStringsSep ":" [
    "${glib.out}/lib"
    "${libxrandr}/lib"
    "${wayland.out}/lib"
  ];
  scriptPath = lib.concatStringsSep ":" [
    "${bash}/bin"
    "${coreutils}/bin"
    "${cups}/sbin"
    "${gawk}/bin"
    "${netcat}/bin"
    "${timetrap}/bin"
    "${util-linux}/bin"
  ];
in
stdenv.mkDerivation (finalAttrs: {
  pname = "prl-tools";
  version = "26.4.1-57516";

  # We download the full distribution to extract prl-tools-lin.iso from
  # => ${dmg}/Parallels\ Desktop.app/Contents/Resources/Tools/prl-tools-lin.iso
  src = fetchurl {
    url = "https://download.parallels.com/desktop/v${lib.versions.major finalAttrs.version}/${finalAttrs.version}/ParallelsDesktop-${finalAttrs.version}.dmg";
    hash = "sha256-8ypP8r9dUiOSqdKMEzsMdo3Qm3V2NPM/MBx8sh0kOx8=";
  };

  hardeningDisable = [
    "pic"
    "format"
  ];

  nativeBuildInputs = [
    autoPatchelfHook
    bbe
    makeWrapper
    p7zip
    perl
    undmg
  ];

  buildInputs = [
    fuse
    glib
    libx11
    libxcomposite
    libxext
    libxrandr
    libxi
    libxinerama
  ];

  runtimeDependencies = [
    dbus
    glib
    libxrandr
  ];

  unpackPhase = ''
    runHook preUnpack

    undmg $src
    export sourceRoot=prl-tools-build
    7z x "Parallels Desktop.app/Contents/Resources/Tools/prl-tools-lin${lib.optionalString stdenv.hostPlatform.isAarch64 "-arm"}.iso" -o$sourceRoot
    runHook postUnpack
  '';

  dontBuild = true;

  installPhase = ''
    runHook preInstall

    ( # tools
      cd tools/tools${
        if stdenv.hostPlatform.isAarch64 then
          "-arm64"
        else if stdenv.hostPlatform.isx86_64 then
          "64"
        else
          "32"
      }
      mkdir -p $out/lib

      # prltoolsd contains hardcoded /bin/bash path
      # we're lucky because it uses only -c command
      # => replace to /bin/sh
      bbe -e "s:/bin/bash:/bin/sh\x00\x00:" -o bin/prltoolsd.tmp bin/prltoolsd
      rm -f bin/prltoolsd
      mv bin/prltoolsd.tmp bin/prltoolsd

      # replace hardcoded /usr/bin/prl_fsd
      substituteInPlace ../mount.fuse.prl_fsd \
        --replace-fail "/usr/bin/prl_fsd" "$out/bin/prl_fsd"

      # install binaries
      for i in bin/* sbin/prl_nettool sbin/prl_snapshot; do
        # also patch binaries to replace /usr/bin/XXX to XXX
        # here a two possible cases:
        # 1. it is uses as null terminated string and should be truncated by null;
        # 2. it is uses inside shell script and should be truncated by space.
        for p in bin/* sbin/prl_nettool sbin/prl_snapshot sbin/prlfsmountd; do
          p=$(basename $p)
          bbe -e "s:/usr/bin/$p\x00:./$p\x00\x00\x00\x00\x00\x00\x00\x00:" -o $i.tmp $i
          bbe -e "s:/usr/sbin/$p\x00:./$p\x00\x00\x00\x00\x00\x00\x00\x00 :" -o $i $i.tmp
          bbe -e "s:/usr/bin/$p:$p         :" -o $i.tmp $i
          bbe -e "s:/usr/sbin/$p:$p          :" -o $i $i.tmp
        done

        install -Dm755 $i $out/$i
      done

      install -Dm755 ../../tools/mount.fuse.prl_fsd $out/sbin/mount.fuse.prl_fsd
      install -Dm755 ../../tools/prlfsmountd.sh $out/sbin/prlfsmountd
      install -Dm755 ../../tools/prlbinfmtconfig.sh $out/sbin/prlbinfmtconfig
      for f in $out/bin/* $out/sbin/*; do
        wrapProgram $f \
          --prefix LD_LIBRARY_PATH ':' "${libPath}" \
          --prefix PATH ':' "${scriptPath}"
      done

      for i in lib/libPrl*.0.0; do
        cp $i $out/lib
        ln -s $out/$i $out/''${i%.0.0}
      done

      substituteInPlace ../99prltoolsd-hibernate \
        --replace-fail "/bin/bash" "${bash}/bin/bash"

      mkdir -p $out/etc/pm/sleep.d
      install -Dm644 ../99prltoolsd-hibernate $out/etc/pm/sleep.d
    )

    runHook postInstall
  '';

  passthru.updateScript = ./update.sh;

  meta = {
    description = "Parallels Tools for Linux guests";
    homepage = "https://parallels.com";
    license = lib.licenses.unfree;
    maintainers = with lib.maintainers; [
      wegank
      codgician
    ];
    platforms = lib.platforms.linux;
  };
})
