{
  config,
  pkgs,
  lib,
  ...
}:
let
  cfg = config.services.hydra-queue-builder-dev;
  user = "hydra-queue-builder";

  format = pkgs.formats.toml { };
in
{
  options = {
    services.hydra-queue-builder-dev = {
      enable = lib.mkEnableOption "QueueBuilder";

      queueRunnerAddr = lib.mkOption {
        description = "Queue Runner address to the grpc server";
        type = lib.types.singleLineStr;
      };

      settings = lib.mkOption {
        description = "Builder settings written to the TOML config file";
        type = lib.types.submodule {
          options = {
            pingInterval = lib.mkOption {
              description = "Interval in which pings are send to the runner";
              type = lib.types.ints.positive;
              default = 10;
            };

            speedFactor = lib.mkOption {
              description = "Additional Speed factor for this machine";
              type = lib.types.oneOf [
                lib.types.ints.positive
                lib.types.float
              ];
              default = 1;
            };

            maxJobs = lib.mkOption {
              description = "Maximum allowed of jobs. This only is used if the queue runner uses this metrics for determining free machines.";
              type = lib.types.ints.positive;
              default = 4;
            };

            buildCores = lib.mkOption {
              description = "Cores made available to each build (NIX_BUILD_CORES). 0 means use all available cores.";
              type = lib.types.ints.unsigned;
              default = config.nix.settings.cores;
              defaultText = lib.literalExpression "config.nix.settings.cores";
            };

            buildDirAvailThreshold = lib.mkOption {
              description = "Threshold in percent for nix build dir before jobs are no longer scheduled on the machine";
              type = lib.types.float;
              default = 10.0;
            };

            storeAvailThreshold = lib.mkOption {
              description = "Threshold in percent for /nix/store before jobs are no longer scheduled on the machine";
              type = lib.types.float;
              default = 10.0;
            };

            load1Threshold = lib.mkOption {
              description = "Maximum Load1 threshold before we stop scheduling jobs on that node. Only used if PSI is not available.";
              type = lib.types.float;
              default = 8.0;
            };

            cpuPsiThreshold = lib.mkOption {
              description = "Maximum CPU PSI in the last 10s before we stop scheduling jobs on that node";
              type = lib.types.float;
              default = 75.0;
            };

            memPsiThreshold = lib.mkOption {
              description = "Maximum Memory PSI in the last 10s before we stop scheduling jobs on that node";
              type = lib.types.float;
              default = 80.0;
            };

            ioPsiThreshold = lib.mkOption {
              description = "Maximum IO PSI in the last 10s before we stop scheduling jobs on that node. If null then this pressure check is disabled.";
              type = lib.types.nullOr lib.types.float;
              default = null;
            };

            systems = lib.mkOption {
              description = "List of supported systems. If null, system and extra-platforms are read from nix; an empty list means none.";
              type = lib.types.nullOr (lib.types.listOf lib.types.singleLineStr);
              default = null;
            };

            supportedFeatures = lib.mkOption {
              description = "Supported features for the builder. If null, system features are read from nix; an empty list means none.";
              type = lib.types.nullOr (lib.types.listOf lib.types.singleLineStr);
              default = null;
            };

            mandatoryFeatures = lib.mkOption {
              description = "Mandatory features for the builder.";
              type = lib.types.listOf lib.types.singleLineStr;
              default = [ ];
            };

            useSubstitutes = lib.mkOption {
              description = "Use substitution for paths";
              type = lib.types.bool;
              default = true;
            };
          };
        };
        default = { };
      };

      authorizationFile = lib.mkOption {
        description = "Path to token authorization file if token auth should be used.";
        type = lib.types.nullOr lib.types.path;
        default = null;
      };

      mtls = lib.mkOption {
        description = "mtls options";
        default = null;
        type = lib.types.nullOr (
          lib.types.submodule {
            options = {
              serverRootCaCertPath = lib.mkOption {
                description = "Server root ca certificate path";
                type = lib.types.path;
              };
              clientCertPath = lib.mkOption {
                description = "Client certificate path";
                type = lib.types.path;
              };
              clientKeyPath = lib.mkOption {
                description = "Client key path";
                type = lib.types.path;
              };
              domainName = lib.mkOption {
                description = "Domain name for mtls";
                type = lib.types.singleLineStr;
              };
            };
          }
        );
      };

      package = lib.mkOption {
        type = lib.types.package;
        default = pkgs.callPackage ./. { };
      };
    };
  };

  config = lib.mkIf cfg.enable {
    systemd.services.hydra-queue-builder-dev = {
      description = "Hydra Queue Builder main service";

      requires = [ "nix-daemon.socket" ];
      after = [ "network.target" ];
      wantedBy = [ "multi-user.target" ];
      # The builder has no hot-reload; restart it when the config changes.
      restartTriggers = [ config.environment.etc."hydra/builder.toml".source ];

      environment = {
        NIX_REMOTE = "daemon";
        LIBEV_FLAGS = "4"; # go ahead and mandate epoll(2)
        RUST_BACKTRACE = "1";

        # Note: it's important to set this for nix-store, because it wants to use
        # $HOME in order to use a temporary cache dir. bizarre failures will occur
        # otherwise
        HOME = "/run/hydra-queue-builder";
      };

      path = [ config.nix.package ];

      serviceConfig = {
        Type = "notify";
        Restart = "always";
        RestartSec = "5s";

        ExecStart = lib.escapeShellArgs (
          [
            "${cfg.package}/bin/hydra-builder"
            "--gateway-endpoint"
            cfg.queueRunnerAddr
            "--config-path"
            "/etc/hydra/builder.toml"
          ]
          ++ lib.optionals (cfg.authorizationFile != null) [
            "--authorization-file"
            cfg.authorizationFile
          ]
          ++ lib.optionals (cfg.mtls != null) [
            "--server-root-ca-cert-path"
            cfg.mtls.serverRootCaCertPath
            "--client-cert-path"
            cfg.mtls.clientCertPath
            "--client-key-path"
            cfg.mtls.clientKeyPath
            "--domain-name"
            cfg.mtls.domainName
          ]
        );

        User = user;
        Group = "hydra";

        PrivateNetwork = false;
        SystemCallFilter = [
          "@system-service"
          "~@privileged"
          "~@resources"
        ];

        ReadWritePaths = [
          "/nix/var/nix/gcroots/"
          "/nix/var/nix/daemon-socket/socket"
        ];
        ReadOnlyPaths = [ "/nix/" ];
        RuntimeDirectory = "hydra-queue-builder";

        ProtectSystem = "strict";
        ProtectHome = true;
        PrivateTmp = true;
        PrivateDevices = true;
        ProtectKernelTunables = true;
        ProtectControlGroups = true;
        RestrictSUIDSGID = true;
        PrivateMounts = true;
        RemoveIPC = true;
        UMask = "0077";

        CapabilityBoundingSet = "";
        NoNewPrivileges = true;

        ProtectKernelModules = true;
        SystemCallArchitectures = "native";
        ProtectKernelLogs = true;
        ProtectClock = true;

        RestrictAddressFamilies = "";

        LockPersonality = true;
        ProtectHostname = true;
        RestrictRealtime = true;
        MemoryDenyWriteExecute = true;
        PrivateUsers = true;
        RestrictNamespaces = true;
      };
    };

    environment.etc."hydra/builder.toml".source = format.generate "builder.toml" (
      lib.filterAttrsRecursive (_: v: v != null) cfg.settings
    );

    systemd.tmpfiles.rules = [
      "d /nix/var/nix/gcroots/per-user/${user} 0755 ${user} hydra -"
    ];

    nix = {
      settings = {
        trusted-users = [ user ];
        experimental-features = [ "nix-command" ];
      };
    };

    users = {
      groups.hydra = { };
      users.${user} = {
        group = "hydra";
        isSystemUser = true;
      };
    };

  };
}
