{ config
, lib
, pkgs
, ...
}:
let
  cfg = config.services.blendfarm;
  json = pkgs.formats.json { };
  configFile = json.generate "ServerSettings" (defaultConfig // cfg.serverConfig);
  defaultConfig = {
    Port = 15000;
    BroadcastPort = 16342;
    BypassScriptUpdate = false;
    BasicSecurityPassword = null;
  };
in
{
  meta.maintainers = with lib.maintainers; [ gador ];

  options.services.blendfarm = with lib.types; {
    enable = lib.mkEnableOption "Blendfarm, a render farm management software for Blender.";
    package = lib.mkPackageOption pkgs "blendfarm" { };
    openFirewall = lib.mkEnableOption "Allow blendfarm network access through the firewall.";

    user = lib.mkOption {
      description = "User under which blendfarm runs.";
      default = "blendfarm";
      type = str;
    };

    group = lib.mkOption {
      description = "Group under which blendfarm runs.";
      default = "blendfarm";
      type = str;
    };

    basicSecurityPasswordFile = lib.mkOption {
      description = ''Path to the password file the client needs to connect to the server.
      The password must not contain a forward slash.'';
      default = null;
      type = nullOr str;
    };

    blenderPackage = lib.mkPackageOption pkgs "blender" { };

    serverConfig = lib.mkOption {
      description = "Server configuration";
      default = defaultConfig;
      type = submodule {
        freeformType = attrsOf anything;
        options = {
          Port = lib.mkOption {
            description = "Default port blendfarm server listens on.";
            default = 15000;
            type = types.port;
          };
          BroadcastPort = lib.mkOption {
            description = "Default port blendfarm server advertises itself on.";
            default = 16342;
            type = types.port;
          };

          BypassScriptUpdate = lib.mkOption {
            description = "Prevents blendfarm from replacing the .py self-generated scripts.";
            default = false;
            type = bool;
          };
        };
      };
    };
  };

  config = lib.mkIf cfg.enable {
    environment.systemPackages = [ cfg.package ];
    networking.firewall = lib.optionalAttrs (cfg.openFirewall) {
      allowedTCPPorts = [ cfg.serverConfig.Port ];
      allowedUDPPorts = [ cfg.serverConfig.BroadcastPort ];
    };

    systemd.services.blendfarm-server = {
      wantedBy = [ "multi-user.target" ];
      after = [ "network-online.target" ];
      wants = [ "network-online.target" ];
      description = "blendfarm server";
      path = [ cfg.blenderPackage ];
      preStart = ''
        rm -f ServerSettings
        install -m640 ${configFile} ServerSettings
        if [ ! -d "BlenderData/nix-blender-linux64" ]; then
          mkdir -p BlenderData/nix-blender-linux64
          echo "nix-blender" > VersionCustom
        fi
        rm -f BlenderData/nix-blender-linux64/blender
        ln -s ${lib.getExe cfg.blenderPackage} BlenderData/nix-blender-linux64/blender
      '' +
      lib.optionalString (cfg.basicSecurityPasswordFile != null) ''
        BLENDFARM_PASSWORD=$(${pkgs.systemd}/bin/systemd-creds cat BLENDFARM_PASS_FILE)
        sed -i "s/null/\"$BLENDFARM_PASSWORD\"/g" ServerSettings
      '';
      serviceConfig = {
        ExecStart = "${cfg.package}/bin/LogicReinc.BlendFarm.Server";
        DynamicUser = true;
        LogsDirectory = "blendfarm";
        StateDirectory = "blendfarm";
        WorkingDirectory = "/var/lib/blendfarm";
        User = cfg.user;
        Group = cfg.group;
        StateDirectoryMode = "0755";
        LoadCredential = lib.optional (cfg.basicSecurityPasswordFile != null) "BLENDFARM_PASS_FILE:${cfg.basicSecurityPasswordFile}";
        ReadWritePaths = "";
        CapabilityBoundingSet = "";
        RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
        RestrictNamespaces = true;
        PrivateDevices = true;
        PrivateUsers = true;
        ProtectClock = true;
        ProtectControlGroups = true;
        ProtectHome = true;
        ProtectKernelLogs = true;
        ProtectKernelModules = true;
        ProtectKernelTunables = true;
        SystemCallArchitectures = "native";
        SystemCallFilter = [
          "@system-service"
          "~@privileged"
          "@chown"
        ];
        RestrictRealtime = true;
        LockPersonality = true;
        UMask = "0066";
        ProtectHostname = true;
      };
    };

    users.users.blendfarm = {
      isSystemUser = true;
      group = "blendfarm";
    };
    users.groups.blendfarm = { };
  };
}
