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

let
  mailSecret = key: {
    sopsFile = ../secrets/mailserver.yaml;
    inherit key;
    owner = "nginx";
    group = "nginx";
    mode = "0440";
    restartUnits = [
      "phpfpm-roundcube.service"
      "dovecot.service"
    ];
  };

  clientIdSecret = config.sops.secrets."mailserver/oauth/client-id".path;
  clientSecret = config.sops.secrets."mailserver/oauth/client-secret".path;
in
{
  sops.secrets."mailserver/oauth/client-id" = mailSecret "mailserver/oauth/client-id";

  sops.secrets."mailserver/oauth/client-secret" = mailSecret "mailserver/oauth/client-secret";

  imports = [
    (builtins.fetchTarball {
      url = "https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/archive/51726d7b7fd94aa69829fd42749a803914cbf3b7/nixos-mailserver-51726d7b7fd94aa69829fd42749a803914cbf3b7.tar.gz";
      sha256 = "0449ssnpwbgfv0cyddzb8s6g641gy5xh1hd3shg32x8pv0a5qy3a";
    })
  ];

  security.acme.acceptTerms = false;

  services.roundcube = {
    enable = true;
    hostName = "mail.mizuki.guru";

    package = pkgs.roundcube.withPlugins (
      plugins: with plugins; [
        persistent_login
      ]
    );

    plugins = [
      "persistent_login"
      "managesieve"
    ];

    dicts = with pkgs.aspellDicts; [
      en
    ];

    maxAttachmentSize = config.mailserver.messageSizeLimit / 1024 / 1024;

    extraConfig = ''
      $_SERVER['HTTPS'] = 'on';
      $_SERVER['SERVER_PORT'] = 443;
      $_SERVER['HTTP_X_FORWARDED_PORT'] = 443;
      $_SERVER['HTTP_X_FORWARDED_PROTO'] = 'https';
      $_SERVER['HTTP_HOST'] = 'mail.mizuki.guru';

      $config['proxy_ips'] = '127.0.0.1';

      $config['imap_host'] = "ssl://${config.mailserver.fqdn}";
      $config['smtp_host'] = "ssl://${config.mailserver.fqdn}";
      $config['smtp_user'] = "%u";
      $config['smtp_pass'] = "%p";

      $config['managesieve_host'] = "tls://${config.mailserver.fqdn}";
      $config['managesieve_port'] = 4190;
      $config['managesieve_usetls'] = true;

      $config['oauth_provider'] = 'generic';
      $config['oauth_provider_name'] = 'authentik';

      $config['oauth_client_id'] =
        trim(file_get_contents('${clientIdSecret}'));

      $config['oauth_client_secret'] =
        trim(file_get_contents('${clientSecret}'));

      $config['oauth_auth_uri'] =
        'https://auth.mizuki.guru/application/o/authorize/';

      $config['oauth_token_uri'] =
        'https://auth.mizuki.guru/application/o/token/';

      $config['oauth_identity_uri'] =
        'https://auth.mizuki.guru/application/o/userinfo/';

      $config['oauth_scope'] =
        "email openid dovecotprofile offline_access";

      $config['oauth_auth_parameters'] = [];
      $config['oauth_identity_fields'] = ['email'];
    '';
  };

  services.nginx.virtualHosts.${config.services.roundcube.hostName} = {
    forceSSL = false;
    enableACME = false;

    listen = [
      {
        addr = "127.0.0.1";
        port = 5656;
      }
    ];

    extraConfig = ''
      set_real_ip_from 127.0.0.1;
      real_ip_header X-Forwarded-For;
      real_ip_recursive on;
    '';
  };

  services.caddy = {
    enable = true;

    virtualHosts."mail.mizuki.guru" = {
      extraConfig = ''
        reverse_proxy localhost:5656
      '';
    };
  };

  services.dovecot2.settings = {
    auth_mechanisms = lib.mkForce [
      "plain"
      "login"
      "xoauth2"
      "oauthbearer"
    ];

    oauth2_tokeninfo_url = "https://auth.mizuki.guru/application/o/userinfo/?access_token=";

    # 주의:
    # 여기는 아직 secret '값'이 아니라 secret '경로'가 들어감.
    # Dovecot이 URL 안에서 file_get_contents 같은 처리를 안 하므로
    # 이 방식은 제대로 동작하지 않을 가능성이 큼.
    oauth2_introspection_url = "https://auth.mizuki.guru/application/o/introspect/";

    oauth2_introspection_mode = "post";
    oauth2_force_introspection = "yes";
    oauth2_active_attribute = "active";
    oauth2_active_value = "true";
    oauth2_username_attribute = "email";
  };

  users.users.dovecot = {
    isSystemUser = true;
    group = "dovecot";
    extraGroups = [ "caddy" ];
  };

  users.groups.dovecot = { };

  users.users.postfix.extraGroups = [ "caddy" ];

  mailserver = {
    enable = true;
    stateVersion = 4;
    fqdn = "mail.mizuki.guru";
    domains = [ "mizuki.guru" ];

    x509 = {
      certificateFile = "/var/lib/caddy/.local/share/caddy/certificates/acme-v02.api.letsencrypt.org-directory/mail.mizuki.guru/mail.mizuki.guru.crt";

      privateKeyFile = "/var/lib/caddy/.local/share/caddy/certificates/acme-v02.api.letsencrypt.org-directory/mail.mizuki.guru/mail.mizuki.guru.key";
    };

    ldap = {
      enable = true;
      uris = [ "ldap://10.11.8.102" ];

      bind = {
        dn = "cn=mailserver,ou=users,dc=ldap,dc=goauthentik,dc=io";
        passwordFile = "/var/lib/secrets/authentik-ldap-bind-pw";
      };

      base = "ou=users,dc=ldap,dc=goauthentik,dc=io";
      scope = "sub";

      attributes = {
        uuid = "uid";
        username = "mail";
        mail = "mail";
      };

      dovecot = {
        passFilter = "mail=%{user}\n  bind = yes";
      };
    };

    accounts = { };
  };
}
