{ opt, hostname, lib }:
with lib; let
  cfg = opt // {
    hostname = hostname;
    autoReboot = boolToString opt.autoReboot;
    entireDisk = boolToString opt.partitioning.useEntireDisk;
    nullifyDisk = boolToString opt.partitioning.nullifyDisk;
    disk = opt.partitioning.disk or "0";
    bootPartition = opt.partitioning.partitions.bootPartition or "0";
    rootPartition = opt.partitioning.partitions.rootPartition or "0";
    swapPartition = opt.partitioning.partitions.swapPartition or "0";
    createBootPool = boolToString opt.partitioning.createBootPool;
    emptySpace = opt.partitioning.emptySpace or "0";
    debug = boolToString opt.debug;
    useSwap = boolToString opt.swapPartition.enable;
    encryptRoot = boolToString opt.encryption.encryptRoot;
    encryptBoot = boolToString opt.encryption.encryptBoot;
    swapSize = opt.swapPartition.size or "0";
    zfsAshift = toString opt.zfsOpts.ashift;
    usePersistModule = boolToString opt.persist.enable;
    oldUefi = boolToString opt.oldUefi;
    argonIterTime = opt.encryption.argonIterTime;
    passwordFile = opt.encryption.passwordFile;
    cryptBoot = opt.encryption.cryptBoot;
    cryptRoot = opt.encryption.cryptRoot;
    bootPoolReservation = opt.zfsOpts.bootPoolReservation;
    rootPoolReservation = opt.zfsOpts.rootPoolReservation;
    persistRoot = opt.persist.persistRoot;
    persistHome = opt.persist.persistHome;
  };
