{ pkgs, lib, config, ... }:
let
  inherit (builtins) hasAttr;
  inherit (config.services) openldap postfix dovecot2;
  inherit (config.users) users groups;
  domain = "sourcephile.fr";
  domainGroup = "sourcephile";
  domainOrg = "sourcephile";
  domainSuffix = "dc=" + lib.concatStringsSep ",dc=" (lib.splitString "." domain);
  posixAccount = pkgs.callPackage (import ./posixAccount.nix) { inherit domain domainSuffix domainGroup; };
in
{
  users.groups.${domainGroup} = {
    gid = 20000;
    members = [
      users."julm".name
    ];
  };
  services.openldap = {
    # sudo ldapsearch -LLL -H ldapi:// -D cn=admin,cn=config -Y EXTERNAL -b 'olcDatabase={1}mdb,cn=config' -s sub
    settings.children."olcDatabase={1}mdb".attrs = {
      objectClass = [ "olcDatabaseConfig" "olcMdbConfig" ];
      olcDatabase = "{1}mdb";
      olcSuffix = domainSuffix;
      olcDbDirectory = "/var/lib/openldap/${domainSuffix}";
      olcDbIndex = [
        "objectClass eq"
        "cn,uid eq"
        "uidNumber,gidNumber eq"
        "member,memberUid eq"
        "mail eq"
        "mailAlias eq"
        "mailEnabled eq"
      ];
      olcAccess = [
        ''to attrs=userPassword
      by self write
      by anonymous auth
      by dn="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write
      by * none
      ''
        ''to attrs=shadowLastChange
      by self write
      by dn="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write
      by * none
      ''
        ''to dn.sub="ou=posix,${domainSuffix}"
      by self read
      ${lib.optionalString (hasAttr postfix.user users) ''by dn="gidNumber=${toString groups.postfix.gid}+uidNumber=${toString users.postfix.uid},cn=peercred,cn=external,cn=auth" read''}
      ${lib.optionalString (hasAttr dovecot2.user users) ''by dn="gidNumber=${toString groups.dovecot2.gid}+uidNumber=${toString users.dovecot2.uid},cn=peercred,cn=external,cn=auth" read''}
      by dn="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" read
      ''
        ''to *
      by self read
      by * none
      ''
      ];
      # Checkpoint the database periodically in case of system failure
      # and to speed up slapd shutdown.
      olcDbCheckpoint = "512 30";
      # Database max size is 1G
      olcDbMaxSize = "1073741824";
      olcLastMod = "TRUE";
      # Database superuser. Needed for syncrepl.
      olcRootDN = "cn=admin,${domainSuffix}";
      # Superuser password, generated with slappasswd -h "{SSHA}"
      # Commented-out because SASL EXTERNAL mechanism is used.
      #olcRootPW = "{SSHA}COkATGNe7rs/g8vWcYP5rqt4u5sWdMgP";
    };
    # sudo ldapsearch -LLL -H ldapi:// -D cn=admin,cn=config -Y EXTERNAL -b 'ou=posix,dc=sourcephile,dc=fr' -s sub
    declarativeContents."${domainSuffix}" = ''
      dn: ${domainSuffix}
      objectClass: top
      objectClass: dcObject
      objectClass: organization
      o: ${domainOrg}

      dn: cn=admin,${domainSuffix}
      objectClass: simpleSecurityObject
      objectClass: organizationalRole
      description: ${domainOrg} LDAP administrator
      roleOccupant: ${domainSuffix}
      userPassword:

      dn: ou=posix,${domainSuffix}
      objectClass: top
      objectClass: organizationalUnit

      dn: ou=accounts,ou=posix,${domainSuffix}
      objectClass: top
      objectClass: organizationalUnit

      dn: ou=groups,ou=posix,${domainSuffix}
      objectClass: top
      objectClass: organizationalUnit

    ''
    /*
      dn: cn=${domainGroup},ou=groups,ou=posix,${domainSuffix}
      objectClass: top
      objectClass: posixGroup
      gidNumber: 20000
      memberUid: julm

      dn: cn=autogeree,ou=groups,ou=posix,${domainSuffix}
      objectClass: top
      objectClass: posixGroup
      gidNumber: 20001
      memberUid: julm
    */
    + lib.concatMapStrings posixAccount [
      rec {
        uid = "julm";
        cn = "Julien Moutinho";
        sn = uid;
        uidNumber = users."julm".uid;
        gidNumber = groups."users".gid;
        mailAlias = [ "julien.moutinho" ];
        userPassword = builtins.readFile (./. + "/${domain}/${uid}/hashedPassword.clear");
        mailHomeDirectory = "/home/${uid}/mail/${domain}";
        mailQuota = "2G";
        mailStorageDirectory =
          let stateDir = "/var/lib/dovecot"; in
          # I'm personnaly using "maildir:" instead of "sdbox:" to be able to use a local (neo)mutt on it,
            # bypassing IMAP because (neo)mutt support of IMAP is very bad
            # (can't even have a decent $folder_format (with %n or %m) working,
            # neither sorting them by date).
            # WARNING: regarding the atomicity of backuping,
            # it's not a good idea to put the mails
            # and the index/control on different ZFS datasets like here.
          "maildir:/home/${uid}/mail/${domain}/mail:LAYOUT=maildir++:UTF-8:CONTROL=${stateDir}/control/${domain}/${uid}:INDEX=${stateDir}/index/${domain}/${uid}";
      }
      rec {
        uid = "testbox";
        cn = "Test Box";
        sn = uid;
        uidNumber = 21000;
        gidNumber = groups.${domainGroup}.gid;
        mailAlias = [ "test.box" ];
        userPassword = builtins.readFile (./. + "/${domain}/${uid}/hashedPassword.clear");
        #mailHomeDirectory = "/home/${uid}/mail/${domain}";
        mailQuota = "1G";
      }
    ];
  };
}