{ lib, pkgs, config, vpns, ... }:
let
  inherit (lib) optionalString mapAttrsToList concatStringsSep filterAttrs mkIf mkMerge;
  inherit (lib.my) isIPv6;

  vxlanPort = 4789;

  selfName = config.system.name;
  memberMeshes = filterAttrs (_: c: c.peers ? "${selfName}") vpns.l2;

  info = mesh: {
    ownAddr = mesh.peers."${selfName}".addr;
    otherPeers = filterAttrs (n: _: n != selfName) mesh.peers;
  };

  mkNetConfig = name: mesh: with info mesh; {
    netdevs."30-l2mesh-${name}" = {
      netdevConfig = {
        Name = mesh.interface;
        Kind = "vxlan";
      };
      vxlanConfig = {
        VNI = mesh.vni;
        Local = ownAddr;
        MacLearning = true;
        DestinationPort = vxlanPort;
        Independent = true;
      };
    };
    links."20-l2mesh-${name}" = {
      matchConfig.Name = mesh.interface;
      # TODO: ipv6? ipsec?
      linkConfig.MTUBytes = "1450";
    };
    networks."90-l2mesh-${name}" = {
      matchConfig.Name = mesh.interface;
      extraConfig = concatStringsSep "\n" (mapAttrsToList (n: peer: ''
        [BridgeFDB]
        MACAddress=00:00:00:00:00:00
        Destination=${peer.addr}
      '') otherPeers);
    };
  };

  mkLibreswanConfig = name: mesh: with info mesh; {
    enable = true;
    # TODO: finish this...
    connections."l2mesh-${name}" = ''
      keyexchange=ike
      type=transport
      left=${ownAddr}

      auto=start
      phase2=esp
      ikev2=yes
    '';
  };

  mkFirewallConfig = name: mesh: with info mesh;
  let
    netProto = if (isIPv6 ownAddr) then "ip6" else "ip";
  in
  ''
    table inet filter {
      chain l2mesh-${name} {
        ${optionalString mesh.security.enable "meta l4proto esp accept"}
        udp dport ${toString vxlanPort} @th,96,24 ${toString mesh.vni} accept
        return
      }
      chain input {
        ${netProto} daddr ${ownAddr} ${netProto} saddr { ${concatStringsSep ", " (mapAttrsToList (_: p: p.addr) otherPeers)} } jump l2mesh-${name}
      }
    }
  '';
in
{
  config = {
    systemd.network = mkMerge (mapAttrsToList mkNetConfig memberMeshes);
    # TODO: finish this...
    #services.libreswan = mkMerge (mapAttrsToList mkLibreswanConfig (filterAttrs (_: c: c.security.enable) memberMeshes));
    my.firewall.extraRules = concatStringsSep "\n" (mapAttrsToList mkFirewallConfig (filterAttrs (_: c: c.firewall) memberMeshes));
  };
}