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

with lib;

let
  compileSieve = name: text: pkgs.runCommand name {} ''
    mkdir $out
    cp ${pkgs.writeText name ''
    ''} $out/${name}
    ${pkgs.dovecot_pigeonhole}/bin/sievec $out/${name}
  '';
in {
  config = {
    services.postfix = {
      enable = true;
      hostname = "surtr.yggdrasil.li";
      recipientDelimiter = "+";
      setSendmail = true;
      postmasterAlias = ""; rootAlias = ""; extraAliases = "";
      destination = [];
      sslCert = "/run/credentials/postfix.service/surtr.yggdrasil.li.pem";
      sslKey = "/run/credentials/postfix.service/surtr.yggdrasil.li.key.pem";
      networks = ["127.0.0.0/8" "[::ffff:127.0.0.0]/104" "[::1]/128" "10.141.0.0/16"];
      config = let
        relay_ccert = "texthash:${pkgs.writeText "relay_ccert" ""}";
      in {
        #the dh params
        smtpd_tls_dh1024_param_file = toString config.security.dhparams.params."postfix-1024".path;
        smtpd_tls_dh512_param_file = toString config.security.dhparams.params."postfix-512".path;
        #enable ECDH
        smtpd_tls_eecdh_grade = "strong";
        #enabled SSL protocols, don't allow SSLv2 and SSLv3
        smtpd_tls_protocols = ["!SSLv2" "!SSLv3" "!TLSv1" "!TLSv1.1" "!TLSv1.2"];
        smtpd_tls_mandatory_protocols = ["!SSLv2" "!SSLv3" "!TLSv1" "!TLSv1.1" "!TLSv1.2"];
        #allowed ciphers for smtpd_tls_security_level=encrypt
        smtpd_tls_mandatory_ciphers = "high";
        #allowed ciphers for smtpd_tls_security_level=may
        #smtpd_tls_ciphers = high
        #enforce the server cipher preference
        tls_preempt_cipherlist = true;
        #disable following ciphers for smtpd_tls_security_level=encrypt
        smtpd_tls_mandatory_exclude_ciphers = ["aNULL" "MD5" "DES" "ADH" "RC4" "PSD" "SRP" "3DES" "eNULL"];
        #disable following ciphers for smtpd_tls_security_level=may
        smtpd_tls_exclude_ciphers = ["aNULL" "MD5" "DES" "ADH" "RC4" "PSD" "SRP" "3DES" "eNULL"];
        #enable TLS logging to see the ciphers for inbound connections
        smtpd_tls_loglevel = "1";
        #enable TLS logging to see the ciphers for outbound connections
        smtp_tls_loglevel = "1";

        smtpd_tls_received_header = true;

        smtpd_tls_ask_ccert = true;
        smtpd_tls_CAfile = toString ./ca/ca.crt;

        smtp_tls_security_level = "dane";
        smtp_dns_support_level = "dnssec";

        tls_server_sni_maps = ''texthash:${pkgs.writeText "sni" ''
          bouncy.email /run/credentials/postfix.service/bouncy.email.full.pem
          mailin.bouncy.email /run/credentials/postfix.service/mailin.bouncy.email.full.pem
          mailsub.bouncy.email /run/credentials/postfix.service/mailsub.bouncy.email.full.pem
          .bouncy.email /run/credentials/postfix.service/bouncy.email.full.pem
        ''}'';

        local_recipient_maps = "";

        # 10 GiB
        message_size_limit = "10737418240";
        # 10 GiB
        mailbox_size_limit = "10737418240";

        smtpd_delay_reject = true;
        smtpd_helo_required = true;
        smtpd_helo_restrictions = "permit";

        smtpd_recipient_restrictions = [
          "reject_unauth_pipelining"
          "reject_non_fqdn_recipient"
          "reject_unknown_recipient_domain"
          "permit_mynetworks"
          "check_ccert_access ${relay_ccert}"
          "reject_non_fqdn_helo_hostname"
          "reject_invalid_helo_hostname"
          "reject_unauth_destination"
          "reject_unknown_recipient_domain"
          "reject_unverified_recipient"
        ];

        smtpd_relay_restrictions = [
          "permit_mynetworks"
          "check_ccert_access ${relay_ccert}"
          "reject_unauth_destination"
        ];

        propagate_unmatched_extensions = ["canonical" "virtual" "alias"];
        smtpd_authorized_verp_clients = "$authorized_verp_clients";
        authorized_verp_clients = "$mynetworks";

        milter_default_action = "accept";
        smtpd_milters = [config.services.opendkim.socket "local:/run/rspamd/rspamd-milter.sock"];
        non_smtpd_milters = [config.services.opendkim.socket "local:/run/rspamd/rspamd-milter.sock"];

        alias_maps = "";

        queue_run_delay = "10s";
        minimal_backoff_time = "1m";
        maximal_backoff_time = "10m";
        maximal_queue_lifetime = "100m";
        bounce_queue_lifetime = "20m";

        smtpd_discard_ehlo_keyword_address_maps = "cidr:${pkgs.writeText "esmtp_access" ''
          # Allow DSN requests from local subnet only
          192.168.0.0/16      silent-discard
          172.16.0.0/12       silent-discard
          10.0.0.0/8          silent-discard
          0.0.0.0/0           silent-discard, dsn
          fd00::/8            silent-discard
          ::/0                silent-discard, dsn
        ''}";

        sender_canonical_maps = "tcp:localhost:${toString config.services.postsrsd.forwardPort}";
        sender_canonical_classes = "envelope_sender";
        recipient_canonical_maps = "tcp:localhost:${toString config.services.postsrsd.reversePort}";
        recipient_canonical_classes = ["envelope_recipient" "header_recipient"];

        virtual_mailbox_domains = ''pgsql:${pkgs.writeText "virtual_mailbox_domains.cf" ''
          dbname = emails
          table = virtual_mailbox_domain
          select_field = domain
          where_field = domain
        ''}'';
        virtual_mailbox_maps = ''pgsql:${pkgs.writeText "virtual_mailbox_maps.cf" ''
          dbname = emails
          table = virtual_mailbox_mapping
          select_field = mailbox
          where_field = lookup
        ''}'';
      };
      masterConfig = {
        smtps = {
          type = "inet";
          private = false;
          command = "smtpd";
          args = [
            "-o" "smtpd_tls_wrappermode=yes"
            "-o" "smtpd_tls_ask_ccert=yes"
            "-o" "smtpd_tls_req_ccert=yes"
            "-o" "smtpd_client_restrictions=permit_tls_all_clientcerts,reject"
            "-o" "smtpd_relay_restrictions=permit_tls_all_clientcerts,reject"
            "-o" "smtpd_recipient_restrictions=reject_unauth_pipelining,reject_non_fqdn_recipient,reject_unknown_recipient_domain,permit_tls_all_clientcerts,reject"
            "-o" "milter_macro_daemon_name=surtr.yggdrasil.li"
          ];
        };
      };
    };

    services.postsrsd = {
      enable = true;
      domain = "surtr.yggdrasil.li";
      separator = "+";
      excludeDomains = [ "surtr.yggdrasil.li"
                         ".bouncy.email" "bouncy.email"
                       ];
    };

    services.opendkim = {
      enable = true;
      user = "postfix"; group = "postfix";
      socket = "local:/run/opendkim/opendkim.sock";
      domains = ''csl:${concatStringsSep "," ["surtr.yggdrasil.li" "bouncy.email"]}'';
      selector = "surtr";
      configFile = builtins.toFile "opendkim.conf" ''
        Syslog true
        MTA surtr.yggdrasil.li
        MTACommand ${config.security.wrapperDir}/sendmail
        LogResults true
      '';
    };

    services.rspamd = {
      enable = true;
      workers = {
        controller = {};
        external = {
          type = "rspamd_proxy";
          bindSockets = [
            { mode = "0660";
              socket = "/run/rspamd/rspamd-milter.sock";
              owner = config.services.rspamd.user;
              group = config.services.rspamd.group;
            }
          ];
          extraConfig = ''
            milter = yes;

            upstream "local" {
              default = yes;
              self_scan = yes;
            }
          '';
        };
      };
      locals = {
        "milter_headers.conf".text = ''
          use = ["authentication-results", "x-spamd-result", "x-rspamd-queue-id", "x-rspamd-server", "x-spam-level", "x-spam-status"];
          extended_headers_rcpt = [];
        '';
        "actions.conf".text = ''
          reject = 15;
          add_header = 10;
          greylist = 5;
        '';
        "groups.conf".text = ''
          symbols {
            "BAYES_SPAM" {
              weight = 2.0;
            }
          }
        '';
        "dmarc.conf".text = ''
          reporting = true;
          send_reports = true;
          report_settings {
            org_name = "Yggdrasil.li";
            domain = "yggdrasil.li";
            email = "postmaster@yggdrasil.li";
          }
        '';
        "redis.conf".text = ''
          servers = "${config.services.redis.servers.rspamd.unixSocket}";
        '';
        "dkim_signing.conf".text = "enabled = false;";
        "neural.conf".text = "enabled = false;";
        "classifier-bayes.conf".text = ''
          enable = true;
          expire = 8640000;
          new_schema = true;
          backend = "redis";
          per_user = true;
          min_learns = 0;

          autolearn = [0, 10];

          statfile {
              symbol = "BAYES_HAM";
              spam = false;
          }
          statfile {
              symbol = "BAYES_SPAM";
              spam = true;
          }
        '';
        # "redirectors.inc".text = ''
        #   visit.creeper.host
        # '';
      };
    };

    users.groups.${config.services.rspamd.group}.members = [ config.services.postfix.user ];

    services.redis.servers.rspamd.enable = true;

    users.groups.${config.services.redis.servers.rspamd.user}.members = [ config.services.rspamd.user ];

    services.dovecot2 = {
      enable = true;
      sslServerCert = "/run/credentials/dovecot2.service/surtr.yggdrasil.li.pem";
      sslServerKey = "/run/credentials/dovecot2.service/surtr.yggdrasil.li.key.pem";
      sslCACert = toString ./ca/ca.crt;
      mailLocation = "maildir:/var/lib/mail/%u/maildir:UTF-8";
      modules = with pkgs; [ dovecot_pigeonhole ];
      protocols = [ "lmtp" "sieve" ];
      extraConfig = ''
        mail_home = /var/lib/mail/%u

        local_name imap.bouncy.email {
          ssl_cert = <$/run/credentials/dovecot2.service/imap.bouncy.email.pem
          ssl_key = <$/run/credentials/dovecot2.service/imap.bouncy.email.key.pem
        }
        local_name bouncy.email {
          ssl_cert = <$/run/credentials/dovecot2.service/bouncy.email.pem
          ssl_key = <$/run/credentials/dovecot2.service/bouncy.email.key.pem
        }

        ssl_require_crl = yes
        ssl_verify_client_cert = yes
        auth_ssl_username_from_cert = yes
        auth_mechanisms = external

        userdb sql {
          args = ${pkgs.writeText "dovecot-sql.conf" ''
            driver = pgsql
            connect = host=localhost dbname=email
            user_query = SELECT mailbox AS user, quota_rule FROM mailbox WHERE mailbox = '%u'
          ''}
          default_fields = uid=dovecot2 gid=dovecot2
        }

        mail_plugins = $mail_plugins quota
        mailbox_list_index = yes
        postmaster_address = postmaster@yggdrasil.li
        recipient_delimiter = +

        service lmtp {
          vsz_limit = 1G

          unix_listener /run/postfix/dovecot-lmtp {
            mode = 0600
            user = postfix
            group = postfix
          }
        }

        namespace inbox {
          separator = /
          inbox = yes
          prefix = 
        }

        plugin {
          quota = maildir
          quota_rule = *:storage=1GB
          quota_rule2 = Trash:storage=+10%%
          quota_status_overquota = "552 5.2.2 Mailbox is full"
          quota_status_success = DUNNO
          quota_status_nouser = DUNNO
          quota_grace = 10%%
        }

        protocol imap {
          mail_max_userip_connections = 50
          mail_plugins = $mail_plugins imap_quota imap_sieve
        }

        service imap-login {
          inet_listener imap {
            port = 0
          }
        }

        service managesieve-login {
          inet_listener sieve {
            port = 4190
            ssl = yes
          }
        }

        plugin {
          sieve_plugins = sieve_imapsieve

          sieve_redirect_envelope_from = orig_recipient
          sieve_before = /etc/dovecot/sieve_before.d
        }
      '';
    };

    environment.etc."dovecot/sieve_before.d/tag-junk.sieve".text = ''
      require ["imap4flags"];

      if header :contains "X-Spam-Flag" "YES" {
        addflag ["\\Junk"];
      }
    '';

    security.dhparams = {
      params = {
        "postfix-512".bits = 512;
        "postfix-1024".bits = 2048;
      };
    };

    security.acme.domains = {
      "bouncy.email" = {};
      "mailin.bouncy.email" = {};
      "mailsub.bouncy.email" = {};
      "imap.bouncy.email" = {};
      "surtr.yggdrasil.li" = {};
    };

    systemd.services.postfix = {
      serviceConfig.LoadCredential = [
        "surtr.yggdrasil.li.key.pem:${config.security.acme.certs."surtr.yggdrasil.li".directory}/key.pem"
        "surtr.yggdrasil.li.pem:${config.security.acme.certs."surtr.yggdrasil.li".directory}/fullchain.pem"
        "bouncy.email.full.pem:${config.security.acme.certs."bouncy.email".directory}/full.pem"
        "mailin.bouncy.email.full.pem:${config.security.acme.certs."mailin.bouncy.email".directory}/full.pem"
        "mailsub.bouncy.email.full.pem:${config.security.acme.certs."mailsub.bouncy.email".directory}/full.pem"
      ];
    };

    systemd.services.dovecot2 = {
      preStart = ''
        for f in /etc/dovecot/sieve_before.d/*.sieve; do
          ${pkgs.dovecot_pigeonhole}/bin/sievec $f
        done
      '';
      
      serviceConfig = {
        LoadCredential = [
          "surtr.yggdrasil.li.key.pem:${config.security.acme.certs."surtr.yggdrasil.li".directory}/key.pem"
          "surtr.yggdrasil.li.pem:${config.security.acme.certs."surtr.yggdrasil.li".directory}/fullchain.pem"
          "bouncy.email.key.pem:${config.security.acme.certs."bouncy.email".directory}/key.pem"
          "bouncy.email.pem:${config.security.acme.certs."bouncy.email".directory}/fullchain.pem"
          "imap.bouncy.email.key.pem:${config.security.acme.certs."imap.bouncy.email".directory}/key.pem"
          "imap.bouncy.email.pem:${config.security.acme.certs."imap.bouncy.email".directory}/fullchain.pem"
        ];

        StateDirectory = "mail";
      };
    };
  };
}