{ config, pkgs, lib, ... }:
{
  # Use NitroKey USB smartcard with SSH.
  # https://nixos.wiki/wiki/Nitrokey

  # Test suite. TODO: Check all of this after config changes!
  # - pinentry should be in $PATH.
  #   Note: Since NixOS 23.11 (and maybe earlier) this is no longer the case.
  #   Yet, all other tests still work!
  #     echo GETPIN | pinentry
  # - smartcard should be listed
  #     gpg --card-status
  # - encryption should work (graphical pinentry should pop-up)
  #     gpg -d ./passphrase.txt.gpg
  # - ssh should work (graphical pinentry should pop-up)
  #     ssh yodaNas
  # - signed git commits should work in IntelliJ
  #     IntelliJ IDE -> Make a Git commit -> Graphical pinentry should pop-up

  # Restart gpg-agent after config change.
  # Otherwise there might be a gpg error about "no pinentry".
  # https://discourse.nixos.org/t/cant-get-gnupg-to-work-no-pinentry/15373/19
  #
  # But how to restart it?
  # https://superuser.com/a/1150399
  # gpgconf --kill gpg-agent
  # killall gpg-agent
  # systemctl --user restart gpg-agent

  # Not sure if this is needed: Reload udev rules.
  # sudo -- udevadm control --reload-rules && udevadm trigger

  # TODO: gpg-agent pinentry problem
  # https://github.com/NixOS/nixpkgs/issues/97861
  #
  # gpgconf --check-programs
  #=> gpgconf: error running '/nix/store/lvsbmqy4dmlri22145hbr6799hgbnpnf-gnupg-2.4.0/bin/pinentry': probably not installed
  #
  # ssh -v nas
  #=> OpenSSH_9.3p2, OpenSSL 3.0.10 1 Aug 2023
  #=> debug1: Reading configuration data /home/yoda/.ssh/config
  #=> debug1: /home/yoda/.ssh/config line 67: Applying options for nas
  #=> debug1: /home/yoda/.ssh/config line 180: Applying options for *
  #=> debug1: Reading configuration data /etc/ssh/ssh_config
  #=> debug1: Executing command: '/nix/store/8fv91097mbh5049i9rglc73dx6kjg3qk-bash-5.2-p15/bin/bash -c '/nix/store/lvsbmqy4dmlri22145hbr6799hgbnpnf-gnupg-2.4.0/bin/gpg-connect-agent --quiet updatestartuptty /bye >/dev/null 2>&1''
  #
  #=> USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
  #=> yoda        2752  0.0  0.0 444812  3040 ?        SLsl 16:09   0:00 /nix/store/lvsbmqy4dmlri22145hbr6799hgbnpnf-gnupg-2.4.0/bin/gpg-agent --supervised --pinentry-program /nix/store/8cvidvpwnwyxixlhqfaa5jlfndh2vir5-pinentry-1.2.1-curses/bin/pinentry

  # NITROKEY SSH WORKAROUND (I): Do all of this in one shell!
  # CREDITS: https://unix.stackexchange.com/a/250045/315162
  #
  # BEFORE: SSH_AUTH_SOCK=/run/user/1000/keyring/ssh
  # AFTER:  SSH_AUTH_SOCK=/run/user/1000/gnupg/S.gpg-agent.ssh
  #
  # systemctl --user stop gpg-agent
  # systemctl --user stop gpg-agent.socket
  # systemctl --user stop gpg-agent-ssh.socket
  # ps -aux | grep -v grep | grep gpg-agent
  # => NONE
  # eval $(gpg-agent --daemon --pinentry-program /nix/store/5j87jnmfh19xlq9ij0v3rh7cwssr4586-pinentry-1.2.1-curses/bin/pinentry --enable-ssh-support --sh)
  # echo $SSH_AUTH_SOCK
  #=> /run/user/1000/gnupg/S.gpg-agent.ssh
  # gpg -d ./passphrase.txt.gpg
  #=> Works!
  # ssh nas
  #=> Works!

  # NITROKEY SSH WORKAROUND (II)
  #
  # export SSH_AUTH_SOCK=/run/user/1000/gnupg/S.gpg-agent.ssh
  # ssh nas
  #=> Works!

  # TODO:
  #  What is the difference between programs.gnupg.agent.enableSSHSupport and
  #  services.gpg-agent.enableSshSupport = true;

  services.udev.packages = [ pkgs.nitrokey-udev-rules ];
  programs = {
    ssh.startAgent = false;
    gnupg.agent = {
      enable = true;
      # Sets SSH_AUTH_SOCK environment variable.
      enableSSHSupport = true;
      #pinentryFlavor = "curses";
      pinentryFlavor = "gnome3";
    };
  };

  # GNOME Keyring: Disable SSH agent.
  #
  # Without this,
  #   export SSH_AUTH_SOCK=/run/user/1000/gnupg/S.gpg-agent.ssh
  # is required before ssh can use the smartcard (through gpg-agent).
  #
  # GNOME Keyring will override the SSH_AUTH_SOCK variable
  # if it starts its own SSH agent. The docs suggest to disable
  # SSH agent support in GNOME Keyring if using another SSH agent:
  # https://wiki.gnome.org/Projects/GnomeKeyring/Ssh
  #
  # Here are related issues:
  # https://github.com/NixOS/nixpkgs/issues/8356
  # https://github.com/NixOS/nixpkgs/issues/42291
  # https://wiki.archlinux.org/title/GnuPG#GNOME_on_Wayland_overrides_SSH_agent_socket
  #
  # Solution 1: https://github.com/NixOS/nixpkgs/issues/42291#issuecomment-399630199
  #   Works for me.
  # Solution 2: https://github.com/NixOS/nixpkgs/issues/42291#issuecomment-687979733
  #   Works for me, but on each login, nextcloud-desktop asks for credentials ...

  # Adds the pinentry binary to the PATH so that e.g.
  #   echo GETPIN | pinentry
  # works.
  # This is not required for
  # - gpg --card-status
  # - ssh nas
  # - signed git commits in IntelliJ
  #environment.systemPackages = with pkgs; [
  #  #pinentry-curses
  #  pinentry-gnome
  #];

  #users.users.yoda = {
  #  packages = with pkgs; [
  #    #pinentry-curses
  #    pinentry-gnome
  #  ];
  #};

  # https://docs.nitrokey.com/nitrokey3/windows/troubleshooting.html#gnupg-openpgp-card-not-available
  #
  # There are two common smartcard services on Linux systems
  # - scdaemon (gpg) with two drivers:
  #   - ccid: directly accesses smartcard
  #   - pcsc: uses the pcscd daemon to access smartcard
  # - pcscd (generic smartcard daemon)
  #
  # `pcscd` might lock the card before `scdaemon` tries to access it
  # using the internal `ccid` driver
  #
  # Either uninstall `pcscd` **or**
  # use the `pcscd` driver for `scdaemon`
  # by adding `disable-ccid` to `~/.gnupg/scdaemon.conf`

  # Smartcard daemon.
  services.pcscd.enable = true;

  home-manager.users.yoda = { osConfig, config, pkgs, ... }: {

    # Disable GNOME Keyring. See comment above.
    #
    # Prevent clobbering SSH_AUTH_SOCK
    home.sessionVariables.GSM_SKIP_SSH_AGENT_WORKAROUND = "1";
    # Disable gnome-keyring ssh-agent
    xdg.configFile."autostart/gnome-keyring-ssh.desktop".text = ''
      ${lib.fileContents "${pkgs.gnome3.gnome-keyring}/etc/xdg/autostart/gnome-keyring-ssh.desktop"}
      Hidden=true
    '';

    # GnuPG configuration.
    programs.gpg = {
      enable = true;
      scdaemonSettings = {
        disable-ccid = true;
      };
      publicKeys = [{
        source = "${../assets/gpg/pubkey_nitrokey.asc}";
        # ultimate
        trust = 5;
      }];

      # Examples:
      #   https://github.com/ioerror/duraconf
      #   https://gist.github.com/graffen/37eaa2332ee7e584bfda
      settings = {
        # Display long key IDs
        keyid-format = "0xlong";

        # List all keys (or the specified ones) along with their fingerprints
        with-fingerprint = true;

        # Display the calculated validity of user IDs during key listings.
        list-options = "show-uid-validity";
        verify-options = "show-uid-validity";
      };
    };

  };
}
