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

let
  domain = "jabber.c3d2.de";
in
{
  c3d2 = {
    deployment.server = "server10";
    hq.statistics.enable = true;
  };

  microvm.mem = 2048;

  networking = {
    hostName = "jabber";
    firewall = {
      allowedTCPPorts = [
        # Prosody
        80
        443
        5222
        5223
        5269
        5270
        5280
        5281
        # Coturn
        3478
        3479
      ];
      allowedUDPPorts = [
        # Coturn
        3478
        3479
      ];
    # TODO: allowedSCTPPorts
    };
  };

  security = {
    acme.certs."${domain}" = {
      extraDomainNames = [
        "chat.c3d2.de"
        "*.${domain}"
      ];
      # DynDNS method
      dnsProvider = "rfc2136";
      credentialsFile = config.sops.secrets."acme/credentials-file".path;
      reloadServices = [ "prosody" ];
      # Make keys accessible by putting them in prosody's group
      inherit (config.services.prosody) group;
    };
    dhparams = {
      enable = true;
      params.prosody = { };
    };
  };

  services = {
    backup.paths = [ "/var/lib/prosody/" ];

    collectd.plugins.exec = ''
      Exec "${config.services.collectd.user}" "${lib.getExe pkgs.ruby}" "${./prosody-stats.rb}"
    '';

    coturn = {
      enable = true;
      realm = "turn.${domain}";
      static-auth-secret-file = config.sops.secrets."coturn/static-auth-secret".path;
      use-auth-secret = true;
      extraConfig = ''
        external-ip=${zentralwerk.lib.dns.publicIPv4}/${zentralwerk.lib.config.site.net.serv.hosts4.jabber}
        # secure-stun # not supported by jabber

        # no old shit
        no-tlsv1
        no-tlsv1_1

        # strongly encouraged options to decrease amplification attacks
        no-rfc5780
        no-stun-backward-compatibility
        response-origin-only-with-rfc5780
      '';
    };

    postgresql = {
      enable = true;
      ensureDatabases = [ "prosody" ];
      ensureUsers = [{
        name = "prosody";
        ensurePermissions = {
          "DATABASE prosody" = "ALL PRIVILEGES";
        };
      }];
      package = pkgs.postgresql_16;
      upgrade.stopServices = [ "prosody" ];
    };

    #TODO: txt records?
    prosody = {
      enable = true;
      allowRegistration = false;
      admins = [ "astro@spaceboyz.net" "0@jabber.c3d2.de" "nek0@jabber.c3d2.de" "sandro@jabber.c3d2.de" ];
      package = pkgs.prosody.override {
        withCommunityModules = [ "cloud_notify" "cloud_notify_extensions" "firewall" "presence_cache" ];
        withExtraLuaPackages = luaPackages: with luaPackages; [
          luadbi-postgresql
          luaossl # required by cloud_notify_extensions
        ];
      };

      modules = {
        # HTTP stuff
        bosh = true;
        http_altconnect = true;
        http_files = true;
        websocket = true;

        admin_telnet = true;
        announce = true;
        carbons = true;
        csi_simple = true;
        mam = true;
        # File-transfer proxies are an outdated technology
        proxy65 = false;
        server_contact_info = true;
        smacks = true;
        s2s_bidi = true;
        turn_external = true;
      };

      ssl = {
        key = "/var/lib/acme/${domain}/key.pem";
        cert = "/var/lib/acme/${domain}/fullchain.pem";
        extraOptions = {
          dhparam = config.security.dhparams.params.nginx.path;
        };
      };

      # encryption is a must
      c2sRequireEncryption = true;
      s2sRequireEncryption = true;
      s2sSecureAuth = true;

      virtualHosts = {
        "${domain}" = {
          enabled = true;
          inherit domain;
        };
        "anon.${domain}" = {
          enabled = true;
          domain = "anon.${domain}";
          extraConfig = ''
            authentication = "anonymous"
          '';
        };
      };
      muc = [{
        domain = "chat.c3d2.de";
        maxHistoryMessages = 100;
        name = "Group chats";
      }];

      httpPorts = [ 80 5280 ];
      httpsPorts = [ 443 5281 ];
      uploadHttp = {
        domain = "upload.${domain}";
        uploadFileSizeLimit = "10 * 1024 * 1024";
        userQuota = 512 * 1024 * 1024;
        uploadExpireAfter = "2 * 60 * 60";
      };

      extraConfig =
        let
          prosodyFirewall = pkgs.writeText "antispam.pfw" ''
            %ZONE spam: creep.im, default.rs, sj.ms, anonym.im, xmpp.jp, safetyjabber.com, im.hot-chilli.net, jabb3r.org, draugr.de, laba.im, xmpp.sh, jabber.bitactive.com, 404.city, jabber.cd, jabber.jc-otto.de, jabster.pl, jabber.no, anoxinon.me, ubuntu-jabber.net, anonarchy.im, jabber.freenet.de, exploit.im, 616.pub, omemo.im, rsocks.net, chatwith.xyz, jabber.cz, jabbim.cz, blabber.im, jabber.root.cz, jabb.im, jabber.infos.ru, jabbim.pl, jabbim.com, linuxlovers.at, jabbim.ru, jabber.sk, njs.netlab.cz, jabba.biz, chatterboxtown.us, crime.io, 0nl1ne.at, verdammung.org, im.apinc.org, 0day.la, 0day.im, xabber.de, conversations.im, jabber.de, chinwag.im, thesecure.biz, shad0w.ru, yourdata.forsale, linux.monster, xmpp.international, paranoid.network, og.im, 4ept.net, darknet.im, ubuntu-jabber.de, nixnet.services, marxist.club, dw.live, 01337.io, sqli.io, breached.im, pwned.life, jabber.fr, chatterboxtown.us, xmpp.xxx, ybgood.de, ejabber.co, jabbers.one

            IN ROSTER?
            PASS.

            LEAVING: spam
            BOUNCE=policy-violation (Your domain has been blacklisted due to spam.)
          '';
        in
        /* lua */ ''
          c2s_direct_tls_ports = { 5223 }
          c2s_direct_tls_ssl = {
            key = "/var/lib/acme/${domain}/key.pem",
            certificate = "/var/lib/acme/${domain}/fullchain.pem",
          }
          s2s_direct_tls_ports = { 5270 }
          certificates = "/var/lib/acme"

          contact_info = {
            abuse = { "mailto:mail@c3d2.de" };
            admin = { "mailto:mail@c3d2.de" };
            feedback = { "mailto:mail@c3d2.de" };
            -- sales = { "mailto:mail@c3d2.de" }; -- we don't sell anything ;)
            security = { "mailto:mail@c3d2.de" };
            support = { "mailto:mail@c3d2.de" };
          }

          storage = "sql"
          sql = { driver = "PostgreSQL", database = "prosody", username = "prosody", password = "" }
          log = { info = "*syslog"; }

          firewall_scripts = { "${prosodyFirewall}" }

          -- How to get the IPv6 from config?
          external_addresses = { "${zentralwerk.lib.dns.publicIPv4}", "2a00:8180:2c00:282:e058:3ff:fea2:d83a", "${hostRegistry.jabber.ip4}", "${hostRegistry.jabber.ip6}" }
          trusted_proxies = { "127.0.0.1", "::1", "${hostRegistry.public-access-proxy.ip4}", "${hostRegistry.public-access-proxy.ip6}" }
          http_cors_override = {
            bosh = {
              enabled = true;
            };
            websocket = {
              enabled = true;
            };
          }
          http_default_host = "${domain}"
          http_host = "${domain}"
          http_external_url = "https://${domain}/"
          http_upload_file_size_limit = 10 * 1024 * 1024
          http_upload_expire_after = 60 * 60 * 24 * 7 -- a week in seconds

          turn_external_host = "turn.${domain}";
          turn_external_secret = "$PROSODY_TURN_SECRET"
        '';
    };
  };

  sops = {
    defaultSopsFile = ./secrets.yaml;
    secrets = {
      "acme/credentials-file" = { };
      "coturn/static-auth-secret".owner = "turnserver";
      "prosody/enviroment" = { };
      "restic/password" = { };
      "restic/repositories/server8" = { };
    };
  };

  systemd.services = {
    collectd.requires = [ "prosody.service" ];

    prosody.serviceConfig = {
      # Allow binding ports <1024
      AmbientCapabilities = "CAP_NET_BIND_SERVICE";
      EnvironmentFile = config.sops.secrets."prosody/enviroment".path;
    };
  };

  system.stateVersion = "21.05";
}
