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

with lib;

let
  dataDir = "/var/lib/matrix-appservice-slack";
  registrationFile = "${dataDir}/slack-registration.yaml";
  #appDir = "${pkgs.matrix-appservice-slack}/${pkgs.matrix-appservice-slack.passthru.nodeAppDir}";
  cfg = config.services.matrix-appservice-slack;
  # TODO: switch to configGen.json once RFC42 is implemented
  settingsFile = pkgs.writeText "matrix-appservice-slack-settings.json" (builtins.toJSON cfg.settings);

in
{
  options = {
    services.matrix-appservice-slack = {
      enable = mkEnableOption "a bridge between Matrix and Slack";

      settings = mkOption rec {
        # TODO: switch to types.config.json as prescribed by RFC42 once it's implemented
        type = types.attrs;
        apply = recursiveUpdate default;
        default = {
          # empty values necessary for registration file generation
          # actual values defined in environmentFile
          auth = {
            clientID = "";
            botToken = "";
          };
        };
        example = literalExample ''
          {
            bridge = {
              domain = "public-domain.tld";
              homeserverUrl = "http://public-domain.tld:8008";
            };
          }
        '';
        description = ''
          <filename>config.yaml</filename> configuration as a Nix attribute set.
          </para>

          <para>
          Configuration options should match those described in
          <link xlink:href="https://github.com/Half-Shot/matrix-appservice-discord/blob/master/config/config.sample.yaml">
          config.sample.yaml</link>.
          </para>

          <para>
          <option>config.bridge.domain</option> and <option>config.bridge.homeserverUrl</option>
          should be set to match the public host name of the Matrix homeserver for webhooks and avatars to work.
          </para>

          <para>
          Secret tokens should be specified using <option>environmentFile</option>
          instead of this world-readable attribute set.
        '';
      };

      environmentFile = mkOption {
        type = types.nullOr types.path;
        default = null;
        description = ''
          File containing environment variables to be passed to the matrix-appservice-discord service,
          in which secret tokens can be specified securely by defining values for
          <literal>APPSERVICE_SLACK_AUTH_CLIENT_I_D</literal> and
          <literal>APPSERVICE_SLACK_AUTH_BOT_TOKEN</literal>.
        '';
      };

      url = mkOption {
        type = types.str;
        default = "http://localhost:${toString cfg.port}";
        description = ''
          The URL where the application service is listening for HS requests.
        '';
      };

      port = mkOption {
        type = types.port;
        default = 9898; # from https://github.com/matrix-org/matrix-appservice-slack/blob/develop/config/config.sample.yaml#L70
        description = ''
          Port number on which the bridge should listen for internal communication with the Matrix homeserver.
        '';
      };

      localpart = mkOption {
        type = with types; nullOr str;
        default = null;
        description = ''
          The user_id localpart to assign to the AS.
        '';
      };

      serviceDependencies = mkOption {
        type = with types; listOf str;
        default = optional config.services.matrix-synapse.enable "matrix-synapse.service";
        description = ''
          List of Systemd services to require and wait for when starting the application service,
          such as the Matrix homeserver if it's running on the same host.
        '';
      };
    };
  };

  config = mkIf cfg.enable {
    systemd.services.matrix-appservice-slack = {
      description = "A bridge between Matrix and Slack.";

      wantedBy = [ "multi-user.target" ];
      wants = [ "network-online.target" ] ++ cfg.serviceDependencies;
      after = [ "network-online.target" ] ++ cfg.serviceDependencies;

      preStart = ''
        if [ ! -f '${registrationFile}' ]; then
          ${pkgs.matrix-appservice-slack}/bin/matrix-appservice-slack \
            --generate-registration \
            --url=${escapeShellArg cfg.url} \
            ${optionalString (cfg.localpart != null) "--localpart=${escapeShellArg cfg.localpart}"} \
            --config='${settingsFile}' \
            --file='${registrationFile}'
        fi
      '';

      serviceConfig = {
        Type = "simple";
        Restart = "always";

        ProtectSystem = "strict";
        ProtectHome = true;
        ProtectKernelTunables = true;
        ProtectKernelModules = true;
        ProtectControlGroups = true;

        DynamicUser = true;
        PrivateTmp = true;
        #WorkingDirectory = appDir;
        StateDirectory = baseNameOf dataDir;
        UMask = 0027;
        EnvironmentFile = cfg.environmentFile;

        ExecStart = ''
          ${pkgs.matrix-appservice-slack}/bin/matrix-appservice-slack \
            --file='${registrationFile}' \
            --config='${settingsFile}' \
            --port='${toString cfg.port}'
        '';
      };
    };
  };

  meta.maintainers = with maintainers; [ chickensoupandrice ];
}

