{ config, lib, ... }:

let
  openTCP = lib.concatMapStringsSep "," (x: toString x) config.architect.firewall.openTCP;
  openUDP = lib.concatMapStringsSep "," (x: toString x) config.architect.firewall.openUDP;
  openTCPVPN = lib.concatMapStringsSep "," (x: toString x) config.architect.firewall.openTCPVPN;
  openUDPVPN = lib.concatMapStringsSep "," (x: toString x) config.architect.firewall.openUDPVPN;

  deviceAddress = interface: device: 
    config.architect.networks.${interface}.devices.${device}.address;
    
  gdevices = [
    (deviceAddress "tailscale" "architect")
    (deviceAddress "tailscale" "dodino")
    (deviceAddress "tailscale" "manduria")
    (deviceAddress "tailscale" "kmerr")
    (deviceAddress "tailscale" "chuck")
  ];
in
{
  networking = {
    # needed to use nftables
    firewall.enable = false;
    nat.enable = false;

    nftables = {
      enable = true;
      ruleset = with config.architect.networks; ''
        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 DOCKER {
            type nat hook prerouting priority dstnat; policy accept;
          }
          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 ${lan.interface} ip saddr ${docker.net} masquerade
            oifname ${lan.interface} 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 ${lan.interface} ip saddr 127.0.0.0/8 drop comment "bind any ip to intf ${lan.interface}"
            iifname ${lan.interface} accept comment "bind any ip to intf ${lan.interface}"
            iifname ${docker.interface} ip saddr ${docker.net} accept comment "bind ip ${docker.net} to intf ${docker.interface}"
            iifname ${tailscale.interface} ip saddr ${tailscale.net} accept
            iifname ${tailscale.interface} ip saddr 100.100.100.100/32 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"
            iifname ${docker.interface} accept
            ip saddr ${tailscale.net} accept comment "tailscale > local"
            ip saddr {${lib.concatStringsSep "," gdevices}} accept comment "vpn > local"

            iifname ${lan.interface} tcp dport {${openTCP}} accept
            iifname ${lan.interface} udp dport {${openUDP}} accept
            jump filter_drop
          }

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

            oifname ${lan.interface} ip saddr ${docker.net} accept
            oifname ${lan.interface} 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
          }
        } 
      '';
    };
  };
}