{
  config,
  pkgs,
  lib,
  ...
}:
let
  cfg = config.services.prometheus.pushgateway;

  cmdlineArgs =
    opt "web.listen-address" cfg.web.listen-address
    ++ opt "web.telemetry-path" cfg.web.telemetry-path
    ++ opt "web.external-url" cfg.web.external-url
    ++ opt "web.route-prefix" cfg.web.route-prefix
    ++ lib.optional cfg.persistMetrics ''--persistence.file="/var/lib/${cfg.stateDir}/metrics"''
    ++ opt "persistence.interval" cfg.persistence.interval
    ++ opt "log.level" cfg.log.level
    ++ opt "log.format" cfg.log.format
    ++ cfg.extraFlags;

  opt = k: v: lib.optional (v != null) ''--${k}="${v}"'';

in
{
  options = {
    services.prometheus.pushgateway = {
      enable = lib.mkEnableOption "Prometheus Pushgateway";

      package = lib.mkPackageOption pkgs "prometheus-pushgateway" { };

      web.listen-address = lib.mkOption {
        type = lib.types.nullOr lib.types.str;
        default = null;
        description = ''
          Address to listen on for the web interface, API and telemetry.

          `null` will default to `:9091`.
        '';
      };

      web.telemetry-path = lib.mkOption {
        type = lib.types.nullOr lib.types.str;
        default = null;
        description = ''
          Path under which to expose metrics.

          `null` will default to `/metrics`.
        '';
      };

      web.external-url = lib.mkOption {
        type = lib.types.nullOr lib.types.str;
        default = null;
        description = ''
          The URL under which Pushgateway is externally reachable.
        '';
      };

      web.route-prefix = lib.mkOption {
        type = lib.types.nullOr lib.types.str;
        default = null;
        description = ''
          Prefix for the internal routes of web endpoints.

          Defaults to the path of
          {option}`services.prometheus.pushgateway.web.external-url`.
        '';
      };

      persistence.interval = lib.mkOption {
        type = lib.types.nullOr lib.types.str;
        default = null;
        example = "10m";
        description = ''
          The minimum interval at which to write out the persistence file.

          `null` will default to `5m`.
        '';
      };

      log.level = lib.mkOption {
        type = lib.types.nullOr (
          lib.types.enum [
            "debug"
            "info"
            "warn"
            "error"
            "fatal"
          ]
        );
        default = null;
        description = ''
          Only log messages with the given severity or above.

          `null` will default to `info`.
        '';
      };

      log.format = lib.mkOption {
        type = lib.types.nullOr lib.types.str;
        default = null;
        example = "logger:syslog?appname=bob&local=7";
        description = ''
          Set the log target and format.

          `null` will default to `logger:stderr`.
        '';
      };

      extraFlags = lib.mkOption {
        type = lib.types.listOf lib.types.str;
        default = [ ];
        description = ''
          Extra commandline options when launching the Pushgateway.
        '';
      };

      persistMetrics = lib.mkOption {
        type = lib.types.bool;
        default = false;
        description = ''
          Whether to persist metrics to a file.

          When enabled metrics will be saved to a file called
          `metrics` in the directory
          `/var/lib/pushgateway`. The directory below
          `/var/lib` can be set using
          {option}`services.prometheus.pushgateway.stateDir`.
        '';
      };

      stateDir = lib.mkOption {
        type = lib.types.str;
        default = "pushgateway";
        description = ''
          Directory below `/var/lib` to store metrics.

          This directory will be created automatically using systemd's
          StateDirectory mechanism when
          {option}`services.prometheus.pushgateway.persistMetrics`
          is enabled.
        '';
      };
    };
  };

  config = lib.mkIf cfg.enable {
    assertions = [
      {
        assertion = !lib.hasPrefix "/" cfg.stateDir;
        message =
          "The option services.prometheus.pushgateway.stateDir"
          + " shouldn't be an absolute directory."
          + " It should be a directory relative to /var/lib.";
      }
    ];
    systemd.services.pushgateway = {
      wantedBy = [ "multi-user.target" ];
      after = [ "network.target" ];
      serviceConfig = {
        ExecStart =
          "${cfg.package}/bin/pushgateway"
          + lib.optionalString (lib.length cmdlineArgs != 0) (
            " \\\n  " + lib.concatStringsSep " \\\n  " cmdlineArgs
          );

        CapabilityBoundingSet = [ "" ];
        DeviceAllow = [ "" ];
        DynamicUser = true;
        NoNewPrivileges = true;

        MemoryDenyWriteExecute = true;

        LockPersonality = true;

        ProtectProc = "invisible";
        ProtectSystem = "strict";
        ProtectHome = "tmpfs";

        PrivateTmp = true;
        PrivateDevices = true;
        PrivateIPC = true;

        ProcSubset = "pid";

        ProtectHostname = true;
        ProtectClock = true;
        ProtectKernelTunables = true;
        ProtectKernelModules = true;
        ProtectKernelLogs = true;
        ProtectControlGroups = true;

        Restart = "always";

        RestrictAddressFamilies = [
          "AF_INET"
          "AF_INET6"
        ];
        RestrictNamespaces = true;
        RestrictRealtime = true;
        RestrictSUIDSGID = true;

        StateDirectory = if cfg.persistMetrics then cfg.stateDir else null;
        SystemCallFilter = [
          "@system-service"
          "~@cpu-emulation"
          "~@privileged"
          "~@reboot"
          "~@setuid"
          "~@swap"
        ];
      };
    };
  };
}
