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

with builtins;
with lib;
let
  cfg = config.tv.ejabberd;

  out = {
    options.tv.ejabberd = api;
    config = mkIf cfg.enable imp;
  };

  api = {
    enable = mkEnableOption "tv.ejabberd";

    certFile = mkOption {
      type = types.str;
      default = toString <secrets/ejabberd.pem>;
    };

    hosts = mkOption {
      type = with types; listOf str;
    };
  };

  imp = {
    environment.systemPackages = [ my-ejabberdctl ];

    systemd.services.ejabberd = {
      wantedBy = [ "multi-user.target" ];
      after = [ "network.target" ];
      serviceConfig = {
        Type = "oneshot";
        RemainAfterExit = "yes";
        PermissionsStartOnly = "true";
        SyslogIdentifier = "ejabberd";
        User = user.name;
        ExecStartPre = pkgs.writeScript "ejabberd-start" ''
          #! /bin/sh
          install -o ${user.name} -m 0400 ${cfg.certFile} /etc/ejabberd/ejabberd.pem
        '';
        ExecStart = pkgs.writeScript "ejabberd-service" ''
          #! /bin/sh
          ${my-ejabberdctl}/bin/ejabberdctl start
        '';
      };
    };

    users.extraUsers = singleton {
      inherit (user) name uid;
      home = "/var/ejabberd";
      createHome = true;
    };
  };

  user = {
    name = "ejabberd";
    uid = 3499746127; # genid ejabberd
  };

  my-ejabberdctl = pkgs.writeScriptBin "ejabberdctl" ''
    #! /bin/sh
    set -euf
    exec env \
        SPOOLDIR=/var/ejabberd \
        EJABBERD_CONFIG_PATH=${config-file} \
      ${pkgs.ejabberd}/bin/ejabberdctl \
        --logs /var/ejabberd \
        "$@"
  '';

  config-file = pkgs.writeText "ejabberd.cfg" ''
    {loglevel, 3}.
    {hosts, ${toErlang cfg.hosts}}.
    {listen,
     [
      {5222, ejabberd_c2s, [
          starttls,
          {certfile, "/etc/ejabberd/ejabberd.pem"},
          {access, c2s},
          {shaper, c2s_shaper},
          {max_stanza_size, 65536}
               ]},
      {5269, ejabberd_s2s_in, [
             {shaper, s2s_shaper},
             {max_stanza_size, 131072}
            ]},
      {5280, ejabberd_http, [
           captcha,
           http_bind,
           http_poll,
           web_admin
          ]}
     ]}.
    {s2s_use_starttls, required}.
    {s2s_certfile, "/etc/ejabberd/ejabberd.pem"}.
    {auth_method, internal}.
    {shaper, normal, {maxrate, 1000}}.
    {shaper, fast, {maxrate, 50000}}.
    {max_fsm_queue, 1000}.
    {acl, local, {user_regexp, ""}}.
    {access, max_user_sessions, [{10, all}]}.
    {access, max_user_offline_messages, [{5000, admin}, {100, all}]}.
    {access, local, [{allow, local}]}.
    {access, c2s, [{deny, blocked},
             {allow, all}]}.
    {access, c2s_shaper, [{none, admin},
              {normal, all}]}.
    {access, s2s_shaper, [{fast, all}]}.
    {access, announce, [{allow, admin}]}.
    {access, configure, [{allow, admin}]}.
    {access, muc_admin, [{allow, admin}]}.
    {access, muc_create, [{allow, local}]}.
    {access, muc, [{allow, all}]}.
    {access, pubsub_createnode, [{allow, local}]}.
    {access, register, [{allow, all}]}.
    {language, "en"}.
    {modules,
     [
      {mod_adhoc,    []},
      {mod_announce, [{access, announce}]},
      {mod_blocking,[]},
      {mod_caps,     []},
      {mod_configure,[]},
      {mod_disco,    []},
      {mod_irc,      []},
      {mod_http_bind, []},
      {mod_last,     []},
      {mod_muc,      [
          {access, muc},
          {access_create, muc_create},
          {access_persistent, muc_create},
          {access_admin, muc_admin}
         ]},
      {mod_offline,  [{access_max_user_messages, max_user_offline_messages}]},
      {mod_ping,     []},
      {mod_privacy,  []},
      {mod_private,  []},
      {mod_pubsub,   [
          {access_createnode, pubsub_createnode},
          {ignore_pep_from_offline, true},
          {last_item_cache, false},
          {plugins, ["flat", "hometree", "pep"]}
         ]},
      {mod_register, [
          {welcome_message, {"Welcome!",
                 "Hi.\nWelcome to this XMPP server."}},
          {ip_access, [{allow, "127.0.0.0/8"},
                 {deny, "0.0.0.0/0"}]},
          {access, register}
         ]},
      {mod_roster,   []},
      {mod_shared_roster,[]},
      {mod_stats,    []},
      {mod_time,     []},
      {mod_vcard,    []},
      {mod_version,  []}
     ]}.
  '';


  # XXX this is a placeholder that happens to work the default strings.
  toErlang = builtins.toJSON;

in
out