#!/bin/bash

set -euo pipefail

# Get network configuration
INTERNAL_NET_V4=$(ip -4 --json route | jq -r '.[] | select(.dev == "internal") | select(.dst == "default" | not) | .dst')
INTERNAL_NET_V6=$(ip -6 --json route | jq -r '.[] | select(.dev == "internal") | select(.dst | startswith("fe80") | not) | select(.dst == "default" | not) | .dst')
PUBLIC_IPV4=$(ip -4 -json addr show internet | jq -r '.[0].addr_info[0].local')
PUBLIC_IPV6=$(ip -6 -json addr show internet | jq -r '.[0].addr_info[0].local')

# Validate required configuration
if [ -z "$INTERNAL_NET_V4" ]; then
    echo "Error: Failed to identify internal IPv4 subnet"
    exit 1
fi

if [ -z "$INTERNAL_NET_V6" ]; then
    echo "Error: Failed to identify internal IPv6 subnet"
    exit 1
fi

if [ -z "$PUBLIC_IPV4" ]; then
    echo "Error: Failed to get public IPv4"
    exit 1
fi

if [ -z "$PUBLIC_IPV6" ]; then
    echo "Error: Failed to get public IPv6"
    exit 1
fi

echo "INTERNAL_NET_V4 = $INTERNAL_NET_V4"
echo "INTERNAL_NET_V6 = $INTERNAL_NET_V6"
echo "PUBLIC_IPV4 = $PUBLIC_IPV4"
echo "PUBLIC_IPV6 = $PUBLIC_IPV6"

TEMPLATE_FILE="router.nft"
CONFIG_FILE="/tmp/router.nft"

# Copy template file to working config
cp "$TEMPLATE_FILE" "$CONFIG_FILE"

echo "add rule inet router postrouting ip saddr $INTERNAL_NET_V4 oifname \"internet\" masquerade ${MASQUERADE_TYPE:-}" >>"$CONFIG_FILE"
echo "add rule inet router postrouting ip6 saddr $INTERNAL_NET_V6 oifname \"internet\" masquerade ${MASQUERADE_TYPE:-}" >>"$CONFIG_FILE"

# Add port forwarding rules if specified
if [ -n "${PORT_FORWARDS:-}" ]; then
    echo "$PORT_FORWARDS" | tr ',' '\n' | while IFS=' ' read -r port internal_ip protocol; do
        if [ -z "$port" ] || [ -z "$internal_ip" ] || [ -z "$protocol" ]; then
            continue
        fi

        # Determine if internal IP is IPv4 or IPv6 and append rules to config file
        case "$internal_ip" in
        *:*) # IPv6 address
            echo "add rule inet router prerouting ip6 daddr $PUBLIC_IPV6 $protocol dport $port dnat to [$internal_ip]:$port" >>"$CONFIG_FILE"
            echo "add rule inet router input ip6 daddr $internal_ip $protocol dport $port accept" >>"$CONFIG_FILE"
            ;;
        *) # IPv4 address
            echo "add rule inet router prerouting ip daddr $PUBLIC_IPV4 $protocol dport $port dnat to $internal_ip:$port" >>"$CONFIG_FILE"
            echo "add rule inet router input ip daddr $internal_ip $protocol dport $port accept" >>"$CONFIG_FILE"
            ;;
        esac
    done
fi

# Add configurable latency if specified
if [ -n "${NETWORK_LATENCY_MS:-}" ]; then
    LATENCY=$((NETWORK_LATENCY_MS / 2)) # Latency is only applied to outbound packets. To achieve the actual configured latency, we apply half of it to each interface.
    JITTER=$((LATENCY / 5))             # Vary latency by 20%

    tc qdisc add dev internet root netem delay "${LATENCY}ms" "${JITTER}ms"
    tc qdisc add dev internal root netem delay "${LATENCY}ms" "${JITTER}ms"
fi

echo "-----------------------------------------------------------------------------------------------"
cat "$CONFIG_FILE"
echo "-----------------------------------------------------------------------------------------------"

nft -f "$CONFIG_FILE"

rm "$CONFIG_FILE"

for iface in internal internet; do
    # Enable RPS (Receive Packet Steering) to always use CPU 1 to handle packets.
    # This prevents packet reordering where otherwise the CPU which handles the interrupt would handle the packet.
    echo 1 >"/sys/class/net/$iface/queues/rx-0/rps_cpus" 2>/dev/null
done

echo "1" >/tmp/setup_done # Health check marker

# Keep container running
exec tail -f /dev/null
