{ inputs, lib, config, hostName, ... }:
let
  wgIface = "wg-intra";
  peers = import wg-intra/peers.nix;
  wg = config.networking.wireguard.interfaces.${wgIface};
in
{
  # Each peer select the other peers allowed to connect to it
  options.networking.wireguard.${wgIface}.peers =
    lib.genAttrs (lib.attrNames peers) (_peerName: {
      enable = lib.mkEnableOption "this peer";
    });
  config = {
    #systemd.services."wireguard-${wgIface}".serviceConfig.LoadCredentialEncrypted =
    #  [ "privateKey:${inputs.self}/hosts/${hostName}/wireguard/${wgIface}/privateKey.cred" ];
    networking.wireguard.interfaces.${wgIface} = lib.recursiveUpdate
      (removeAttrs peers.${hostName} [ "ipv4" "persistentKeepalive" "peer" ])
      {
        peers =
          lib.mapAttrsToList
            (_peerName: peer:
              lib.recursiveUpdate
                {
                  persistentKeepalive =
                    peer.persistentKeepalive # Useful if this peer is behind a NAT
                      or peers.${hostName}.persistentKeepalive # Useful if this host is behind a NAT
                      or null;
                }
                peer.peer)
            (removeAttrs
              (lib.filterAttrs (peerName: _: config.networking.wireguard.${wgIface}.peers.${peerName}.enable) peers)
              [ hostName ]);
        privateKeyFile = "\$CREDENTIALS_DIRECTORY/privateKey";

        # Set the MTU to a minimum
        # (IPv4 requires at least 68 but it's 1280 for IPv6).
        # This prevents connections to stall on huge packets,
        # or delaying their initializing due to TCP PMTU probing.
        postSetup = ''
          ip link set dev ${wgIface} mtu 1280
        '';
      };
    networking.hosts = lib.mkMerge [
      (lib.mapAttrs'
        (hostName: host:
          lib.nameValuePair host.ipv4 [ "${hostName}.wg" ])
        peers)
      {
        "${peers.losurdo.ipv4}" = [
          "nix-extracache.losurdo.wg"
          "nix-localcache.losurdo.wg"
          "sftp.losurdo.wg"
        ];
      }
    ];
    networking.firewall.extraCommands = lib.optionalString (wg.listenPort != null) ''
      ip46tables -A nixos-fw -i any -p udp -m udp --dport ${toString wg.listenPort} -j ACCEPT
    '';

    networking.nftables.ruleset = lib.optionalString (wg.listenPort != null) ''
      table inet filter {
        chain input-lan {
          udp dport ${toString wg.listenPort} counter accept \
            comment "Wireguard ${wgIface} input from peers"
        }
        chain input-net {
          udp dport ${toString wg.listenPort} counter accept \
            comment "Wireguard ${wgIface} input from peers"
        }
        chain input-intra {
          ${lib.optionalString (peers.${hostName}.peer.endpointsUpdater.enable or false) ''
            tcp dport ${toString peers.${hostName}.listenPort} ip daddr ${peers.${hostName}.ipv4} counter accept comment "Wireguard ${wgIface} from peers to endpointUpdater"
            ''
          }
        }
        chain input {
          iifname ${wgIface} jump input-intra
          iifname ${wgIface} log level warn prefix "input-intra: " counter drop
        }

        chain output-lan {
          udp sport ${toString wg.listenPort} counter accept \
            comment "Wireguard ${wgIface} output to peers"
        }
        chain output-net {
          udp sport ${toString wg.listenPort} counter accept \
            comment "Wireguard ${wgIface} output to peers"
        }
        chain output-intra {
          ${lib.concatStringsSep "\n"
              (lib.mapAttrsToList (peerName: peer: ''
                ip daddr ${peer.ipv4} \
                  tcp dport ${toString peer.listenPort} \
                  counter accept \
                  comment "Wireguard ${wgIface} to endpointUpdater ${peerName}"
                '')
                (lib.filterAttrs (peerName: peer:
                  config.networking.wireguard.${wgIface}.peers.${peerName}.enable &&
                  (peers.${peerName}.peer.endpointsUpdater.enable or false))
                  peers))
          }
        }
        chain output {
          oifname ${wgIface} jump output-intra
          oifname ${wgIface} log level warn prefix "output-intra: " counter drop
        }
      }
    '';

    services.fail2ban.ignoreIP = lib.concatMap
      (host: host.peer.allowedIPs)
      (lib.attrValues peers);
    networking.networkmanager.unmanaged = [ wgIface ];
  };
}