# 10.0.0.0/24 - phys clients (lan).
# 10.1.0.0/24 - containers.
# 10.1.1.0/24 - vpn clients.
{
	config,
	const,
	lib,
	util,
	...
}: let
	external = "188.242.247.132"; # Wan host IP address.
	internal = "10.0.0.1";        # Lan host IP address.
	wifi     = "10.0.0.2";        # Wifi router IP address.

	lan = "br0";    # Lan interface.
	wan = "enp8s0"; # Wan interface.
in {
	# Disable SSH access from everywhere, configure access bellow.
	services.openssh.openFirewall = false;

	# NOTE: Debugging.
	# systemd.services."systemd-networkd".environment.SYSTEMD_LOG_LEVEL = "debug";

	# Wan configuration.
	# REF: https://nixos.wiki/wiki/Systemd-networkd
	# SEE: man 5 systemd.network
	systemd.network = {
		enable = true;
		wait-online.enable = false; # HACK: So we can use both NM and networkd.
		networks = {
			"10-${wan}" = {
				matchConfig.Name = wan;
				linkConfig.RequiredForOnline = "carrier";
				dhcpV4Config = {
					ClientIdentifier = "mac";
					UseDNS    = false;
					UseRoutes = true;
				};
				dhcpV6Config = {
					DUIDRawData = "00:03:00:01:a8:a1:59:47:fd:a2";
					DUIDType    = "vendor";
					UseDNS      = false;
					WithoutRA   = "solicit";
					# PrefixDelegationHint = "::/56";
				};
				networkConfig = {
					DHCP = "yes";
					DNS  = "1.1.1.1";
					IPv6AcceptRA = true;
					IPv6SendRA   = false;
					DHCPPrefixDelegation = true;
				};
				dhcpPrefixDelegationConfig = {
					UplinkInterface = ":self";
					SubnetId = 0;
					Announce = false;
				};
			};
			"20-enp6s0f0" = {
				linkConfig.RequiredForOnline = "enslaved";
				matchConfig.Name             = "enp6s0f0";
				networkConfig.Bridge         = lan;
			};
			"20-enp6s0f1" = {
				linkConfig.RequiredForOnline = "enslaved";
				matchConfig.Name             = "enp6s0f1";
				networkConfig.Bridge         = lan;
			};
			"20-enp7s0f0" = {
				linkConfig.RequiredForOnline = "enslaved";
				matchConfig.Name             = "enp7s0f0";
				networkConfig.Bridge         = lan;
			};
			"20-enp7s0f1" = {
				linkConfig.RequiredForOnline = "enslaved";
				matchConfig.Name             = "enp7s0f1";
				networkConfig.Bridge         = lan;
			};
			"30-${lan}" = {
				matchConfig.Name = lan;
				bridgeConfig = {};
				linkConfig.RequiredForOnline = "carrier";
				address = [
					"10.0.0.1/24"
				];
				networkConfig = {
					DHCPPrefixDelegation = true;
					DHCPServer   = true;
					IPv6AcceptRA = false;
					IPv6SendRA   = true;
				};
				ipv6SendRAConfig = {
					# EmitDNS = true;
					# DNS = ":self";
				};
				dhcpPrefixDelegationConfig = {
					Announce = true;
					SubnetId = 1;
					UplinkInterface = wan;
				};
				dhcpServerConfig = {
					DNS                 = "10.0.0.1";
					DefaultLeaseTimeSec = "12h";
					EmitDNS             = true;
					EmitNTP             = true;
					EmitRouter          = true;
					EmitTimezone        = true;
					MaxLeaseTimeSec     = "24h";
					# PersistLeases       = false;
					PoolOffset          = 100;
					PoolSize            = 150;
					ServerAddress       = "10.0.0.1/24";
					Timezone            = const.timeZone;
					UplinkInterface     = wan;
				};
				dhcpServerStaticLeases = let
					mkStatic = Address: MACAddress: { dhcpServerStaticLeaseConfig = { inherit Address MACAddress; }; };
				in [
					# TODO: Add pocket.
					(mkStatic "10.0.0.2"  "9c:9d:7e:8e:3d:c8") # Wifi AP.
					(mkStatic "10.0.0.3"  "d8:bb:c1:cc:da:30") # Desktop.
					(mkStatic "10.0.0.4"  "2c:be:eb:52:53:2b") # Phone.
					(mkStatic "10.0.0.5"  "14:85:7f:eb:6c:25") # Work.
					(mkStatic "10.0.0.6"  "08:38:e6:31:54:b6") # Tablet.
					(mkStatic "10.0.0.7"  "2c:f0:5d:3b:07:78") # Dasha.
					(mkStatic "10.0.0.8"  "ac:5f:ea:ef:b5:05") # Dasha phone.
					(mkStatic "10.0.0.9"  "10:b1:df:ea:18:57") # Laptop.
					(mkStatic "10.0.0.10" "9c:1c:37:62:3f:d5") # Printer.
					(mkStatic "10.0.0.11" "dc:a6:32:f5:77:95") # RPi.
					(mkStatic "10.0.0.12" "ec:9c:32:ad:bc:4a") # Camera.
				];
			};
		};

		netdevs = {
			"10-${lan}" = {
				netdevConfig = {
					Kind = "bridge";
					Name = lan;
				};
			};
		};
	};

	networking = {
		dhcpcd.enable = false;
		useDHCP       = false;
		useNetworkd   = true;
		networkmanager.enable = lib.mkForce false;
		firewall = {
			enable = true;
			allowPing = true;
			rejectPackets = false; # Drop.

			logRefusedConnections  = false;
			logReversePathDrops    = false;
			logRefusedPackets      = false;
			logRefusedUnicastsOnly = true;

			extraCommands = let
				# Container configs.
				cfg = config.container.module;

				# Const.
				tcp = "tcp";
				udp = "udp";

				# Create port forwarding rule.
				mkForward = src: sport: dst: dport: proto: "iptables -t nat -I PREROUTING -d ${src} -p ${proto} --dport ${toString sport} -j DNAT --to-destination ${dst}:${toString dport}\n";
			in (util.trimTabs ''
				# Wan access for 10.0.0.0/24 subnet.
				iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -d 0/0 -o ${wan} -j MASQUERADE

				# Full access from VPN clients.
				iptables -I INPUT -j ACCEPT -s ${cfg.vpn.clients} -d ${internal}
				iptables -I INPUT -j ACCEPT -s ${cfg.frkn.address} -d ${internal}

				# Full access from Lan.
				iptables -I INPUT -j ACCEPT -i ${lan} -d ${internal}

				# Allow DHCP.
				iptables -I INPUT -j ACCEPT -i ${lan} -p udp --dport 67
			'')
			# Expose DNS server for internal network.
			+ (mkForward internal cfg.dns.port cfg.dns.address cfg.dns.port tcp)
			+ (mkForward internal cfg.dns.port cfg.dns.address cfg.dns.port udp)

			# Email server.
			+ (mkForward external 25  cfg.mail.address 25  tcp)
			+ (mkForward internal 25  cfg.mail.address 25  tcp)
			+ (mkForward internal 465 cfg.mail.address 465 tcp)
			+ (mkForward internal 993 cfg.mail.address 993 tcp)

			# FRKN internal proxy server.
			+ (mkForward internal cfg.frkn.port     cfg.frkn.address cfg.frkn.port     tcp)
			+ (mkForward internal cfg.frkn.torport  cfg.frkn.address cfg.frkn.torport  tcp)
			+ (mkForward internal cfg.frkn.xrayport cfg.frkn.address cfg.frkn.xrayport tcp)
			+ (mkForward internal cfg.frkn.port     cfg.frkn.address cfg.frkn.port     udp)
			+ (mkForward internal cfg.frkn.torport  cfg.frkn.address cfg.frkn.torport  udp)
			+ (mkForward internal cfg.frkn.xrayport cfg.frkn.address cfg.frkn.xrayport udp)

			# VPN connections.
			+ (mkForward external cfg.vpn.port cfg.vpn.address cfg.vpn.port udp)

			# Nginx HTTP.
			+ (mkForward external cfg.proxy.port cfg.proxy.address cfg.proxy.port tcp)
			+ (mkForward internal cfg.proxy.port cfg.proxy.address cfg.proxy.port tcp)

			# Download ports for torrents.
			+ (mkForward external 54630 cfg.download.address 54630 tcp)
			+ (mkForward external 54631 cfg.download.address 54631 tcp)
			+ (mkForward external 54630 cfg.download.address 54630 udp)
			+ (mkForward external 54631 cfg.download.address 54631 udp)

			# Git SSH connections.
			+ (mkForward external cfg.git.portSsh cfg.git.address cfg.git.portSsh tcp)
			+ (mkForward internal cfg.git.portSsh cfg.git.address cfg.git.portSsh tcp)

			# Print serivce.
			+ (mkForward internal cfg.print.port cfg.print.address cfg.print.port tcp);

			# SSH access from WAN.
			# + (mkForward external 22143 config.container.host 22143 tcp)
		};
	};
}