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

let
  cfg = config.services.activity-relay;

  configFile = pkgs.writeText "activity-relay.json" (
    builtins.toJSON ({
      ACTOR_PEM = cfg.actorPem.path;
      REDIS_URL = cfg.redisUrl;
      RELAY_BIND = cfg.relay.bind;
      RELAY_SERVICENAME = cfg.relay.serviceName;
      RELAY_DOMAIN = cfg.relay.domain;
      RELAY_SUMMARY = cfg.relay.summary;
      JOB_CONCURRENCY = cfg.jobConcurrency;
    } // lib.optionalAttrs (cfg.relay.icon != null) {
      RELAY_ICON = cfg.relay.icon;
    } // lib.optionalAttrs (cfg.relay.image != null) {
      RELAY_IMAGE = cfg.relay.image;
    }));
in
{
  options.services.activity-relay = {
    enable = lib.mkEnableOption "Activity-Relay ActivityPub relay server";

    package = lib.mkOption {
      type = lib.types.package;
      default = pkgs.activity-relay;
    };

    user = lib.mkOption {
      type = lib.types.str;
      default = "relay";
    };
    group = lib.mkOption {
      type = lib.types.str;
      default = "relay";
    };

    actorPem.path = lib.mkOption {
      type = lib.types.str;
      default = "${config.users.users.${cfg.user}.home}/actor.pem";
    };
    actorPem.generate = lib.mkOption {
      type = lib.types.bool;
      default = true;
    };

    redisUrl = lib.mkOption {
      type = lib.types.str;
      default = "redis://127.0.0.1:6379";
    };

    relay = {
      bind = lib.mkOption {
        type = lib.types.str;
        default = "0.0.0.0:8080";
      };
      domain = lib.mkOption {
        type = lib.types.str;
      };
      serviceName = lib.mkOption {
        type = lib.types.str;
        default = "NixOS Activity-Relay";
      };
      summary = lib.mkOption {
        type = lib.types.str;
        default = "";
      };
      icon = lib.mkOption {
        type = with lib.types; nullOr str;
        default = null;
      };
      image = lib.mkOption {
        type = with lib.types; nullOr str;
        default = null;
      };
    };

    jobConcurrency = lib.mkOption {
      type = lib.types.int;
      default = 50;
    };
  };

  config = lib.mkIf cfg.enable {
    users = {
      users.${cfg.user} = {
        isSystemUser = true;
        inherit (cfg) group;
        home = "/var/lib/activity-relay";
      };
      groups.${cfg.group} = {};
    };

    systemd.services = {
      relay-api = {
        description = "Activity-Relay server";
        after = [ "network.target" ];
        wantedBy = [ "multi-user.target" ];
        serviceConfig = {
          User = cfg.user;
          Group = cfg.group;
          StateDirectory = "activity-relay";
          CapabilityBoundingSet = "";
          PrivateDevices = true;
          PrivateUsers = true;
          ProtectHome = true;
          ProtectKernelLogs = true;
          ProtectProc = "invisible";
          RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
          RestrictNamespaces = true;
          SystemCallArchitectures = "native";
          # SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ];
          Restart = lib.mkDefault "always";
          RestartSec = 10;
        };
        script = ''
          ${lib.optionalString cfg.actorPem.generate ''
            if ! [ -e "${cfg.actorPem.path}" ]; then
              ${lib.getExe pkgs.openssl} genrsa -traditional > "${cfg.actorPem.path}"
            fi
          ''}

          exec ${lib.getExe cfg.package} -c ${configFile} -v server
        '';
      };

      relay-worker = {
        description = "Activity-Relay worker";
        after = [ "network.target" "relay-api.service" ];
        wantedBy = [ "multi-user.target" ];
        serviceConfig = {
          User = cfg.user;
          Group = cfg.group;
          StateDirectory = "activity-relay";
          CapabilityBoundingSet = "";
          PrivateDevices = true;
          PrivateUsers = true;
          ProtectHome = true;
          ProtectKernelLogs = true;
          ProtectProc = "invisible";
          RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
          RestrictNamespaces = true;
          SystemCallArchitectures = "native";
          # SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ];
          Restart = lib.mkDefault "always";
          RestartSec = 10;
          ExecStart = "${lib.getExe cfg.package} -c ${configFile} -v worker";
        };
      };
    };
  };
}
