{ config, lib, ... }:

with import ./network.nix;
with lib;

let
  openTCP = concatMapStringsSep "," (x: toString x) config.architect.firewall.openTCP;
  openUDP = concatMapStringsSep "," (x: toString x) config.architect.firewall.openUDP;
  openTCPVPN = concatMapStringsSep "," (x: toString x) config.architect.firewall.openTCPVPN;
  openUDPVPN = concatMapStringsSep "," (x: toString x) config.architect.firewall.openUDPVPN;
in
{
  networking = {
    # needed to use nftables
    firewall.enable = false;
    nat.enable = false;

    nftables = {
      enable = true;
      ruleset = ''
        table ip raw {
          chain PREROUTING {
            type filter hook prerouting priority raw; policy accept;
          }

          chain OUTPUT {
            type filter hook output priority raw; policy accept;
          }
        }

        table ip nat {
          chain PREROUTING {
            type nat hook prerouting priority dstnat; policy accept;
          }

          chain INPUT {
            type nat hook input priority 100; policy accept;
          }

          chain OUTPUT {
            type nat hook output priority -100; policy accept;
          }

          chain POSTROUTING {
            type nat hook postrouting priority srcnat; policy accept;
            oifname ${wan-if} ip saddr {${
              lib.concatStringsSep "," towan-wg
            }} masquerade
            oifname ${wan-if} ip saddr ${docker-net} masquerade
            oifname ${wan-if} ip saddr ${tailscale-net} masquerade
          }
        }

        table ip mangle {
          chain PREROUTING {
            type filter hook prerouting priority mangle; policy drop;
            ct state invalid,untracked drop comment "drop invalid"
            ip daddr 255.255.255.255 accept comment "allow broadcast traffic"
            ip daddr 224.0.0.0/4 accept comment "allow multicast traffic"
            iifname ${wan-if} ip saddr ${vpn-net} drop comment "bind any ip to intf ${wan-if}"
            iifname ${wan-if} ip saddr 127.0.0.0/8 drop comment "bind any ip to intf ${wan-if}"
            iifname ${wan-if} accept comment "bind any ip to intf ${wan-if}"
            iifname ${vpn-if} ip saddr ${vpn-net} accept comment "bind ip ${vpn-net} to intf ${vpn-if}"
            iifname ${docker-if} ip saddr ${docker-net} accept comment "bind ip ${docker-net} to intf ${docker-if}"
            iifname ${tailscale-if} ip saddr ${tailscale-net} accept
            iifname "lo" accept comment "bind any ip to intf lo"
            jump mangle_drop
          }

          chain INPUT {
            type filter hook input priority mangle; policy accept;
          }

          chain FORWARD {
            type filter hook forward priority mangle; policy accept;
          }

          chain OUTPUT {
            type route hook output priority mangle; policy accept;
          }

          chain POSTROUTING {
            type filter hook postrouting priority mangle; policy accept;
          }

          chain mangle_drop {
            ip protocol icmp jump mangle_drop_icmp
            ip protocol udp jump mangle_drop_udp
            ip protocol tcp jump mangle_drop_tcp
            log prefix "MANGLE-DROP-UNK "
            drop
          }

          chain mangle_drop_icmp {
            log prefix "MANGLE-DROP-ICMP "
            drop
          }

          chain mangle_drop_tcp {
            log prefix "MANGLE-DROP-TCP "
            drop
          }

          chain mangle_drop_udp {
          log prefix "MANGLE-DROP-UDP "
          drop
          }
        }

        table ip filter {
          chain INPUT {
            type filter hook input priority filter; policy drop;

            ct state established,related accept
            iifname "lo" accept comment "loopback"
            ip daddr 255.255.255.255 accept comment "allow broadcast traffic"
            ip daddr 224.0.0.0/4 accept comment "allow multicast traffic"
            ip saddr ${lan-net} accept comment "lan > local"
            ip saddr ${tailscale-net} accept comment "tailscale > local"
            ip saddr {${lib.concatStringsSep "," gdevices}} accept comment "vpn > local"

            iifname ${wan-if} tcp dport {${openTCP}} accept
            iifname ${wan-if} udp dport {${openUDP}} accept
            iifname ${vpn-if} tcp dport {${openTCPVPN}} accept
            iifname ${vpn-if} udp dport {${openUDPVPN}} accept
                      
            iifname ${vpn-if} icmp type echo-request accept
            iifname ${docker-if} udp dport 53 accept
            jump filter_drop
          }

          chain FORWARD {
            type filter hook forward priority filter; policy drop;
            ct state established,related accept

            # client to client
            ip saddr {${lib.concatStringsSep "," c2c-wg}} ip daddr {${
               lib.concatStringsSep "," c2c-wg
            }} accept                    
                    
            # nat to wan
            oifname ${wan-if} ip saddr {${
              lib.concatStringsSep "," towan-wg
            }} accept

            oifname ${wan-if} ip saddr ${docker-net} accept
            oifname ${wan-if} ip saddr ${tailscale-net} accept
                    
            jump filter_drop
          }

          chain OUTPUT {
            type filter hook output priority filter; policy drop;
            ct state established,related accept
            accept comment "local > *"
            jump filter_drop
          }

          chain filter_drop {
            ip protocol icmp jump filter_drop_icmp
            ip protocol udp jump filter_drop_udp
            ip protocol tcp jump filter_drop_tcp
            log prefix "DROP-UNK "
            drop
          }

          chain filter_drop_icmp {
            log prefix "DROP-icmp "
            drop
          }

          chain filter_drop_tcp {
            log prefix "DROP-tcp "
            drop
          }

          chain filter_drop_udp {
            log prefix "DROP-udp "
            drop
          }
        } 
      '';
    };
  };
}