{
  config,
  lib,
  pkgs,
  options,
  ...
}:

let
  cfg = config.services.prometheus.exporters.pgbouncer;
  inherit (lib)
    mkOption
    mkPackageOption
    types
    optionals
    getExe
    escapeShellArg
    concatStringsSep
    ;
in
{
  port = 9127;
  extraOpts = {
    package = mkPackageOption pkgs "prometheus-pgbouncer-exporter" { };

    telemetryPath = mkOption {
      type = types.str;
      default = "/metrics";
      description = ''
        Path under which to expose metrics.
      '';
    };

    connectionString = mkOption {
      type = types.nullOr types.str;
      default = null;
      example = "postgres://admin:@localhost:6432/pgbouncer?sslmode=require";
      description = ''
        Connection string for accessing pgBouncer.

        NOTE: You MUST keep pgbouncer as database name (special internal db)!!!

        NOTE: ignore_startup_parameters MUST contain "extra_float_digits".

        NOTE: Admin user (with password or passwordless) MUST exist in the
        auth_file if auth_type other than "any" is used.

        WARNING: this secret is stored in the world-readable Nix store!
        Use [](#opt-services.prometheus.exporters.pgbouncer.connectionEnvFile) if the
        URL contains a secret.
      '';
    };

    connectionEnvFile = mkOption {
      type = types.nullOr types.str;
      default = null;
      description = ''
        File that must contain the environment variable
        `PGBOUNCER_EXPORTER_CONNECTION_STRING` which is set to the connection
        string used by pgbouncer. I.e. the format is supposed to look like this:

        ```
        PGBOUNCER_EXPORTER_CONNECTION_STRING="postgres://admin@localhost:6432/pgbouncer?sslmode=require"
        ```

        NOTE: You MUST keep pgbouncer as database name (special internal db)!
        NOTE: `services.pgbouncer.settings.pgbouncer.ignore_startup_parameters`
        MUST contain "extra_float_digits".

        Mutually exclusive with [](#opt-services.prometheus.exporters.pgbouncer.connectionString).
      '';
    };

    pidFile = mkOption {
      type = types.nullOr types.str;
      default = null;
      description = ''
        Path to PgBouncer pid file.

        If provided, the standard process metrics get exported for the PgBouncer
        process, prefixed with 'pgbouncer_process_...'. The pgbouncer_process exporter
        needs to have read access to files owned by the PgBouncer process. Depends on
        the availability of /proc.

        <https://prometheus.io/docs/instrumenting/writing_clientlibs/#process-metrics>.

      '';
    };

    webSystemdSocket = mkOption {
      type = types.bool;
      default = false;
      description = ''
        Use systemd socket activation listeners instead of port listeners (Linux only).
      '';
    };

    logLevel = mkOption {
      type = types.enum [
        "debug"
        "info"
        "warn"
        "error"
      ];
      default = "info";
      description = ''
        Only log messages with the given severity or above.
      '';
    };

    logFormat = mkOption {
      type = types.enum [
        "logfmt"
        "json"
      ];
      default = "logfmt";
      description = ''
        Output format of log messages. One of: [logfmt, json]
      '';
    };

    webConfigFile = mkOption {
      type = types.nullOr types.path;
      default = null;
      description = ''
        Path to configuration file that can enable TLS or authentication.
      '';
    };

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

  };

  serviceOpts = {
    after = [ "pgbouncer.service" ];
    script = concatStringsSep " " (
      [
        "exec -- ${escapeShellArg (getExe cfg.package)}"
        "--web.listen-address ${cfg.listenAddress}:${toString cfg.port}"
      ]
      ++ optionals (cfg.connectionString != null) [
        "--pgBouncer.connectionString ${escapeShellArg cfg.connectionString}"
      ]
      ++ optionals (cfg.telemetryPath != null) [
        "--web.telemetry-path ${escapeShellArg cfg.telemetryPath}"
      ]
      ++ optionals (cfg.pidFile != null) [
        "--pgBouncer.pid-file ${escapeShellArg cfg.pidFile}"
      ]
      ++ optionals (cfg.logLevel != null) [
        "--log.level ${escapeShellArg cfg.logLevel}"
      ]
      ++ optionals (cfg.logFormat != null) [
        "--log.format ${escapeShellArg cfg.logFormat}"
      ]
      ++ optionals (cfg.webSystemdSocket != false) [
        "--web.systemd-socket ${escapeShellArg cfg.webSystemdSocket}"
      ]
      ++ optionals (cfg.webConfigFile != null) [
        "--web.config.file ${escapeShellArg cfg.webConfigFile}"
      ]
      ++ cfg.extraFlags
    );

    serviceConfig.RestrictAddressFamilies = [
      "AF_INET"
      "AF_INET6"
      "AF_UNIX"
    ];
    serviceConfig.EnvironmentFile = lib.mkIf (cfg.connectionEnvFile != null) [
      cfg.connectionEnvFile
    ];
  };

  imports = [
    (lib.mkRemovedOptionModule [ "connectionStringFile" ] ''
      As replacement, the option `services.prometheus.exporters.pgbouncer.connectionEnvFile`
      has been added. In contrast to `connectionStringFile` it must be an environment file
      with the connection string being set to `PGBOUNCER_EXPORTER_CONNECTION_STRING`.

      The change was necessary since the former option wrote the contents of the file
      into the cmdline of the exporter making the connection string effectively
      world-readable.
    '')
    {
      options.warnings = options.warnings;
      options.assertions = options.assertions;
    }
  ];
}
