{ config, lib, pkgs, options, ... }:
let
  inherit (lib) types escapeShellArg;
  cfg = config.nixfiles.common.bootnext;
  bootNextScriptMain = pkgs.writeShellScript "bootnext-wrapped" ''
    set -Eeuxo pipefail

    PATH=${lib.escapeShellArg (with pkgs; lib.makeBinPath [ gnugrep coreutils efibootmgr ])}
    export PATH

    function do_bootnext() {
      uuid="$1"
      shift
      entryName="$1"
      shift

      efibootmgr -n "$(efibootmgr | grep -Fi "$uuid" | grep -F "$entryName" | cut -d' ' -f1 | tr -dc '[:digit:]')"
    }

    case "$1" in
    ${lib.concatStringsSep "\n" (
      lib.mapAttrsToList (name: value:
        "  ${escapeShellArg name}) do_bootnext ${escapeShellArg value.efiPartUUID} ${escapeShellArg value.name} ;;"
      ) cfg.entries
    )}
      *) echo "Boot entry \"$1\" not configured."; exit 1;;
    esac
  '';

  bootNextScript = pkgs.writeShellScriptBin "bootnext" ''
    # this wrapper is needed because the sudoers config needs the path to the
    # actual script and self referencing is a pain. this way we can guarantee
    # that the script passed is exactly the same as the one in the sudoers
    # config. i could use realpath but this is probably safer since it is not
    # evaluated at runtime. who knows.
    if [[ "$(id -u)" -ne 0 ]]; then
      exec sudo ${escapeShellArg bootNextScriptMain} "$@"
    else
      exec ${escapeShellArg bootNextScriptMain} "$@"
    fi
  '';

  desktopWrapper = pkgs.writeShellScript "bootnext-desktop-wrapper" ''
    if ${pkgs.libsForQt5.kdialog}/bin/kdialog --warningyesno "Are you sure you want to reboot?" ; then
      ${bootNextScript}/bin/bootnext "$@"
      reboot
    fi
  '';

  bootnextDesktopEntries = pkgs.symlinkJoin {
    name = "bootnext-desktop-entries";
      paths = lib.mapAttrsToList (name: value: pkgs.makeDesktopItem {
        name = "bootnext-reboot-${name}";
        desktopName = "Reboot into ${value.desktopEntry.name}";
        comment = "Select the entry defined by the `${name}` configuration in the bootnext script and then reboot.";
        icon = "${value.desktopEntry.icon}";
        keywords = [ "bootnext" "reboot" "${name}" "${value.desktopEntry.name}" ];
        exec = "${desktopWrapper} ${name}";
      }) (lib.filterAttrs (_: value: value.desktopEntry.enable) cfg.entries);
  };

in
{
  options = {
    nixfiles.common.bootnext = {
      enable = lib.mkOption {
        description = ''
          Whether to enable the bootnext wrapper command for controlling boot order
        '';
        type = types.bool;
        default = false;
        example = true;
      };
      enableDesktopEntries = lib.mkEnableOption "generation of bootnext Desktop entries" // { default = true; };
      entries = let
        entryModule = {name, config, ... }: {
          options = let
            uuidType = with types; lib.mkOptionType {
              name = "uuid";
              description = "UUID";
              descriptionClass = "noun";
              check = let
                uuidRegex = "^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$";
              in x: str.check x && (builtins.match uuidRegex x) != null;
              inherit (str) merge;
            };
          in {
            efiPartUUID = lib.mkOption {
              description = "UUID of EFI partition containing boot entry";
              type = uuidType;
              apply = lib.strings.toLower;
            };
            name = lib.mkOption {
              description = "Name of boot entry as it appears in efibootmgr";
              type = types.str;
              example = "Windows Boot Manager";
            };
            desktopEntry = {
              enable = lib.mkOption {
                description = "Whether to generate this desktop entry.";
                type = types.bool;
                default = true;
                example = false;
              };
              name = lib.mkOption {
                description = "Display name of boot entry for desktop entry.";
                type = types.str;
                default = config.name;
                example = "Windows";
              };
              icon = lib.mkOption {
                description = "Path or name of icon to use for desktop entry";
                type = with types; nullOr str;
                default = null;
              };
            };
          };
        };
      in lib.mkOption {
        description = "bootnext entry";
        type = with types; attrsOf (submodule entryModule);
      };
    };
  };

  config = lib.mkIf cfg.enable {
    environment.systemPackages = [ bootNextScript ] ++ lib.optional cfg.enableDesktopEntries bootnextDesktopEntries;

    security.sudo.extraRules = lib.mkAfter [
      {
        commands = [
          { command = "${bootNextScriptMain}"; options = [ "NOPASSWD" ]; }
        ];
        groups = [ "wheel" ];
      }
    ];
  };
}