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

with lib;

let
  cfg = config.fudo.mail-server;

  createDomainDkimCert = dom:
    let
      dkim_key = "${cfg.dkim.key-directory}/${dom}.${cfg.dkim.selector}.key";
      dkim_txt = "${cfg.dkim.key-directory}/${dom}.${cfg.dkim.selector}.txt";
    in
      ''
          if [ ! -f "${dkim_key}" ] || [ ! -f "${dkim_txt}" ]
          then
              ${cfg.dkim.package}/bin/opendkim-genkey -s "${cfg.dkim.selector}" \
                                                   -d "${dom}" \
                                                   --bits="${toString cfg.dkim.key-bits}" \
                                                   --directory="${cfg.dkim.key-directory}"
              mv "${cfg.dkim.key-directory}/${cfg.dkim.selector}.private" "${dkim_key}"
              mv "${cfg.dkim.key-directory}/${cfg.dkim.selector}.txt" "${dkim_txt}"
              echo "Generated key for domain ${dom} selector ${cfg.dkim.selector}"
          fi
        '';

  createAllCerts = lib.concatStringsSep "\n" (map createDomainDkimCert cfg.local-domains);

  keyTable = pkgs.writeText "opendkim-KeyTable"
    (lib.concatStringsSep "\n" (lib.flip map cfg.local-domains
      (dom: "${dom} ${dom}:${cfg.dkim.selector}:${cfg.dkim.key-directory}/${dom}.${cfg.dkim.selector}.key")));
  signingTable = pkgs.writeText "opendkim-SigningTable"
    (lib.concatStringsSep "\n" (lib.flip map cfg.local-domains (dom: "${dom} ${dom}")));

  dkim = config.services.opendkim;
  args = [ "-f" "-l" ] ++ lib.optionals (dkim.configFile != null) [ "-x" dkim.configFile ];
in
{

  options.fudo.mail-server.dkim = {
    signing = mkOption {
      type = types.bool;
      default = true;
      description = "Enable dkim signatures for mail.";
    };

    key-directory = mkOption {
      type = types.str;
      default = "/var/dkim";
      description = "Path to use to store DKIM keys.";
    };

    selector = mkOption {
      type = types.str;
      default = "mail";
      description = "Name to use for mail-signing keys.";
    };

    key-bits = mkOption {
      type = types.int;
      default = 2048;
      description = ''
            How many bits in generated DKIM keys. RFC6376 advises minimum 1024-bit keys.

            If you have already deployed a key with a different number of bits than specified
            here, then you should use a different selector (dkimSelector). In order to get
            this package to generate a key with the new number of bits, you will either have to
            change the selector or delete the old key file.
      '';
    };

    package = mkOption {
      type = types.package;
      default = pkgs.opendkim;
      description = "OpenDKIM package to use.";
    };
  };

  config = mkIf (cfg.dkim.signing && cfg.enable) {
    services.opendkim = {
      enable = true;
      selector = cfg.dkim.selector;
      domains = "csl:${builtins.concatStringsSep "," cfg.local-domains}";
      configFile = pkgs.writeText "opendkim.conf" (''
          Canonicalization relaxed/simple
          UMask 0002
          Socket ${dkim.socket}
          KeyTable file:${keyTable}
          SigningTable file:${signingTable}
        '' + (lib.optionalString cfg.debug ''
          Syslog yes
          SyslogSuccess yes
          LogWhy yes
        ''));
    };

    users.users = {
      "${config.services.postfix.user}" = {
        extraGroups = [ "${config.services.opendkim.group}" ];
      };
    };

    systemd.services.opendkim = {
      preStart = lib.mkForce createAllCerts;
      serviceConfig = {
        ExecStart = lib.mkForce "${cfg.dkim.package}/bin/opendkim ${escapeShellArgs args}";
        PermissionsStartOnly = lib.mkForce false;
      };
    };

    systemd.tmpfiles.rules = [
      "d '${cfg.dkim.key-directory}' - ${config.services.opendkim.user} ${config.services.opendkim.group} - -"
    ];
  };
}