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

let
  cfg = config.services.tts;
in

{
  options.services.tts =
    let
      inherit (lib)
        literalExpression
        mkOption
        mkEnableOption
        types
        ;
    in
    {
      servers = mkOption {
        type = types.attrsOf (
          types.submodule (
            { ... }:
            {
              options = {
                enable = mkEnableOption "Coqui TTS server";

                port = mkOption {
                  type = types.port;
                  example = 5000;
                  description = ''
                    Port to bind the TTS server to.
                  '';
                };

                model = mkOption {
                  type = types.nullOr types.str;
                  default = "tts_models/en/ljspeech/tacotron2-DDC";
                  example = null;
                  description = ''
                    Name of the model to download and use for speech synthesis.

                    Check `tts-server --list_models` for possible values.

                    Set to `null` to use a custom model.
                  '';
                };

                useCuda = mkOption {
                  type = types.bool;
                  default = false;
                  example = true;
                  description = ''
                    Whether to offload computation onto a CUDA compatible GPU.
                  '';
                };

                extraArgs = mkOption {
                  type = types.listOf types.str;
                  default = [ ];
                  description = ''
                    Extra arguments to pass to the server commandline.
                  '';
                };
              };
            }
          )
        );
        default = { };
        example = literalExpression ''
          {
            english = {
              port = 5300;
              model = "tts_models/en/ljspeech/tacotron2-DDC";
            };
            german = {
              port = 5301;
              model = "tts_models/de/thorsten/tacotron2-DDC";
            };
            dutch = {
              port = 5302;
              model = "tts_models/nl/mai/tacotron2-DDC";
            };
          }
        '';
        description = ''
          TTS server instances.
        '';
      };
    };

  config =
    let
      inherit (lib)
        mkIf
        mapAttrs'
        nameValuePair
        optionalString
        concatMapStringsSep
        escapeShellArgs
        ;
    in
    mkIf (cfg.servers != { }) {
      systemd.services = mapAttrs' (
        server: options:
        nameValuePair "tts-${server}" {
          description = "Coqui TTS server instance ${server}";
          after = [
            "network-online.target"
          ];
          wantedBy = [
            "multi-user.target"
          ];
          path = with pkgs; [
            espeak-ng
          ];
          environment.HOME = "/var/lib/tts";
          serviceConfig = {
            DynamicUser = true;
            User = "tts";
            StateDirectory = "tts";
            ExecStart =
              "${pkgs.tts}/bin/tts-server --port ${toString options.port} "
              + optionalString (options.model != null) "--model_name ${options.model} "
              + optionalString (options.useCuda) "--use_cuda "
              + (escapeShellArgs options.extraArgs);
            CapabilityBoundingSet = "";
            DeviceAllow =
              if options.useCuda then
                [
                  # https://docs.nvidia.com/dgx/pdf/dgx-os-5-user-guide.pdf
                  "/dev/nvidia1"
                  "/dev/nvidia2"
                  "/dev/nvidia3"
                  "/dev/nvidia4"
                  "/dev/nvidia-caps/nvidia-cap1"
                  "/dev/nvidia-caps/nvidia-cap2"
                  "/dev/nvidiactl"
                  "/dev/nvidia-modeset"
                  "/dev/nvidia-uvm"
                  "/dev/nvidia-uvm-tools"
                ]
              else
                "";
            DevicePolicy = "closed";
            LockPersonality = true;
            # jit via numba->llvmpipe
            MemoryDenyWriteExecute = false;
            PrivateDevices = true;
            PrivateUsers = true;
            ProtectHome = true;
            ProtectHostname = true;
            ProtectKernelLogs = true;
            ProtectKernelModules = true;
            ProtectKernelTunables = true;
            ProtectControlGroups = true;
            ProtectProc = "invisible";
            ProcSubset = "pid";
            RestrictAddressFamilies = [
              "AF_UNIX"
              "AF_INET"
              "AF_INET6"
            ];
            RestrictNamespaces = true;
            RestrictRealtime = true;
            SystemCallArchitectures = "native";
            SystemCallFilter = [
              "@system-service"
              "~@privileged"
            ];
            UMask = "0077";
          };
        }
      ) cfg.servers;
    };
}
