{ lib, config, ... }:
let
  inherit (config) networking users;
  inherit (config.services) postfix;
in
{
  imports = [
    postfix/autogeree.net.nix
    postfix/sourcephile.fr.nix
  ];
  users.groups.acme.members = [ postfix.user ];
  networking.nftables.ruleset = ''
    table inet filter {
      chain input-net {
        tcp dport { smtp, submissions } counter accept comment "postfix: SMTP"
      }
      chain output-net {
        skuid ${postfix.user} tcp dport smtp counter accept comment "postfix: SMTP"
      }
    }
  '';
  services.postfix = {
    enable = true;
    networksStyle = "host";
    hostname = "${networking.hostName}.${networking.domain}";
    domain = networking.domain;
    origin = "$myhostname";
    destination = [
      "localhost"
      "localhost.localdomain"
      "$myhostname"
    ];
    postmasterAlias = "root";
    rootAlias = "root@${networking.domain}";
    sslKey = "/var/lib/acme/${networking.domain}/key.pem";
    sslCert = "/var/lib/acme/${networking.domain}/fullchain.pem";
    networks = [
      "127.0.0.0/8"
      "[::1]/128"
    ];
    setSendmail = true;
    # Parse the extension in email address, eg. contact+extension@
    recipientDelimiter = "+";
    #mapFiles.sender_access = postfix/sender_access.clear;
    #mapFiles.virtual_mailbox_maps = ;
    config = {
      debug_peer_level = "4";
      debug_peer_list = [
        #"chomsky.autogeree.net"
        #"localhost"
        #"mail.sourcephile.fr"
      ];

      #
      # Sending to the world
      #
      # Appending .domain is the MUA's job
      append_dot_mydomain = false;
      smtp_body_checks = "";
      #smtp_cname_overrides_servername = false;
      smtp_connect_timeout = "60s";
      #smtp_header_checks = "regexp:/var/lib/postfix/smtp_header_checks";
      smtp_mime_header_checks = "";
      smtp_nested_header_checks = "";
      smtp_tls_exclude_ciphers = [ "ADH" "MD5" "CAMELLIA" "SEED" "3DES" "DES" "RC4" "eNULL" "aNULL" ];
      #smtp_tls_fingerprint_digest = "sha1";
      smtp_tls_loglevel = "1";
      #smtp_tls_note_starttls_offer = true;
      #smtp_tls_policy_maps = "hash:/var/lib/postfix/conf/tls_policy";
      # Only allow TLSv* protocols
      smtp_tls_protocols = [ "!SSLv2" "!SSLv3" ];
      #smtp_tls_scert_verifydepth = "5";
      #smtp_tls_secure_cert_match = [ "nexthop" "dot-nexthop" ];
      smtp_tls_security_level = "may";
      smtp_tls_session_cache_database = "btree:$data_directory/smtp_tls_session_cache";
      #smtp_tls_session_cache_timeout = "3600s";
      #smtp_tls_verify_cert_match = "hostname";

      #
      # Receiving from the world
      #
      message_size_limit = "20480000";
      maximal_queue_lifetime = "5d";
      default_extra_recipient_limit = "5000";
      line_length_limit = "2048";
      duplicate_filter_limit = "5000";
      # Stops mail from poorly written software
      strict_rfc821_envelopes = true;
      mime_header_checks = [ ];
      milter_header_checks = [ ];
      nested_header_checks = [ ];
      body_checks = [ ];
      content_filter = "";
      permit_mx_backup_networks = [ ];
      propagate_unmatched_extensions = [ "canonical" "virtual" "alias" ];
      #masquerade_classes = [ "envelope_sender" "header_sender" "header_recipient" ];
      #masquerade_domains = "";
      #masquerade_exceptions = "root";
      queue_minfree = "0";
      # Stops some techniques used to harvest email addresses
      disable_vrfy_command = true;
      enable_long_queue_ids = false;
      # Useful to test restrictions
      smtpd_authorized_xclient_hosts = "127.0.0.1";
      smtpd_banner = "$myhostname ESMTP $mail_name (NixOS)";
      smtpd_client_connection_count_limit = "50";
      smtpd_client_connection_rate_limit = "0";
      smtpd_client_event_limit_exceptions = "$mynetworks";
      smtpd_client_message_rate_limit = "0";
      smtpd_client_new_tls_session_rate_limit = "0";
      smtpd_client_port_logging = false;
      smtpd_client_recipient_rate_limit = "0";
      # Ban 5 sec on error
      smtpd_error_sleep_time = "5";
      # Needed to enforce reject_unknown_helo_hostname
      smtpd_helo_required = true;
      smtpd_helo_restrictions = [
        "reject_invalid_helo_hostname"
        "reject_non_fqdn_helo_hostname"
        # Don't talk to mail systems that don't know their own hostname.
        "reject_unknown_helo_hostname"
        "permit"
      ];
      smtpd_client_restrictions = [
      ];
      # Set in postfix/*.nix and used in submissions/smptd
      # with reject_sender_login_mismatch
      smtpd_sender_login_maps = [ ];
      smtpd_sender_restrictions = [
        "reject_non_fqdn_sender"
        #"check_sender_access hash:/etc/postfix/sender_access"
        "permit"
      ];
      smtpd_reject_unlisted_recipient = true;
      # Check the RCPT TO, before smtpd_recipient_restrictions
      # Restrictions based on what is allowed or not,
      # these are applied before smtpd_recipient_restrictions
      smtpd_relay_restrictions = [
        "permit_mynetworks"
        # Check the recipient's address in virtual_mailbox_domains and virtual_mailbox_maps
        "permit_auth_destination"
        # The world is only authorized to use our relay for the above destinations.
        "reject"
      ];
      # Restrictions based on what is working or not
      smtpd_recipient_restrictions = [
        # Reject if the domain is not fully qualified
        "reject_non_fqdn_recipient"
        # Reject if the domain is not working, even before bothering to check the address
        "reject_unknown_recipient_domain"
        # Reject if the address is not working
        # WARNING: this does not work if the recipient is greylisting.
        # WARNING: verify(8) has a cache, dumpable if verify(8) is stopped, with:
        # postmap -s btree:/var/lib/postfix/data/verify_cache
        #"reject_unverified_recipient"
        "permit"
      ];
      # Trust the verify database
      #unverified_recipient_reject_code = "550";
      smtpd_data_restrictions = [
        # Force the smtpd's client to wait OK before sending
        "reject_unauth_pipelining"
        "permit"
      ];
      smtpd_end_of_data_restrictions = [
        # Enforce mail volume quota via policy service callouts.
        #check_policy_service unix:private/policy
      ];
      #smtpd_milters = "";
      smtpd_peername_lookup = true;
      smtpd_recipient_limit = "5000";
      smtpd_recipient_overshoot_limit = "5000";
      #smtpd_restriction_classes = "";
      #smtpd_sasl_auth_enable = true;
      #smtpd_sasl_path = "private/auth";
      #smtpd_sasl_security_options = "noanonymous";
      #smtpd_sasl_type = "dovecot";
      smtpd_starttls_timeout = "300s";
      #smtpd_tls_always_issue_session_ids = true;
      #smtpd_tls_CApath = "/etc/postfix/x509/ca/";
      smtpd_tls_ask_ccert = false;
      #smtpd_tls_ccert_verifydepth = "5";
      smtpd_tls_ciphers = "high";
      smtpd_tls_eecdh_grade = "auto";
      # Disable weak ciphers as reported by https://ssl-tools.net
      # https://serverfault.com/questions/744168/how-to-disable-rc4-on-postfix
      smtpd_tls_exclude_ciphers = [ "ADH" "MD5" "CAMELLIA" "SEED" "3DES" "DES" "RC4" "eNULL" "aNULL" ];
      smtpd_tls_fingerprint_digest = "sha512";
      # Log only a summary message on TLS handshake completion
      smtpd_tls_loglevel = "1";
      smtpd_tls_mandatory_ciphers = "high";
      smtpd_tls_mandatory_protocols = [ "!SSLv2" "!SSLv3" ];
      # Only allow TLSv*
      smtpd_tls_protocols = [ "!SSLv2" "!SSLv3" ];
      #smtpd_tls_received_header = false;
      smtpd_tls_req_ccert = false;
      # Postfix 2.3 and later
      # encrypt
      #  Mandatory TLS encryption: announce STARTTLS support to SMTP clients, and require that clients use TLS
      #  encryption. According to [1720]RFC 2487 this MUST NOT be applied in case of a publicly-referenced
      #  SMTP server. Instead, this option should be used only on dedicated servers.
      smtpd_tls_security_level = "may";
      smtpd_tls_session_cache_database = "btree:$data_directory/smtpd_tls_session_cache";
      #smtpd_tls_session_cache_timeout = "3600s";
      #smtpd_tls_chain_files =

      relayhost = [ ];
      #relay_clientcerts = hash:/var/lib/postfix/conf/relay_clientcerts
      # This is where to put backup MX domains
      relay_domains = [ ];
      relay_recipient_maps = [ ];

      # Use a non blocking source of randomness
      tls_random_source = "dev:/dev/urandom";
      # Map each domain to a specific X.509 certificate
      tls_server_sni_maps = "hash:/run/keys/postfix-sni";

      # Only explicitely aliased accounts have a mail, not all the passwd
      #local_recipient_maps = "$alias_maps";
      # Note that the local transport rewrites the envelope recipient
      # according to the alias_maps, and thus the aliasing is transparent
      # to the nexthop (eg. dovecot)
      #local_transport = local:$myhostname
      # No console bell on new mail
      biff = false;
      forward_path = [
        /*
      "$home/.forward''${recipient_delimiter}''${extension}"
        */
        "$home/.forward"
      ];

      # Filled by the postfix/*.nix
      virtual_mailbox_domains = [ ];
      # Completed by the postfix/*.nix
      virtual_mailbox_maps = [
        # Is it necessary because it's already in virtual_alias_maps
        "hash:/etc/postfix/virtual"
      ];
      virtual_transport = "lmtp:unix:private/dovecot-lmtp";
      /*
        dovecot_destination_recipient_limit = "1";
        virtual_transport = "dovecot";
      */

      # There is no fallback
      fallback_transport = "";
    };
    virtualMapType = "hash";
    masterConfig =
      let
        mkVal = value:
          if lib.isList value
          then lib.concatStringsSep "," value
          else
            if value == true then "yes"
            else if value == false then "no"
            else toString value;
        mkKeyVal = opt: val: [ "-o" (opt + "=" + mkVal val) ];
        mkArgs = args: lib.concatLists (lib.mapAttrsToList mkKeyVal args);
      in
      {
        pickup = {
          args = mkArgs {
            cleanup_service_name = "submissions-header-cleanup";
          };
        };
        # Implicit TLS on port 465
        # https://tools.ietf.org/html/rfc8314#section-3.3
        submissions = {
          type = "inet";
          private = false;
          command = "smtpd";
          args = mkArgs {
            syslog_name = "postfix/submissions";
            # Implicit TLS, not STARTTLS
            smtpd_tls_wrappermode = true;
            smtpd_tls_mandatory_protocols = [
              "TLSv1.3"
              # FIXME: to be removed when K-9 Mail will support TLSv1.3,
              # K-9 Mail 5.600 does not.
              "TLSv1.2"
            ];
            milter_macro_daemon_name = "ORIGINATING";
            smtpd_helo_restrictions = [
              "permit_sasl_authenticated"
            ] ++ postfix.config.smtpd_helo_restrictions;
            smtpd_relay_restrictions = [
              # SASL authorizes to send to the world
              "permit_sasl_authenticated"
              "reject"
            ];
            smtpd_sasl_auth_enable = true;
            smtpd_sasl_type = "dovecot";
            smtpd_sasl_path = "private/auth";
            smtpd_sasl_local_domain = "";
            # Offer SASL authentication only after a TLS-encrypted session has been established
            smtpd_tls_auth_only = true;
            smtpd_sasl_tls_security_options = [ "noanonymous" ];
            # Do not put SASL logins in mail headers
            smtpd_sasl_authenticated_header = false;
            # Who cares about (old) Outlook
            broken_sasl_auth_clients = false;
            smtpd_sender_restrictions = [
              "reject_non_fqdn_sender"
              # Check that the SASL user is using only its own
              # mail addresses on the envelope, as indicated in smtpd_sender_login_maps
              "reject_sender_login_mismatch"
              "permit"
            ];
            # No X.509 certificates for users, for now
            smtpd_tls_ask_ccert = false;
            smtpd_tls_ccert_verifydepth = 0;
            smtpd_tls_loglevel = 1;
            smtpd_tls_req_ccert = false;
            cleanup_service_name = "submissions-header-cleanup";
          };
        };
      };
    extraMasterConf = ''
      #spfcheck    unix  -        n       n       -        0        spawn
      #  user=policyd-spf argv=/usr/sbin/postfix-policyd-spf-perl
      # -o smtpd_sender_restrictions=reject_sender_login_mismatch
      # -o smtpd_sender_login_maps=hash:/etc/postfix/vaccounts
      # -o cleanup_service_name=submissions-header-cleanup
      #spfcheck  unix  -       n       n       -       0       spawn
      #  user=policyd-spf argv=/usr/bin/postfix-policyd-spf-perl
      #uucp      unix  -       n       n       -       -       pipe
      #  flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)
      #smtp      inet  n       -       -       -       -       smtpd
      #  -o cleanup_service_name=pre-cleanup
      #  -o content_filter=amavis:[127.0.0.1]:10024
      #  -o smtpd_sender_restrictions=reject_unauth_pipelining,reject_non_fqdn_sender,permit
      #  -o receive_override_options=no_address_mappings
      #amavis    unix  -       -       n        -      2       lmtp
      #  -o lmtp_data_done_timeout=1200
      #  -o lmtp_send_xforward_command=yes
      #  -o lmtp_tls_note_starttls_offer=no
      #127.0.0.1:10025 inet n  -       n       -       -       smtpd
      #  -o content_filter=
      #  -o local_header_rewrite_clients=
      #  -o local_recipient_maps=
      #  -o mynetworks=127.0.0.0/8
      #  -o receive_override_options=no_header_body_checks,no_milters,no_unknown_recipient_checks
      #  -o relay_recipient_maps=
      #  -o smtpd_client_connection_count_limit=0
      #  -o smtpd_client_connection_rate_limit=0
      #  -o smtpd_client_restrictions=permit_mynetworks,reject
      #  -o smtpd_data_restrictions=reject_unauth_pipelining
      #  -o smtpd_delay_reject=no
      #  -o smtpd_end_of_data_restrictions=
      #  -o smtpd_error_sleep_time=0
      #  -o smtpd_hard_error_limit=1000
      #  -o smtpd_helo_restrictions=
      #  -o smtpd_milters=
      #  -o smtpd_recipient_restrictions=permit_mynetworks,reject
      #  -o smtpd_restriction_classes=
      #  -o smtpd_sender_restrictions=
      #  -o smtpd_soft_error_limit=1001
      #  -o strict_rfc821_envelopes=yes
      #submission inet n       -       -       -       -       smtpd
      #  -o cleanup_service_name=pre-cleanup
      #  -o content_filter=amavis:[127.0.0.1]:10024
      #  -o milter_macro_daemon_name=ORIGINATING
      #  -o receive_override_options=no_address_mappings
      #  -o smtpd_sender_restrictions=permit_tls_clientcerts,reject
      #  -o smtpd_tls_ask_ccert=yes
      #  -o smtpd_tls_auth_only=yes
      #  -o smtpd_tls_ccert_verifydepth=2
      #  -o smtpd_tls_loglevel=1
      #  -o smtpd_tls_req_ccert=yes
      #  -o smtpd_tls_security_level=encrypt
      #smtps     inet  n       -       -       -       -       smtpd
      #  -o milter_macro_daemon_name=ORIGINATING
      #  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
      #  -o smtpd_sasl_auth_enable=yes
      #  -o smtpd_tls_ask_ccert=yes
      #  -o smtpd_tls_auth_only=yes
      #  -o smtpd_tls_ccert_verifydepth=0
      #  -o smtpd_tls_loglevel=1
      #  -o smtpd_tls_req_ccert=no
      #  -o smtpd_tls_security_level=encrypt
      #  -o smtpd_tls_wrappermode=yes
      #pickup    fifo  n       -       -       60      1       pickup
      #  -o cleanup_service_name=pre-cleanup
      #  -o content_filter=amavis:[127.0.0.1]:10024
      #pre-cleanup unix n      -       -       -       0       cleanup
      #  -o virtual_alias_maps=
      #cleanup   unix  n       -       -       -       0       cleanup
      #  -o mime_header_checks=
      #  -o nested_header_checks=
      #  -o body_checks=
      #  -o header_checks=
      #-- SYMPA begin
      #sympa unix - n n - - pipe
      #  flags=R user=sympa argv=/usr/lib/sympa/bin/queue ''${recipient}
      #sympabounce unix - n n - - pipe
      #  flags=R user=sympa argv=/usr/lib/sympa/bin/bouncequeue ''${recipient}
      #-- SYMPA end
    '';
    #noclue    unix  -       n       n       -       -       pipe
    #  flags=q user=noclue argv=/usr/local/bin/noclue-delivery ${recipient} ${sender}
  };
}