{ config, lib, pkgs, ... }: with lib;
let
  cfg = config.boot.iscsi-initiator;
in
{
  # If you're booting entirely off another machine you may want to add
  # this snippet to always boot the latest "system" version. It is not
  # enabled by default in case you have an initrd on a local disk:
  #
  #     boot.initrd.postMountCommands = ''
  #       ln -sfn /nix/var/nix/profiles/system/init /mnt-root/init
  #       stage2Init=/init
  #     '';
  #
  # Note: Theoretically you might want to connect to multiple portals and
  # log in to multiple targets, however the authors of this module so far
  # don't have the need or expertise to reasonably implement it. Also,
  # consider carefully before making your boot chain depend on multiple
  # machines to be up.
  options.boot.iscsi-initiator = with types; {
    name = mkOption {
      description = ''
        Name of the iSCSI initiator to boot from. Note, booting from iscsi
        requires networkd based networking.
      '';
      default = null;
      example = "iqn.2020-08.org.linux-iscsi.initiatorhost:example";
      type = nullOr str;
    };

    discoverPortal = mkOption {
      description = ''
        iSCSI portal to boot from.
      '';
      default = null;
      example = "192.168.1.1:3260";
      type = nullOr str;
    };

    target = mkOption {
      description = ''
        Name of the iSCSI target to boot from.
      '';
      default = null;
      example = "iqn.2020-08.org.linux-iscsi.targethost:example";
      type = nullOr str;
    };

    logLevel = mkOption {
      description = ''
        Higher numbers elicits more logs.
      '';
      default = 1;
      example = 8;
      type = int;
    };

    loginAll = mkOption {
      description = ''
        Do not log into a specific target on the portal, but to all that we discover.
        This overrides setting target.
      '';
      type = bool;
      default = false;
    };

    extraIscsiCommands = mkOption {
      description = "Extra iscsi commands to run in the initrd.";
      default = "";
      type = lines;
    };

    extraConfig = mkOption {
      description = "Extra lines to append to /etc/iscsid.conf";
      default = null;
      type = nullOr lines;
    };

    extraConfigFile = mkOption {
      description = ''
        Append an additional file's contents to `/etc/iscsid.conf`. Use a non-store path
        and store passwords in this file. Note: the file specified here must be available
        in the initrd, see: `boot.initrd.secrets`.
      '';
      default = null;
      type = nullOr str;
    };
  };

  config = mkIf (cfg.name != null) {
    # The "scripted" networking configuration (ie: non-networkd)
    # doesn't properly order the start and stop of the interfaces, and the
    # network interfaces are torn down before unmounting disks. Since this
    # module is specifically for very-early-boot network mounts, we need
    # the network to stay on.
    #
    # We could probably fix the scripted options to properly order, but I'm
    # not inclined to invest that time today. Hopefully this gets users far
    # enough along and they can just use networkd.
    networking.useNetworkd = true;
    networking.useDHCP = false; # Required to set useNetworkd = true

    boot.initrd = {
      network.enable = true;

      # By default, the stage-1 disables the network and resets the interfaces
      # on startup. Since our startup disks are on the network, we can't let
      # the network not work.
      network.flushBeforeStage2 = false;

      kernelModules = [ "iscsi_tcp" ];

      extraUtilsCommands = ''
        copy_bin_and_libs ${pkgs.openiscsi}/bin/iscsid
        copy_bin_and_libs ${pkgs.openiscsi}/bin/iscsiadm
        ${optionalString (!config.boot.initrd.network.ssh.enable) "cp -pv ${pkgs.glibc.out}/lib/libnss_files.so.* $out/lib"}

        mkdir -p $out/etc/iscsi
        cp ${config.environment.etc.hosts.source} $out/etc/hosts
        cp ${pkgs.openiscsi}/etc/iscsi/iscsid.conf $out/etc/iscsi/iscsid.fragment.conf
        chmod +w $out/etc/iscsi/iscsid.fragment.conf
        cat << 'EOF' >> $out/etc/iscsi/iscsid.fragment.conf
        ${optionalString (cfg.extraConfig != null) cfg.extraConfig}
        EOF
      '';

      extraUtilsCommandsTest = ''
        $out/bin/iscsiadm --version
      '';

      preLVMCommands = let
        extraCfgDumper = optionalString (cfg.extraConfigFile != null) ''
          if [ -f "${cfg.extraConfigFile}" ]; then
            printf "\n# The following is from ${cfg.extraConfigFile}:\n"
            cat "${cfg.extraConfigFile}"
          else
            echo "Warning: boot.iscsi-initiator.extraConfigFile ${cfg.extraConfigFile} does not exist!" >&2
          fi
        '';
      in ''
        ${optionalString (!config.boot.initrd.network.ssh.enable) ''
        # stolen from initrd-ssh.nix
        echo 'root:x:0:0:root:/root:/bin/ash' > /etc/passwd
        echo 'passwd: files' > /etc/nsswitch.conf
      ''}

        cp -f $extraUtils/etc/hosts /etc/hosts

        mkdir -p /etc/iscsi /run/lock/iscsi
        echo "InitiatorName=${cfg.name}" > /etc/iscsi/initiatorname.iscsi

        (
          cat "$extraUtils/etc/iscsi/iscsid.fragment.conf"
          printf "\n"
          ${optionalString cfg.loginAll ''echo "node.startup = automatic"''}
          ${extraCfgDumper}
        ) > /etc/iscsi/iscsid.conf

        iscsid --foreground --no-pid-file --debug ${toString cfg.logLevel} &
        iscsiadm --mode discoverydb \
          --type sendtargets \
          --discover \
          --portal ${escapeShellArg cfg.discoverPortal} \
          --debug ${toString cfg.logLevel}

        ${if cfg.loginAll then ''
        iscsiadm --mode node --loginall all
      '' else ''
        iscsiadm --mode node --targetname ${escapeShellArg cfg.target} --login
      ''}

        ${cfg.extraIscsiCommands}

        pkill -9 iscsid
      '';
    };

    services.openiscsi = {
      enable = true;
      inherit (cfg) name;
    };

    assertions = [
      {
        assertion = cfg.loginAll -> cfg.target == null;
        message = "iSCSI target name is set while login on all portals is enabled.";
      }
      {
        assertion = !config.boot.initrd.systemd.enable;
        message = "systemd stage 1 does not support iscsi yet.";
      }
    ];
  };
}
