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

let
  cfg = config.services.docuseal;

  env = {
    RAILS_ENV = "production";
    NODE_ENV = "production";
    WORKDIR = "/var/lib/docuseal";

    PORT = toString (cfg.port);
    HOST = cfg.host;

    REDIS_URL = "redis://${cfg.redis.host}:${toString cfg.redis.port}";
  }
  // cfg.extraConfig;
in
{
  options.services.docuseal = {
    enable = lib.mkEnableOption "DocuSeal, open source document signing";

    package = lib.mkPackageOption pkgs "docuseal" { };

    secretKeyBaseFile = lib.mkOption {
      description = ''
        Path to file containing the secret key base.
        A new secret key base can be generated by running:

        `openssl rand -hex 64`

        If this file does not exist, it will be created with a new secret key base.
      '';
      default = "/var/lib/docuseal/secrets/secret-key-base";
      type = lib.types.path;
    };

    host = lib.mkOption {
      description = "DocuSeal host.";
      type = lib.types.str;
      default = "127.0.0.1";
    };

    port = lib.mkOption {
      description = "DocuSeal port.";
      type = lib.types.port;
      default = 3000;
    };

    extraConfig = lib.mkOption {
      type = lib.types.attrs;
      default = { };
      description = ''
        Extra environment variables to pass to DocuSeal services.

        Refer to <https://www.docuseal.com/docs/configuring-docuseal-via-environment-variables>.
      '';
    };

    extraEnvFiles = lib.mkOption {
      type = with lib.types; listOf path;
      default = [ ];
      description = ''
        Extra environment files to pass to DocuSeal services. Useful for passing down environmental secrets.
        e.g. DATABASE_URL
      '';
      example = [ "/etc/docuseal/s3config.env" ];
    };

    redis = {
      createLocally = lib.mkOption {
        type = lib.types.bool;
        default = true;
        description = "Whether to create a local redis automatically.";
      };

      name = lib.mkOption {
        type = lib.types.str;
        default = "docuseal";
        description = ''
          Name of the redis server. Only used if `createLocally` is set to true.
        '';
      };

      host = lib.mkOption {
        type = lib.types.str;
        default = "localhost";
        description = ''
          Redis server address.
        '';
      };

      port = lib.mkOption {
        type = lib.types.port;
        default = 6379;
        description = "Port of the redis server.";
      };
    };
  };

  config = lib.mkIf cfg.enable {

    assertions = [
      {
        assertion = cfg.redis.createLocally -> cfg.redis.host == "localhost";
        message = "the redis host must be localhost if services.docuseal.redis.createLocally is set to true";
      }
    ];

    systemd.services.docuseal = {
      description = "DocuSeal server";
      wantedBy = [ "multi-user.target" ];
      environment = env;
      serviceConfig = {
        Type = "simple";
        ExecStartPre = pkgs.writeShellScript "docuseal-pre-script" ''
          cat > /var/lib/docuseal/docuseal.env <<EOF
          SECRET_KEY_BASE="$(cat ${cfg.secretKeyBaseFile})"
          EOF
        '';
        ExecStart = "${cfg.package}/bin/rails server --pid=/var/lib/docuseal/docuseal.pids";
        Restart = "always";
        EnvironmentFile = [ "docuseal.env" ] ++ cfg.extraEnvFiles;
        # Runtime directory and mode
        RuntimeDirectory = "docuseal";
        RuntimeDirectoryMode = "0750";
        # System Call Filtering
        SystemCallFilter = [
          "@system-service"
          "~@privileged"
        ];
        # User and group
        DynamicUser = true;
        # Working directory
        WorkingDirectory = "/var/lib/docuseal";
        # State directory and mode
        StateDirectory = "docuseal";
        StateDirectoryMode = "0750";
        # Logs directory and mode
        LogsDirectory = "docuseal";
        LogsDirectoryMode = "0750";
        # Proc filesystem
        ProcSubset = "pid";
        ProtectProc = "invisible";
        # Access write directories
        UMask = "0027";
        # Capabilities
        CapabilityBoundingSet = "";
        # Security
        NoNewPrivileges = true;
        # Sandboxing
        ProtectSystem = "strict";
        ProtectHome = true;
        PrivateTmp = true;
        PrivateDevices = true;
        PrivateUsers = (cfg.port >= 1024);
        ProtectClock = true;
        ProtectHostname = true;
        ProtectKernelLogs = true;
        ProtectKernelModules = true;
        ProtectKernelTunables = true;
        ProtectControlGroups = true;
        RestrictAddressFamilies = [
          "AF_UNIX"
          "AF_INET"
          "AF_INET6"
          "AF_NETLINK"
        ];
        RestrictNamespaces = true;
        LockPersonality = true;
        MemoryDenyWriteExecute = false;
        RestrictRealtime = true;
        RestrictSUIDSGID = true;
        RemoveIPC = true;
        PrivateMounts = true;
        # System Call Filtering
        SystemCallArchitectures = "native";
      }
      // lib.optionalAttrs (cfg.port < 1024) {
        AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
        CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
      };
    };

    services.redis = lib.optionalAttrs cfg.redis.createLocally {
      servers."${cfg.redis.name}" = {
        enable = true;
        port = cfg.redis.port;
      };
    };
  };

  meta.maintainers = with lib.maintainers; [ stunkymonkey ];
}
