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

let
  inherit (lib)
    mkEnableOption
    mkIf
    mkOption
    mkPackageOption
    mkRemovedOptionModule
    types
    ;

  cfg = config.services.plantuml-server;

in

{
  imports = [
    (mkRemovedOptionModule [
      "services"
      "plantuml-server"
      "allowPlantumlInclude"
    ] "This option has been removed from PlantUML.")
  ];

  options = {
    services.plantuml-server = {
      enable = mkEnableOption "PlantUML server";

      package = mkPackageOption pkgs "plantuml-server" { };

      packages = {
        jdk = mkPackageOption pkgs "jdk" { };
        jetty = mkPackageOption pkgs "jetty" {
          default = [ "jetty_12" ];
        };
      };

      user = mkOption {
        type = types.str;
        default = "plantuml";
        description = "User which runs PlantUML server.";
      };

      group = mkOption {
        type = types.str;
        default = "plantuml";
        description = "Group which runs PlantUML server.";
      };

      home = mkOption {
        type = types.path;
        default = "/var/lib/plantuml";
        description = "Home directory of the PlantUML server instance.";
      };

      listenHost = mkOption {
        type = types.str;
        default = "127.0.0.1";
        description = "Host to listen on.";
      };

      listenPort = mkOption {
        type = types.port;
        default = 8080;
        description = "Port to listen on.";
      };

      plantumlLimitSize = mkOption {
        type = types.int;
        default = 4096;
        description = "Limits image width and height.";
      };

      graphvizPackage = mkPackageOption pkgs "graphviz" { };

      plantumlStats = mkOption {
        type = types.bool;
        default = false;
        description = "Set it to on to enable statistics report (https://plantuml.com/statistics-report).";
      };

      httpAuthorization = mkOption {
        type = types.nullOr types.str;
        default = null;
        description = "When calling the proxy endpoint, the value of HTTP_AUTHORIZATION will be used to set the HTTP Authorization header.";
      };
    };
  };

  config = mkIf cfg.enable {
    systemd.services.plantuml-server = {
      description = "PlantUML server";
      wantedBy = [ "multi-user.target" ];

      environment = {
        PLANTUML_LIMIT_SIZE = toString cfg.plantumlLimitSize;
        GRAPHVIZ_DOT = "${cfg.graphvizPackage}/bin/dot";
        PLANTUML_STATS = if cfg.plantumlStats then "on" else "off";
        HTTP_AUTHORIZATION = cfg.httpAuthorization;
      };
      script = ''
        ${cfg.packages.jdk}/bin/java \
          -jar ${cfg.packages.jetty}/start.jar \
            --module=http,ee11-deploy,ee11-jsp \
            -Djetty.home=${cfg.packages.jetty} \
            -Djetty.base=${cfg.package} \
            -Djetty.http.host=${cfg.listenHost} \
            -Djetty.http.port=${toString cfg.listenPort}
      '';

      serviceConfig = {
        User = cfg.user;
        Group = cfg.group;
        StateDirectory = mkIf (cfg.home == "/var/lib/plantuml") "plantuml";
        StateDirectoryMode = mkIf (cfg.home == "/var/lib/plantuml") "0750";

        # Hardening
        AmbientCapabilities = [ "" ];
        CapabilityBoundingSet = [ "" ];
        DynamicUser = true;
        LockPersonality = true;
        NoNewPrivileges = true;
        PrivateDevices = true;
        PrivateNetwork = false;
        PrivateTmp = true;
        PrivateUsers = true;
        ProtectClock = true;
        ProtectControlGroups = true;
        ProtectHome = true;
        ProtectHostname = true;
        ProtectKernelLogs = true;
        ProtectKernelModules = true;
        ProtectKernelTunables = true;
        ProtectSystem = "strict";
        RestrictAddressFamilies = [
          "AF_UNIX"
          "AF_INET"
          "AF_INET6"
        ];
        RestrictNamespaces = true;
        RestrictRealtime = true;
        RestrictSUIDSGID = true;
        SystemCallArchitectures = "native";
        SystemCallFilter = [ "@system-service" ];
      };
    };
  };

  meta.maintainers = with lib.maintainers; [
    truh
    anthonyroussel
  ];
}