in ''
  set -eux

  if [ "${cfg.debug}" = "true" ]; then
  cat >&2 << FIN
    autoReboot="${cfg.autoReboot}"
    flakesPath="${cfg.flakesPath}"
    hostname="${cfg.hostname}"
    mainuser="${cfg.mainuser}"
    debug="${cfg.debug}"
    entireDisk="${cfg.entireDisk}"
    nullifyDisk="${cfg.nullifyDisk}"
    disk="${cfg.disk}"
    bootPartition="${cfg.bootPartition}"
    rootPartition="${cfg.rootPartition}"
    swapPartition="${cfg.swapPartition}"
    efiMountPoint="${cfg.efiMountPoint}"
    emptySpace="${cfg.emptySpace}"
    createBootPool="${cfg.createBootPool}"
    efiSize="${cfg.efiSize}"
    bootSize="${cfg.bootSize}"
    rootSize="${cfg.rootSize}"
    swapSize="${cfg.swapSize}"
    encryptRoot="${cfg.encryptRoot}"
    encryptBoot="${cfg.encryptBoot}"
    useSwap="${cfg.useSwap}"
    argonIterTime="${cfg.argonIterTime}"
    cryptRoot="${cfg.cryptRoot}"
    cryptBoot="${cfg.cryptBoot}"
    passwordFile="${cfg.passwordFile}"
    zfsAshift="${cfg.zfsAshift}"
    rootPoolReservation="${cfg.rootPoolReservation}"
    bootPoolReservation="${cfg.bootPoolReservation}"
    usePersistModule="${cfg.usePersistModule}"
    persistRoot="${cfg.persistRoot}"
    persistHome="${cfg.persistHome}"
    oldUefi="${cfg.oldUefi}"
  FIN
  fi

  pprint () {
    local timestamp
    timestamp=$(date +%FT%T.%3NZ)
    echo -e "$timestamp $1" 1>&2
  }

  if [ ! -d "${cfg.flakesPath}" ]; then
    pprint "flakesPath does not exists!"
    exit 2
  fi

  if [ "${cfg.encryptBoot}" = "true" || "${cfg.encryptRoot}" = "true" ]; then
    if [ ! -f "${cfg.passwordFile}" ]; then
      pprint "passwordFile does not exists!"
      exit 2
    fi
  fi

  create_new_part_table() {
    wack=0
    diskByID=""
    if echo ${cfg.disk} | grep '/dev/disk/by-id'; then
      diskByID=${cfg.disk}
    else
      byid=$(find -L /dev/disk -samefile ${cfg.disk} | grep by-id)
      if [ "$byid" = "" ]; then
        pprint "fatal: Could not find a /dev/disk/by-id symlink for %s\n" "${cfg.disk}"
        wack=1
      else
        diskByID=$byid
      fi
    fi

    if [ "${cfg.debug}" = "true" ]; then
      cat >&2 << FIN
        diskByID=$diskByID
  FIN
    fi

    # The for loop has the actual output
    if [ "$wack" -gt 0 ]; then
      exit 2
    fi

    if [ "${cfg.nullifyDisk}" = "true" ]; then
      diskname=$(basename $(readlink -f $diskByID))
      isHDD=$(cat /sys/block/$diskname/queue/rotational)
      if [ "$isHDD" = 1 ]; then
        cat /dev/zero > "$diskByID" || true
      else
        blkdiscard "$diskByID"
      fi
    fi

    # partitioning
    sgdisk --zap-all "$diskByID"

    pprint "Creating boot (EFI) partition"
    sgdisk -n1:1MiB:+${cfg.efiSize} -t1:EF00 "$diskByID"
    efiPart="$diskByID-part1"

    if [ "${cfg.createBootPool}" = "true" ]; then
      pprint "Creating boot (ZFS) partition"
      if [ "${cfg.encryptBoot}" = "true" ]; then
        sgdisk -n2:0:+${cfg.bootSize} -t2:8309 "$diskByID"
      else
        sgdisk -n2:0:+${cfg.bootSize} -t2:BF00 "$diskByID"
      fi
      bootPart="$diskByID-part2"
    fi

    if [ "${cfg.emptySpace}" != "0" ]; then
      pprint "Creating temp empty partition at the end of the disk"
      sgdisk -n5:-${cfg.emptySpace}:0 -t5:8300 "$diskByID"
    fi

    if [ "${cfg.useSwap}" = "true" ]; then
      pprint "Creating SWAP partition"
      sgdisk -n4:-${cfg.swapSize}:0 -t4:8200 "$diskByID"
      swapPart="$diskByID-part4"
    fi

    if [ "${cfg.encryptRoot}" = "true" ]; then
      pprint "Creating LUKS partition"
      sgdisk -n3:0:${cfg.rootSize} -t3:8309 "$diskByID"
    else
      pprint "Creating ROOT partition"
      sgdisk -n3:0:${cfg.rootSize} -t3:BF00 "$diskByID"
    fi
    rootPart="$diskByID-part3"

    if [ "${cfg.emptySpace}" != "0" ]; then
      pprint "Remove temp partition"
      sgdisk -d 5 -s "$diskByID"
    fi

    partprobe "$diskByID"
    sleep 1

    pprint "Format EFI partition $efiPart"
    mkfs.vfat -n EFI "$efiPart"
  }


  # Installation begin
  if [ "${cfg.entireDisk}" = "true" ]; then
    create_new_part_table
  else
    use_existing_part_table
  fi

  if [ "${cfg.encryptBoot}" = "true" || "${cfg.encryptRoot}" = "true" ]; then
    password=$(cat ${cfg.passwordFile})
    dd if=/dev/urandom of=/tmp/keyfile0.bin bs=1024 count=4

    if [ "${cfg.createBootPool}" = "true" ]; then
      if [ "${cfg.encryptBoot}" = "true" ]; then
        pprint "Creating LUKS container on $bootPart"
        echo -n "$password" | cryptsetup --type luks2 --pbkdf argon2id --iter-time ${cfg.argonIterTime} -c aes-xts-plain64 -s 512 -h sha256 luksFormat "$bootPart" -
        pprint "Add keyfile to LUKS container on $bootPart"
        echo -n "$password" | cryptsetup luksAddKey $bootPart /tmp/keyfile0.bin -

        pprint "Open LUKS container on $bootPart"
        cryptsetup luksOpen --allow-discards "$bootPart" "${cfg.cryptBoot}" -d /tmp/keyfile0.bin
        bootPool="$(ls /dev/disk/by-id/dm-uuid-*${cfg.cryptBoot})"
      else
        bootPool="$bootPart"
      fi
    fi

    if [ "${cfg.encryptRoot}" = "true" ]; then
      pprint "Creating LUKS container on $rootPart"
      echo -n "$password" | cryptsetup --type luks2 --pbkdf argon2id --iter-time ${cfg.argonIterTime} -c aes-xts-plain64 -s 512 -h sha256 luksFormat "$rootPart" -
      pprint "Add keyfile to LUKS container on $rootPart"
      echo -n "$password" | cryptsetup luksAddKey $rootPart /tmp/keyfile0.bin -

      pprint "Open LUKS container on $rootPart"
      cryptsetup luksOpen --allow-discards "$rootPart" "${cfg.cryptRoot}" -d /tmp/keyfile0.bin
      rootPool="$(ls /dev/disk/by-id/dm-uuid-*${cfg.cryptRoot})"
    else
      rootPool="$rootPart"
    fi
  else
    [ "${cfg.createBootPool}" = "true" ] && bootPool="$bootPart"
    rootPool="$rootPart"
  fi

  pprint "Create ZFS root pool on $rootPool"
  zpool create \
    -f \
    -o ashift=${cfg.zfsAshift} \
    -o autotrim=on \
    -O acltype=posixacl \
    -O atime=on \
    -O canmount=off \
    -O compression=zstd \
    -O dnodesize=auto \
    -O normalization=formD \
    -O relatime=on \
    -O xattr=sa \
    -O dedup=off \
    -O mountpoint=/ \
    -R /mnt \
    rpool "$rootPool"

  pprint "Create ZFS root datasets"

  if [ "${cfg.rootPoolReservation}" != "0" ]; then
    zfs create -o refreservation=${cfg.rootPoolReservation} -o canmount=off -o mountpoint=none rpool/reserved
  fi
  # top level datasets
  zfs create -o canmount=off -o mountpoint=none rpool/nixos
  zfs create -o canmount=off -o mountpoint=none rpool/user
  zfs create -o canmount=off -o mountpoint=none rpool/persistent
  # empty root
  zfs create -o canmount=noauto -o mountpoint=/ rpool/nixos/root
  zfs mount rpool/nixos/root
  zfs create -o canmount=on -o mountpoint=/home rpool/user/home
  # persistent across boots
  if [ "${cfg.usePersistModule}" = "true" ]; then
    zfs create -o canmount=on -o mountpoint=${cfg.persistRoot} rpool/persistent/impermanence
    mkdir -p /mnt${cfg.persistRoot}${cfg.persistHome}
    chown 1000:100 /mnt${cfg.persistRoot}${cfg.persistHome}
    chmod 755 /mnt${cfg.persistRoot}${cfg.persistHome}
  fi
  zfs create -o canmount=on -o mountpoint=/srv rpool/persistent/servers
  zfs create -o canmount=on -o mountpoint=/etc/secrets rpool/persistent/secrets
  zfs create -o canmount=on -o mountpoint=/nix rpool/persistent/nix
  zfs create -o canmount=on -o mountpoint=/var/log rpool/persistent/log
  zfs create -o canmount=noauto -o atime=off rpool/persistent/lxd
  zfs create -o canmount=on -o mountpoint=/var/lib/docker -o atime=off rpool/persistent/docker
  zfs create -o canmount=on -o mountpoint=/var/lib/containers -o atime=off rpool/persistent/containers
  zfs create -o canmount=on -o mountpoint=/var/lib/nixos-containers -o atime=off rpool/persistent/nixos-containers
  zfs create -o canmount=on -o mountpoint=/media/bittorrent -o atime=off -o recordsize=16K -o compression=lz4 rpool/persistent/bittorrent
  chown 1000:100 /mnt/media/bittorrent
  chmod 775 /mnt/media/bittorrent
  zfs create -o canmount=on -o mountpoint=/var/lib/libvirt rpool/persistent/libvirt
  zfs create -o canmount=on -o mountpoint=/media/libvirt rpool/persistent/libvirt-user
  zfs create -o canmount=on -o mountpoint=/media/libvirt/images -o atime=off -o recordsize=16K -o compression=lz4 rpool/persistent/libvirt-user/images
  chown 1000:67 /mnt/media/libvirt
  chmod 775 /mnt/media/libvirt

  # Create empty zfs snapshots
  zfs snapshot rpool/nixos@empty
  zfs snapshot rpool/nixos/root@empty
  zfs snapshot rpool/user@empty
  zfs snapshot rpool/user/home@empty
  zfs snapshot rpool/persistent@empty
  zfs snapshot rpool/persistent/impermanence@empty
  zfs snapshot rpool/persistent/servers@empty
  zfs snapshot rpool/persistent/secrets@empty
  zfs snapshot rpool/persistent/nix@empty
  zfs snapshot rpool/persistent/log@empty
  zfs snapshot rpool/persistent/lxd@empty
  zfs snapshot rpool/persistent/docker@empty
  zfs snapshot rpool/persistent/containers@empty
  zfs snapshot rpool/persistent/nixos-containers@empty
  zfs snapshot rpool/persistent/bittorrent@empty
  zfs snapshot rpool/persistent/libvirt@empty
  zfs snapshot rpool/persistent/libvirt-user@empty
  zfs snapshot rpool/persistent/libvirt-user/images@empty


  if [ "${cfg.createBootPool}" = "true" ]; then
    pprint "Create ZFS boot pool on $bootPool"
    zpool create \
      -f \
      -o compatibility=grub2 \
      -o ashift=${cfg.zfsAshift} \
      -o autotrim=on \
      -O acltype=posixacl \
      -O atime=on \
      -O canmount=off \
      -O compression=lz4 \
      -O devices=off \
      -O normalization=formD \
      -O relatime=on \
      -O xattr=sa \
      -O dedup=off \
      -O mountpoint=/boot \
      -R /mnt \
      bpool "$bootPool"

    pprint "Create ZFS boot datasets"

    if [ "${cfg.bootPoolReservation}" != "0" ]; then
      zfs create -o refreservation=${cfg.bootPoolReservation} -o canmount=off -o mountpoint=none bpool/reserved
    fi
    zfs create -o canmount=off -o mountpoint=none bpool/nixos
    zfs create -o canmount=on -o mountpoint=/boot bpool/nixos/boot

    zfs snapshot bpool/nixos@empty
    zfs snapshot bpool/nixos/boot@empty
  fi

  # Disable cache, stale cache will prevent system from booting
  if [ "${cfg.usePersistModule}" = "true" ]; then
      mkdir -p /mnt"${cfg.persistRoot}"/etc/zfs/
      rm -f /mnt"${cfg.persistRoot}"/etc/zfs/zpool.cache
      touch /mnt"${cfg.persistRoot}"/etc/zfs/zpool.cache
      chmod a-w /mnt"${cfg.persistRoot}"/etc/zfs/zpool.cache
      chattr +i /mnt"${cfg.persistRoot}"/etc/zfs/zpool.cache
  else
      mkdir -p /mnt/etc/zfs/
      rm -f /mnt/etc/zfs/zpool.cache
      touch /mnt/etc/zfs/zpool.cache
      chmod a-w /mnt/etc/zfs/zpool.cache
      chattr +i /mnt/etc/zfs/zpool.cache
  fi

  mkdir -p /mnt${cfg.efiMountPoint}
  mount -t vfat "$efiPart" /mnt${cfg.efiMountPoint}

  if [ "${cfg.useSwap}" = "true" ]; then
      mkswap -L swap -f "$swapPart"
  fi

  pprint "Generate NixOS configuration"
  configExists=false
  [ -f ${cfg.flakesPath}/machines/${cfg.hostname}/configuration.nix ] && configExists=true
  nixos-generate-config --root /mnt --dir ${cfg.flakesPath}/machines/${cfg.hostname}
  [ "$configExists" = "false" ] && rm -f ${cfg.flakesPath}/machines/${cfg.hostname}/configuration.nix

  pprint "Append ZFS configuration to hardware-configuration.nix"

  hostID=$(head -c8 /etc/machine-id)

  hardwareConfig=$(mktemp)
  cat <<CONFIG > "$hardwareConfig"
    networking.hostId = "$hostID";
    boot.zfs.devNodes = "/dev/disk/by-id";
    boot.supportedFilesystems = [ "zfs" ];
  CONFIG
  if [ "${cfg.encryptBoot}" = "true" ]; then
    bootPartUuid=$(blkid --match-tag PARTUUID --output value "$bootPart")
    cat <<CONFIG >> "$hardwareConfig"
    boot.initrd.luks.devices."${cfg.cryptBoot}".device = "/dev/disk/by-partuuid/$bootPartUuid";
  CONFIG
  fi
  if [ "${cfg.encryptRoot}" = "true" ]; then
    rootPartUuid=$(blkid --match-tag PARTUUID --output value "$rootPart")
    cat <<CONFIG >> "$hardwareConfig"
    boot.initrd.luks.devices."${cfg.cryptRoot}".device = "/dev/disk/by-partuuid/$rootPartUuid";
  CONFIG
  fi

  sed -i "\$e cat $hardwareConfig" ${cfg.flakesPath}/machines/${cfg.hostname}/hardware-configuration.nix
  sed -i 's|fsType = "zfs";|fsType = "zfs"; options = [ "zfsutil" "X-mount.mkdir" ];|g' ${cfg.flakesPath}/machines/${cfg.hostname}/hardware-configuration.nix
  if [ "${cfg.useSwap}" == "true" ]; then
    swapPartUuid=$(blkid --match-tag PARTUUID --output value "$swapPart")
    sed -i "s|swapDevices = \[ \];|swapDevices = \[\n    {\n      device = \"/dev/disk/by-partuuid/$swapPartUuid\";\n      randomEncryption.enable = true;\n      randomEncryption.allowDiscards = true;\n    }\n  \];|" ${cfg.flakesPath}/machines/${cfg.hostname}/hardware-configuration.nix
  fi
  chown 1000:100 ${cfg.flakesPath}/machines/${cfg.hostname}/hardware-configuration.nix
  git config --global --add safe.directory "${cfg.flakesPath}"
  git -C "${cfg.flakesPath}" add -A

  pprint "Gen ssh host key for initrd"
  ssh-keygen -t ed25519 -N "" -f /mnt/etc/secrets/ssh_host_key
  chown root:root /mnt/etc/secrets/ssh_host_key
  chmod 600 /mnt/etc/secrets/ssh_host_key

  if [ "${cfg.encryptBoot}" = "true" || "${cfg.encryptRoot}" = "true" ]; then
    cp /tmp/keyfile0.bin /mnt/etc/secrets/keyfile0.bin
    chmod 000 /mnt/etc/secrets/keyfile*.bin
  fi

  if [ "${cfg.debug}" != "true" ]; then
    nixos-install --flake "${cfg.flakesPath}/#${cfg.hostname}" --root /mnt --no-root-passwd
  fi

  configPath="/mnt/persist/home/"${cfg.mainuser}"/nixos-config"
  if [ ! -d "$configPath" ]; then
    mkdir -p $configPath
    chown 1000:100 $configPath
  fi
  cp -aT ${cfg.flakesPath} $configPath

  if [ "${cfg.oldUefi}" = "true" ]; then
    mkdir -p /mnt/boot/efi/EFI/Microsoft/Boot
    cp /mnt/boot/efi/EFI/BOOT/BOOTX64.EFI /mnt/boot/efi/EFI/Microsoft/Boot/bootmgr.efi
    cp /mnt/boot/efi/EFI/BOOT/BOOTX64.EFI /mnt/boot/efi/EFI/Microsoft/Boot/bootmgfw.efi
  fi

  if [ "${cfg.debug}" != "true" ]; then
    umount -Rl /mnt
    zpool export -a
    [ "${cfg.encryptBoot}" = "true" ] && cryptsetup luksClose ${cfg.cryptBoot}
    [ "${cfg.encryptRoot}" = "true" ] && cryptsetup luksClose ${cfg.cryptRoot}

    if [ "${cfg.autoReboot}" = "true" ]; then
      if ! systemctl reboot --firmware-setup ; then
        pprint "Reboot into efi firmware setup failed! Shutdown in 30 seconds"
        sleep 30
        systemctl poweroff
      fi
    fi
  fi
''
