{ current-date
, current-host-name
, current-user-name
, lib
, stockholm
, StrictHostKeyChecking ? "yes"
}:

let out = {
    inherit deploy;
    inherit infest;
    inherit init;
    inherit nixos-install;
    inherit populate;
  };

  deploy =
    { system ? current-host-name
    , target ? system
    }@args: let
      config = get-config system;
    in ''
      #! /bin/sh
      # ${current-date} ${current-user-name}@${current-host-name}
      # krebs.deploy
      set -efu
      (${populate args})
      ${rootssh target ''
        ${nix-install args}
        ${config.krebs.build.profile}/bin/switch-to-configuration switch
      ''}
      echo OK
    '';

  infest =
    { system ? current-host-name
    , target ? system
    }@args: let
      config = get-config system;
    in ''
      #! /bin/sh
      # ${current-date} ${current-user-name}@${current-host-name}
      # krebs.infest
      set -efu

      ${rootssh target ''
        ${builtins.readFile ./4lib/infest/prepare.sh}
        ${builtins.readFile ./4lib/infest/install-nix.sh}
      ''}

      # Prepare target source via bind-mounting


      (${nixos-install args})

      ${rootssh target ''
        ${builtins.readFile ./4lib/infest/finalize.sh}
      ''}
    '';

  init =
    { system ? current-host-name
    }@args: let
      config = get-config system;
    in ''
      #! /bin/sh
      # ${current-date} ${current-user-name}@${current-host-name}
      # krebs.init
      set -efu

      system=${lib.shell.escape system}
      secrets_dir=${config.krebs.build.source.dir.secrets.path}
      key_type=ed25519
      key_file=$secrets_dir/ssh.id_$key_type
      key_comment=$system

      if test -e "$key_file"; then
        echo "Warning: privkey already exists: $key_file" >&2
      else
        ssh-keygen \
            -C "$key_comment" \
            -t "$key_type" \
            -f "$key_file" \
            -N ""
        rm "$key_file.pub"
      fi

      pubkey=$(ssh-keygen -y -f "$key_file")

      cat<<EOF
      # put following into config.krebs.hosts.$system:
      ssh.privkey.path = <secrets/ssh.$key_type>;
      ssh.pubkey = $(echo $pubkey | jq -R .);
      EOF
    '';

  nixos-install =
    { system ? current-host-name
    , target ? system
    }@args: let
    in ''
      #! /bin/sh
      # ${current-date} ${current-user-name}@${current-host-name}
      # krebs.nixos-install
      (${populate (args // { root = "/mnt"; })})

      ${rootssh target ''
        export PATH; PATH=/root/.nix-profile/bin:$PATH

        src=$(type -p nixos-install)
        cat_src() {
          sed < "$src" "$(
            { sed < "$src" -n '
                  /^if ! test -e "\$mountPoint\/\$NIXOS_CONFIG/,/^fi$/=
                  /^nixpkgs=/=
                  /^NIX_PATH=/,/^$/{/./=}

                  # Disable: Copy the NixOS/Nixpkgs sources to the target as
                  # the initial contents of the NixOS channel.
                  /^srcs=/,/^ln -sfn /=
                '
            } | sed 's:$:s/^/#krebs#/:'
          )"
        }

        # Location to insert `nix-install`
        i=$(sed -n '/^echo "building the system configuration/=' "$src")

        {
          cat_src | sed -n "1,$i{p}"
          cat ${doc (nix-install args)}
          cat_src | sed -n "$i,\''${$i!p}"
        } > nixos-install
        chmod +x nixos-install

        # Wrap inserted nix-install into chroot.
        nix_env=$(cat_src | sed -n '
          s:.*\(/nix/store/[a-z0-9]*-nix-[0-9.]\+/bin/nix-env\).*:\1:p;T;q
        ')
        echo "nix-env is $nix_env" >&2
        findpkg() {(
          name=$1
          path=$(find /nix/store \
              -mindepth 1 -maxdepth 1 -type d -name '*-'"$name"'-*' \
            | head -n 1 | sed s:^/mnt::)
          if echo "$path" | grep .; then
            echo "$name is $path" >&2
          else
            echo "Error: package not found: $name" >&2
            exit 1
          fi
        )}
        cacert=$(findpkg cacert)
        coreutils=$(findpkg coreutils)
        cp "$cacert"/etc/ssl/certs/ca-bundle.crt /mnt/root/SSL_CERT_FILE
        env="$coreutils/bin/env SSL_CERT_FILE=/root/SSL_CERT_FILE"
        sed -i '
          s:^NIX_PATH=:chroot $mountPoint '"$env"' &:
          s:^nix-env:'"$nix_env"':
        ' nixos-install

        ./nixos-install
      ''}
    '';

  doc = s:
    let b = "EOF${builtins.hashString "sha256" s}"; in
    ''
    <<\${b}
    ${s}
    ${b}
    '';

  get-config = system:
    stockholm.users.${current-user-name}.${system}.config
      or (abort "unknown system: ${system}, user: ${current-user-name}");

  nix-install =
    { system ? current-host-name
    , target ? system
    }:
    let
      config = get-config system;

      nix-path =
        lib.concatStringsSep ":"
          (lib.mapAttrsToList (name: src: "${name}=${src.target-path}")
            (config.krebs.build.source.dir //
             config.krebs.build.source.git));
    in ''
      set -efu
      NIX_PATH=${lib.shell.escape nix-path} \
      nix-env \
        --show-trace \
        -f '<stockholm>' \
        --argstr current-date ${lib.shell.escape current-date} \
        --argstr current-host-name ${lib.shell.escape current-host-name} \
        --argstr current-user-name ${lib.shell.escape current-user-name} \
        --profile ${lib.shell.escape config.krebs.build.profile} \
        --set \
        -A ${lib.escapeShellArg (lib.concatStringsSep "." [
              "users"
              config.krebs.build.user.name
              config.krebs.build.host.name
              "system"
            ])}
    '';

  populate =
    { system ? current-host-name
    , target ? system
    , root ? ""
    }@args:
    let out = ''
        #! /bin/sh
        # ${current-date} ${current-user-name}@${current-host-name}
        set -efu
        ${lib.concatStringsSep "\n"
          (lib.concatMap
            (type: lib.mapAttrsToList (_: methods.${type})
                                      config.krebs.build.source.${type})
            ["dir" "git"])}
      '';


      config = get-config system;

      current-host = config.krebs.hosts.${current-host-name};
      current-user = config.krebs.users.${current-user-name};

      methods.dir = config:
        let
          can-push = config.host.name == current-host.name;
          target-path = root + config.target-path;
          push-method = ''
            rsync \
              --exclude .git \
              --exclude .graveyard \
              --exclude old \
              --exclude tmp \
              --rsync-path='mkdir -p ${target-path} && rsync' \
              --delete-excluded \
              -vrLptgoD \
              ${config.path}/ \
              root@${target}:${target-path}
          '';
        in
        if can-push then push-method else
        let dir = "file://${config.host.name}${config.path}"; in
        # /!\ revise this message when using more than just push-method
        throw "No way to push ${dir} from ${current-host.name} to ${target}";

      methods.git = config:
        let target-path = root + config.target-path;
        in rootssh target ''
          mkdir -p ${target-path}
          cd ${target-path}
          if ! test -e .git; then
            git init
          fi
          if ! cur_url=$(git config remote.origin.url 2>/dev/null); then
            git remote add origin ${config.url}
          elif test "$cur_url" != ${config.url}; then
            git remote set-url origin ${config.url}
          fi
          if test "$(git rev-parse --verify HEAD 2>/dev/null)" != ${config.rev}; then
            git fetch origin
            git checkout ${config.rev} -- .
            git checkout -q ${config.rev}
            git submodule init
            git submodule update
          fi
          git clean -dxf
        '';
    in out;

  rootssh = target: script:
    let
      flags = "-o StrictHostKeyChecking=${StrictHostKeyChecking}";
    in
    "ssh ${flags} root@${target} -T ${doc ''
      set -efu
      ${script}
    ''}";

in out