#!/bin/sh
#
# Netmode Advanced Mode Helper Library
# Unified configuration for bridges, routed interfaces, and standalone interfaces
#

. /lib/functions.sh
. /usr/share/libubox/jshn.sh

# Global variables to collect ports across all bridge-vlan configurations
BRIDGE_ALL_PORTS=""
BRIDGE_CREATED="0"
SHARED_BRIDGE_NAME="br-vlan"

#
# Validation Functions
#

# Validate MTU (65-9000)
validate_mtu() {
	local mtu="$1"

	if [ "$mtu" = "n" ]; then
		return 0
	fi

	# Check if it's a number
	if ! echo "$mtu" | grep -qE '^[0-9]+$'; then
		logger -s -p user.err -t "netmode-advanced" "ERROR: MTU must be a number: '$mtu'"
		return 1
	fi

	# Check range
	if [ "$mtu" -lt 65 -o "$mtu" -gt 9000 ]; then
		logger -s -p user.err -t "netmode-advanced" "ERROR: MTU out of range (must be [65-9000]): $mtu"
		return 1
	fi

	return 0
}

# Validate VLAN ID (1-4094)
validate_vlan_id() {
	local vid="$1"
	local description="${2:-VLAN ID}"

	# Check if it's a number
	if ! echo "$vid" | grep -qE '^[0-9]+$'; then
		logger -s -p user.err -t "netmode-advanced" "ERROR: $description must be a number: '$vid'"
		return 1
	fi

	# Check range
	if [ "$vid" -lt 1 -o "$vid" -gt 4094 ]; then
		logger -s -p user.err -t "netmode-advanced" "ERROR: $description out of range (must be 1-4094): $vid"
		return 1
	fi

	return 0
}

# Validate MAC address format
validate_mac_address() {
	local mac="$1"
	local allow_macros="${2:-0}"

	# If macros are allowed, check for BaseMACAddress patterns
	if [ "$allow_macros" = "1" ]; then
		case "$mac" in
			BaseMACAddress|BaseMACAddressP[0-9]*)
				return 0
				;;
		esac
	fi

	# Validate MAC address format (XX:XX:XX:XX:XX:XX)
	if ! echo "$mac" | grep -qiE '^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$'; then
		logger -s -p user.err -t "netmode-advanced" "ERROR: Invalid MAC address format: '$mac' (expected XX:XX:XX:XX:XX:XX)"
		return 1
	fi

	return 0
}

# Validate interface name (no special characters except underscore)
validate_interface_name() {
	local name="$1"

	if [ -z "$name" ]; then
		logger -s -p user.err -t "netmode-advanced" "ERROR: Interface name cannot be empty"
		return 1
	fi

	# Check for invalid characters
	if ! echo "$name" | grep -qE '^[a-zA-Z0-9_]+$'; then
		logger -s -p user.err -t "netmode-advanced" "ERROR: Invalid interface name '$name' (only alphanumeric and underscore allowed)"
		return 1
	fi

	# Check for reserved names
	case "$name" in
		loopback|lo|globals)
			logger -s -p user.err -t "netmode-advanced" "ERROR: Interface name '$name' is reserved"
			return 1
			;;
	esac

	return 0
}

# Validate port specification
validate_port_spec() {
	local port_spec="$1"

	if [ -z "$port_spec" ]; then
		logger -s -p user.err -t "netmode-advanced" "ERROR: Port specification cannot be empty"
		return 1
	fi

	# Check if it's ALL, ALL_LAN, WAN, or EXT
	case "$port_spec" in
		ALL|ALL_LAN|WAN|wan|EXT|ext)
			return 0
			;;
	esac


	local OLD_IFS="$IFS"
	IFS='-'
	for port in $port_spec; do
		[ -z "$port" ] && continue

		# :u (untagged) and :tpid:NNNN are mutually exclusive
		if echo "$port" | grep -q ':u' && echo "$port" | grep -qE ':tpid:[0-9]+'; then
			logger -s -p user.err -t "netmode-advanced" "ERROR: ':u' (untagged) and ':tpid:' are mutually exclusive in '$port'"
			return 1
		fi

		# Strip :u and optional :tpid:NNNN suffixes before validating port names
		port="${port//:u/}"
		port="$(echo "$port" | sed 's/:tpid:[0-9]*//')"

		case "$port" in
			LAN|LAN[1-8]|WAN|wan|EXT|ext|eth[0-9]*|ae_*|wl[0-9]*)
				# Valid port identifier
				;;
			*)
				logger -s -p user.err -t "netmode-advanced" "ERROR: Invalid port identifier in '$port_spec': '$port'"
				IFS="$OLD_IFS"
				return 1
				;;
		esac
	done
	IFS="$OLD_IFS"

	return 0
}

# Validate interface type syntax
validate_interface_type() {
	local type_spec="$1"

	if [ -z "$type_spec" ]; then
		logger -s -p user.err -t "netmode-advanced" "ERROR: Interface type cannot be empty"
		return 1
	fi

	# Remove protocol modifiers for validation (-pppoe, -dhcpv6, -dhcp, -static, -none, -n, -disabled, -d)
	# Use a loop to strip known modifiers from the end
	local base_type="$type_spec"
	base_type="${base_type%-disabled}"
	base_type="${base_type%-d}"
	base_type="${base_type%-iptv}"
	base_type="${base_type%-inet}"
	base_type="${base_type%-mgmt}"
	base_type="${base_type%-lan}"
	base_type="${base_type%-wan}"
	base_type="${base_type%-pppoe}"
	base_type="${base_type%-dhcpv6}"
	base_type="${base_type%-dhcp}"
	base_type="${base_type%-static}"
	base_type="${base_type%-none}"
	base_type="${base_type%-n}"

	# Validate based on type
	case "$base_type" in
		bridge:transparent|route:transparent|direct:transparent)
			return 0
			;;
		device-ref:*)
			# Device reference - just check that it's not empty
			local ref_name="${base_type#*:}"
			if [ -z "$ref_name" ]; then
				logger -s -p user.err -t "netmode-advanced" "ERROR: Reference interface name cannot be empty in '$type_spec'"
				return 1
			fi
			return 0
			;;
		bridge:tagged:*|bridge:wan-tagged:*|brvlan:tagged:*|brvlan:wan-tagged:*)
			local vid="${base_type##*:}"
			validate_vlan_id "$vid" "VLAN ID in type '$type_spec'" || return 1
			;;
		bridge:qinq:*|bridge:transparent-qinq:*|bridge:tagged-qinq:*)
			# Extract VIDs and validate
			local vids="${base_type#*qinq:}"
			local cvid="${vids%%:*}"
			local svid="${vids#*:}"

			if [ "$cvid" != "$vids" ]; then
				# Has both C and S tags
				validate_vlan_id "$cvid" "C-VID in type '$type_spec'" || return 1
				validate_vlan_id "$svid" "S-VID in type '$type_spec'" || return 1
			else
				# Only S-tag
				validate_vlan_id "$cvid" "S-VID in type '$type_spec'" || return 1
			fi
			;;
		brvlan:mixed:*)
			# Extract VID (format: brvlan:mixed:VID:PORTS or brvlan:mixed:VID)
			local params="${base_type#brvlan:mixed:}"
			local vid="${params%%:*}"
			validate_vlan_id "$vid" "VLAN ID in type '$type_spec'" || return 1
			;;
		route:vlan:*)
			local vid="${base_type#route:vlan:}"
			# Check if it has :MAC suffix
			vid="${vid%%:*}"
			validate_vlan_id "$vid" "VLAN ID in type '$type_spec'" || return 1
			;;
		route:macvlan:*)
			local mac="${base_type#route:macvlan:}"
			validate_mac_address "$mac" "1" || return 1
			;;
		direct:vlan:*)
			local vid="${base_type#direct:vlan:}"
			validate_vlan_id "$vid" "VLAN ID in type '$type_spec'" || return 1
			;;
		*)
			logger -s -p user.err -t "netmode-advanced" "ERROR: Unknown interface type: '$type_spec'"
			logger -s -p user.err -t "netmode-advanced" "Valid types: bridge:transparent, bridge:tagged:VID, brvlan:wan-tagged:VID, route:vlan:VID, route:macvlan:MAC, device-ref:INTERFACE, direct:VID"
			return 1
			;;
	esac

	return 0
}

disable_macoffset_for_iface() {
	local iface="$1"
	local file="/etc/macoffset"

	[ -z "$iface" ] && {
		logger -s -p user.err -t "netmode-advanced" "No interface name provided"
		return 1
	}

	[ ! -f "$file" ] && {
		logger -s -p user.err -t "netmode-advanced" "macoffset file not found: $file"
		return 1
	}

	logger -s -p user.info -t "netmode-advanced" "Disabling macoffset for interface: $iface"

	# Create a temp file
	local tmp="$(mktemp)"

	awk -v iface="$iface" '
	BEGIN {
		in_config = 0
	}

	# Force autofill to false
	/"autofill"[[:space:]]*:/ {
		sub(/true/, "false")
		print
		next
	}

	# Detect start of config array
	/"config"[[:space:]]*:[[:space:]]*\[/ {
		in_config = 1
		print
		next
	}

	# Detect end of config array
	in_config && /\]/ {
		in_config = 0
		print
		next
	}

	# Inside config array: filter out matching interfaces
	in_config {
		# Strip quotes and commas for matching
		line = $0
		gsub(/[",]/, "", line)

		# Drop entries containing network.<iface>
		if (line ~ ("network\\." iface "( |$)")) {
			next
		}

		print
		next
	}

	# Default: print unchanged
	{
		print
	}
	' "$file" > "$tmp"

	# Replace original file
	cp "$tmp" "$file"
	rm -f "$tmp"

	logger -s -p user.info -t "netmode-advanced" "macoffset updated: interface '$iface' removed and autofill disabled"
}

# Get base MAC address from environment
get_base_mac() {
	local base_mac=$(fw_printenv -n ethaddr 2>/dev/null)
	[ -z "$base_mac" ] && base_mac="00:00:00:00:00:00"
	echo "$base_mac"
}

# Increment MAC address by N
# Input: MAC address and increment value
# Output: Incremented MAC address
increment_mac() {
	local mac="$1"
	local increment="${2:-1}"

	# Validate increment is a number
	if ! echo "$increment" | grep -qE '^[0-9]+$'; then
		logger -s -p user.err -t "netmode-advanced" "ERROR: MAC increment must be a number: '$increment'"
		echo "00:00:00:00:00:00"
		return 1
	fi

	# Validate base MAC format
	if ! validate_mac_address "$mac" "0"; then
		echo "00:00:00:00:00:00"
		return 1
	fi

	# Convert MAC to decimal
	local mac_decimal=$(echo "$mac" | tr -d ':' | awk '{print "0x"$1}' | xargs printf "%d" 2>/dev/null)

	# Check for conversion error
	if [ -z "$mac_decimal" ]; then
		logger -s -p user.err -t "netmode-advanced" "ERROR: Failed to convert MAC address to decimal: '$mac'"
		echo "00:00:00:00:00:00"
		return 1
	fi

	# Add increment
	mac_decimal=$((mac_decimal + increment))

	# Check for overflow (48-bit MAC address max: 281474976710655)
	if [ "$mac_decimal" -gt 281474976710655 ]; then
		logger -s -p user.err -t "netmode-advanced" "WARNING: MAC address overflow after increment, wrapping around"
		mac_decimal=$((mac_decimal & 0xFFFFFFFFFFFF))
	fi

	# Convert back to MAC format
	printf "%02X:%02X:%02X:%02X:%02X:%02X\n" \
		$((mac_decimal >> 40 & 0xFF)) \
		$((mac_decimal >> 32 & 0xFF)) \
		$((mac_decimal >> 24 & 0xFF)) \
		$((mac_decimal >> 16 & 0xFF)) \
		$((mac_decimal >> 8 & 0xFF)) \
		$((mac_decimal & 0xFF))
}

# Resolve MAC address from macro or explicit value
# Input: MAC address or macro (BaseMACAddress, BaseMACAddressP1, etc.)
# Output: Actual MAC address
resolve_mac_address() {
	local mac_spec="$1"
	local base_mac=""
	local result=""

	if [ -z "$mac_spec" ]; then
		logger -s -p user.err -t "netmode-advanced" "ERROR: MAC address specification is empty"
		echo "00:00:00:00:00:00"
		return 1
	fi

	case "$mac_spec" in
		BaseMACAddress)
			result=$(get_base_mac)
			if [ -z "$result" -o "$result" = "00:00:00:00:00:00" ]; then
				logger -s -p user.err -t "netmode-advanced" "WARNING: Base MAC address not found or invalid, using default"
			fi
			echo "$result"
			;;
		BaseMACAddressP*)
			local increment="${mac_spec#BaseMACAddressP}"
			if [ -z "$increment" ]; then
				logger -s -p user.err -t "netmode-advanced" "ERROR: Invalid MAC macro format: '$mac_spec' (expected BaseMACAddressPNN)"
				echo "00:00:00:00:00:00"
				return 1
			fi
			base_mac=$(get_base_mac)
			result=$(increment_mac "$base_mac" "$increment")
			echo "$result"
			;;
		*)
			# Validate explicit MAC address
			if validate_mac_address "$mac_spec" "0"; then
				echo "$mac_spec"
			else
				logger -s -p user.err -t "netmode-advanced" "ERROR: Invalid MAC address or macro: '$mac_spec'"
				echo "00:00:00:00:00:00"
				return 1
			fi
			;;
	esac
}

# Resolve device name from UCI device section
# Input: Device identifier (e.g., "LAN1", "WAN", "eth0")
# Output: Actual interface name from UCI or the input if not found
resolve_device_name() {
	local device_id="$1"
	local resolved_name=""

	# Try to get from UCI device section
	resolved_name="$(uci -q get network.${device_id}.name)"

	# If not found in UCI, return the input as-is
	if [ -z "$resolved_name" ]; then
		resolved_name="$device_id"
	fi

	echo "$resolved_name"
}

# Get physical ports from board.json
get_lan_ports() {
	local ports=""

	if [ -f /etc/board.json ]; then
		json_load_file /etc/board.json
		json_select network 2>/dev/null || return 1
		json_select lan 2>/dev/null || return 1

		if json_is_a ports array; then
			json_get_keys port_keys ports
			for key in $port_keys; do
				json_select ports
				json_get_var port "$key"
				json_select ..
				echo -n "$port "
			done
		else
			json_get_var device device
			[ -n "$device" ] && echo -n "$device"
		fi

		json_cleanup
	fi
}

get_wan_port() {
	local wan_port=""

	if [ -f /etc/board.json ]; then
		json_load_file /etc/board.json
		json_select network 2>/dev/null || return 1
		json_select wan 2>/dev/null || return 1
		json_get_var wan_port device
		json_cleanup
	fi

	# Fallback to UCI WAN device
	if [ -z "$wan_port" ]; then
		wan_port="$(uci -q get network.WAN.name)"
	fi

	# Final fallback
	[ -z "$wan_port" ] && wan_port="$(uci -q get network.WAN.ifname)"

	# Error if still not found
	if [ -z "$wan_port" ]; then
		logger -s -p user.err -t "netmode-advanced" "ERROR: WAN port not found in board.json or UCI"
		return 1
	fi

	echo "$wan_port"
}

# Parse port list specification
# Input: "ALL" or "LAN1-LAN2-WAN" or "LAN1-LAN2:tpid:34984" or "LAN1-LAN2:u" or "WAN"
# Note: :u (untagged) and :tpid:NNNN are mutually exclusive.
# Output: Space-separated list of actual port names resolved from UCI,
#         with :u or :tpid:NNNN suffix preserved where present.
parse_port_list() {
	local spec="$1"
	local result=""

	if [ "$spec" = "ALL" ]; then
		# Try to resolve from UCI device sections first
		# Check for single LAN port first
		if uci -q get network.LAN >/dev/null 2>&1; then
			local lan_port=$(resolve_device_name "LAN")
			result="$result $lan_port"
		else
			# Check for LAN1-8
			for device_macro in LAN1 LAN2 LAN3 LAN4 LAN5 LAN6 LAN7 LAN8; do
				local resolved=$(resolve_device_name "$device_macro")
				# Only add if the device section exists in UCI
				if uci -q get network.${device_macro} >/dev/null 2>&1; then
					result="$result $resolved"
				fi
			done
		fi

		# Add WAN port
		local wan_port=$(resolve_device_name "WAN")
		if [ -n "$wan_port" ]; then
			result="$result $wan_port"
		fi

		# Add EXT port if it exists
		if uci -q get network.EXT >/dev/null 2>&1; then
			local ext_port=$(resolve_device_name "EXT")
			if [ -n "$ext_port" ]; then
				result="$result $ext_port"
			fi
		fi

		# Fallback to board.json if UCI didn't provide devices
		if [ -z "$result" ]; then
			local lan_ports=$(get_lan_ports)
			for port in $lan_ports; do
				result="$result $port"
			done
			local wan_port=$WAN_PORT
			[ -n "$wan_port" ] && result="$result $wan_port"
		fi
	elif [ "$spec" = "ALL_LAN" ]; then
		# ALL_LAN: All LAN ports excluding WAN and EXT
		# Try to resolve from UCI device sections first
		# Check for single LAN port first
		if uci -q get network.LAN >/dev/null 2>&1; then
			local lan_port=$(resolve_device_name "LAN")
			result="$result $lan_port"
		else
			# Check for LAN1-8
			for device_macro in LAN1 LAN2 LAN3 LAN4 LAN5 LAN6 LAN7 LAN8; do
				local resolved=$(resolve_device_name "$device_macro")
				# Only add if the device section exists in UCI
				if uci -q get network.${device_macro} >/dev/null 2>&1; then
					result="$result $resolved"
				fi
			done
		fi

		# Fallback to board.json if UCI didn't provide devices
		if [ -z "$result" ]; then
			local lan_ports=$(get_lan_ports)
			for port in $lan_ports; do
				result="$result $port"
			done
		fi
	else
		local OLD_IFS="$IFS"
		IFS='-'
		for port_spec in $spec; do
			[ -z "$port_spec" ] && continue

			local untagged=0
			local tpid_suffix=""

			# Parse LAN-LAN1-LAN2-WAN-EXT format, with optional :u or :tpid:NNNN suffix.
			# :u and :tpid:NNNN are mutually exclusive
			if echo "$port_spec" | grep -q ':u' && echo "$port_spec" | grep -qE ':tpid:[0-9]+'; then
				logger -s -p user.err -t "netmode-advanced" "ERROR: ':u' (untagged) and ':tpid:' are mutually exclusive in '$port_spec'"
				return 1
			fi

			# Check for :tpid:NNNN
			if echo "$port_spec" | grep -qE ':tpid:[0-9]+'; then
				tpid_suffix=$(echo "$port_spec" | grep -oE ':tpid:[0-9]+')
				port_spec="${port_spec%${tpid_suffix}}"
			fi

			# Check for :u
			if echo "$port_spec" | grep -q ':u'; then
				untagged=1
				port_spec="${port_spec//:u/}"
			fi

			case "$port_spec" in
				LAN|WAN|LAN[1-8]|EXT)
					# Resolve from UCI device section
					local resolved=$(resolve_device_name "$port_spec")
					if [ -n "$resolved" ]; then
						local suffix=""
						[ "$untagged" = "1" ] && suffix=":u"
						suffix="${suffix}${tpid_suffix}"
						result="$result ${resolved}${suffix}"
					fi
					;;
				*)
					local suffix=""
					[ "$untagged" = "1" ] && suffix=":u"
					suffix="${suffix}${tpid_suffix}"
					result="$result ${port_spec}${suffix}"
					;;
			esac
		done
		IFS="$OLD_IFS"
	fi

	echo "$result" | xargs
}

# Parse advanced interface type specification
# Format: bridge:TYPE or route:TYPE or direct:vlan:VID or brvlan:TYPE (bridge VLAN filtering)
# Returns: mode, vlan_type, cvid, svid, mac_addr, proto, disabled
parse_interface_type() {
	local type_spec="$1"
	local _mode="bridge"
	local _vlan_type="none"
	local _cvid=""
	local _svid=""
	local _mac_addr=""
	local _proto="dhcp"
	local _disabled="0"
	local _purpose="" # mgmt, inet (internet) or iptv

	# Check for modifiers (must be at the end of the string)
	# Process -disabled and -d (both set disabled flag)
	if echo "$type_spec" | grep -qE -- '-(disabled|d)$'; then
		_disabled="1"
		type_spec="${type_spec%-disabled}"
		type_spec="${type_spec%-d}"
	fi

	# route interfaces can have different purposes, which will
	# affect firewall and mcast uci
	if echo "$type_spec" | grep -qE -- 'route|direct'; then
		if echo "$type_spec" | grep -qE -- '-iptv$'; then
			_purpose="iptv"
			type_spec="${type_spec%-iptv}"
		fi
	
		if echo "$type_spec" | grep -qE -- '-inet$'; then
			_purpose="inet"
			type_spec="${type_spec%-inet}"
		fi
	
		if echo "$type_spec" | grep -qE -- '-mgmt$'; then
			_purpose="mgmt"
			type_spec="${type_spec%-mgmt}"
		fi
	fi

	if echo "$type_spec" | grep -qE -- '-lan$'; then
		_purpose="lan"
		type_spec="${type_spec%-lan}"
	fi

	if echo "$type_spec" | grep -qE -- '-wan$'; then
		_purpose="wan"
		type_spec="${type_spec%-wan}"
	fi

	# Process protocol modifiers
	if echo "$type_spec" | grep -q -- '-pppoe$'; then
		_proto="pppoe"
		type_spec="${type_spec%-pppoe}"
	elif echo "$type_spec" | grep -q -- '-dhcpv6$'; then
		_proto="dhcpv6"
		type_spec="${type_spec%-dhcpv6}"
	elif echo "$type_spec" | grep -q -- '-dhcp$'; then
		_proto="dhcp"
		type_spec="${type_spec%-dhcp}"
	elif echo "$type_spec" | grep -qE -- '-(none|n)$'; then
		_proto="none"
		type_spec="${type_spec%-none}"
		type_spec="${type_spec%-n}"
	elif echo "$type_spec" | grep -q -- '-static$'; then
		_proto="static"
		type_spec="${type_spec%-static}"
	fi

	# Parse main type
	case "$type_spec" in
		# Bridge modes (traditional VLAN devices)
		bridge:transparent)
			_mode="bridge"
			_vlan_type="none"
			;;
		bridge:tagged:*)
			_mode="bridge"
			_vlan_type="tagged"
			_cvid="${type_spec#bridge:tagged:}"
			;;
		bridge:wan-tagged:*)
			_mode="bridge"
			_vlan_type="wan-tagged"
			_cvid="${type_spec#bridge:wan-tagged:}"
			;;
		bridge:transparent-qinq:*)
			_mode="bridge"
			_vlan_type="transparent-qinq"
			local vids="${type_spec#bridge:transparent-qinq:}"
			if echo "$vids" | grep -q ':'; then
				_cvid="${vids%%:*}"
				_svid="${vids##*:}"
			else
				_svid="$vids"
			fi
			;;
		bridge:tagged-qinq:*)
			_mode="bridge"
			_vlan_type="tagged-qinq"
			local vids="${type_spec#bridge:tagged-qinq:}"
			_cvid="${vids%%:*}"
			_svid="${vids##*:}"
			;;
		bridge:qinq:*)
			_mode="bridge"
			_vlan_type="qinq"
			local vids="${type_spec#bridge:qinq:}"
			_cvid="${vids%%:*}"
			_svid="${vids##*:}"
			;;

		# Bridge VLAN filtering modes (new)
		brvlan:tagged:*)
			_mode="brvlan"
			_vlan_type="tagged"
			_cvid="${type_spec#brvlan:tagged:}"
			;;
		brvlan:wan-tagged:*)
			_mode="brvlan"
			_vlan_type="wan-tagged"
			_cvid="${type_spec#brvlan:wan-tagged:}"
			;;
		brvlan:mixed:*:*)
			_mode="brvlan"
			_vlan_type="mixed"
			local params="${type_spec#brvlan:mixed:}"
			_cvid="${params%%:*}"
			_svid="${params#*:}"  # Use svid to store tagged ports spec
			;;
		brvlan:mixed:*)
			_mode="brvlan"
			_vlan_type="mixed"
			_cvid="${type_spec#brvlan:mixed:}"
			;;

		# Routed modes
		route:vlan:*:*)
			_mode="route"
			_vlan_type="vlan"
			local params="${type_spec#route:vlan:}"
			_cvid="${params%%:*}"
			_mac_addr="${params##*:}"
			;;
		route:vlan:*)
			_mode="route"
			_vlan_type="vlan"
			_cvid="${type_spec#route:vlan:}"
			;;
		route:macvlan:*)
			_mode="route"
			_vlan_type="macvlan"
			_mac_addr="${type_spec#route:macvlan:}"
			;;
		route:transparent)
			_mode="route"
			_vlan_type="none"
			;;

		# Device reference mode (reuse device from another interface)
		device-ref:*)
			_mode="device-ref"
			_vlan_type="none"
			# Extract reference interface name
			_cvid="${type_spec#device-ref:}"
			;;

		# Direct (standalone) mode
		direct:vlan:*)
			_mode="direct"
			_vlan_type="vlan"
			_cvid="${type_spec#direct:vlan:}"
			;;

		direct:transparent)
			_mode="direct"
			_vlan_type="none"
			;;

		*)
			# Default: transparent bridge
			_mode="bridge"
			_vlan_type="none"
			;;
	esac

	# Export results to caller
	eval "export PARSE_MODE='$_mode'"
	eval "export PARSE_VLAN_TYPE='$_vlan_type'"
	eval "export PARSE_CVID='$_cvid'"
	eval "export PARSE_SVID='$_svid'"
	eval "export PARSE_MAC_ADDR='$_mac_addr'"
	eval "export PARSE_PROTO='$_proto'"
	eval "export PARSE_DISABLED='$_disabled'"
	eval "export PARSE_PURPOSE='$_purpose'"
}

# Create VLAN device in UCI
# Usage: create_vlan_device <base_if> <vid> <vlan_type> [tpid]
create_vlan_device() {
	local base_if="$1"
	local vid="$2"
	local vlan_type="$3"  # 8021q or 8021ad
	local tpid="$4"       # optional TPID value, e.g. 34984

	local vlan_name="${base_if}.${vid}"
	local sec_name=$(echo "$vlan_name" | tr '.-' '__')

	uci -q delete "network.${sec_name}"
	uci -q set "network.${sec_name}=device"
	uci -q set "network.${sec_name}.type=${vlan_type}"
	uci -q set "network.${sec_name}.enabled=1"
	uci -q set "network.${sec_name}.vid=${vid}"
	uci -q set "network.${sec_name}.ifname=${base_if}"
	uci -q set "network.${sec_name}.name=${vlan_name}"

	if [ -n "$tpid" ]; then
		uci -q set "network.${sec_name}.tpid=${tpid}"
	fi

	echo "$vlan_name"
}

# Create MACVLAN device in UCI
create_macvlan_device() {
	local base_if="$1"
	local mac_addr="$2"
	local if_name="$3"

	local macvlan_name="${if_name:0:12}mv"
	local sec_name=$(echo "$macvlan_name" | tr '.-' '__')

	logger -s -p user.info -t "netmode-advanced" "Creating MACVLAN device: $macvlan_name with MAC $mac_addr"

	uci -q delete "network.${sec_name}"
	uci -q set "network.${sec_name}=device"
	uci -q set "network.${sec_name}.type=macvlan"
	uci -q set "network.${sec_name}.enabled=1"
	uci -q set "network.${sec_name}.ifname=${base_if}"
	uci -q set "network.${sec_name}.name=${macvlan_name}"
	uci -q set "network.${sec_name}.macaddr=${mac_addr}"

	echo "$macvlan_name"
}

set_mtu_for_port() {
	local port="$1"
	local mtu="$2"
	local port_section=""

	if [ "${mtu}" != "n" ]; then
		port_section="$(uci -X show network | grep -F ".name='${port}'" | cut -d . -f 2)"
		if [ -n "${port_section}" ]; then
			logger -s -p user.info -t "netmode-advanced" "Setting mtu to ${mtu} for ${port_section} (port=${port})"
			uci -q set network.${port_section}.mtu=${mtu}
		fi
	fi
}

# Function to add port to global list if not already present
add_to_bridge_ports() {
	local port="$1"

	# Check if port already in list
	local found=0
	for existing_port in $BRIDGE_ALL_PORTS; do
		if [ "$existing_port" = "$port" ]; then
			found=1
			break
		fi
	done

	# Add if not found
	if [ "$found" = "0" ]; then
		BRIDGE_ALL_PORTS="$BRIDGE_ALL_PORTS $port"
	fi
}

# Function to create the shared bridge device (call once after collecting all ports)
create_shared_bridge() {
	local _log_prefix="netmode-advanced"
	local br_sec_name=$(echo "$SHARED_BRIDGE_NAME" | tr '.-' '_')
	local mtu="${1}"

	logger -s -p user.info -t "$_log_prefix" "Creating shared bridge: $SHARED_BRIDGE_NAME with ports: $BRIDGE_ALL_PORTS"

	# Create bridge device with VLAN filtering enabled
	uci -q delete "network.${br_sec_name}"
	uci -q set "network.${br_sec_name}=device"
	uci -q set "network.${br_sec_name}.name=${SHARED_BRIDGE_NAME}"
	uci -q set "network.${br_sec_name}.type=bridge"
	uci -q set "network.${br_sec_name}.vlan_filtering=1"
	uci -q set "network.${br_sec_name}.bridge_empty=1"
	uci -q delete "network.${br_sec_name}.ports"

	# Add all collected ports to bridge
	for port in $BRIDGE_ALL_PORTS; do
		logger -s -p user.info -t "$_log_prefix" "Adding port to shared bridge: $port"
		uci -q add_list "network.${br_sec_name}.ports=${port}"

		set_mtu_for_port "${port}" "${mtu}"
	done

	BRIDGE_CREATED="1"
	logger -s -p user.info -t "$_log_prefix" "Shared bridge created successfully"
}

# Example usage workflow:
# 1. Call create_bridge_vlan_filtering() for each bridge-vlan configuration
#    This collects all ports but doesn't create the bridge yet
#
# 2. After processing all bridge-vlans, call create_shared_bridge()
#    This creates the single bridge device with all collected ports
#
# Example:
# create_bridge_vlan_filtering "office1" "brvlan:wan-tagged:1499" "LAN3-LAN4-WAN" ""
# create_bridge_vlan_filtering "office2" "brvlan:wan-tagged:1510" "LAN4-WAN" ""
# create_shared_bridge
# Create bridge with VLAN filtering
# Uses modern bridge VLAN filtering instead of VLAN devices
create_bridge_vlan_filtering() {
	local bridge_name="$1"
	local bridge_type="$2"
	local port_list="$3"
	local mac_addr="$4"

	local _log_prefix="netmode-advanced"

	logger -s -p user.info -t "$_log_prefix" "Processing bridge VLAN: $bridge_name, type: $bridge_type, ports: $port_list, mac: $mac_addr"

	# Parse bridge type
	parse_interface_type "$bridge_type"

	local proto="$PARSE_PROTO"
	local vlan_type="$PARSE_VLAN_TYPE"
	local cvid="$PARSE_CVID"
	local svid="$PARSE_SVID"
	local disabled="$PARSE_DISABLED"

	# Parse ports
	local actual_ports=$(parse_port_list "$port_list")

	logger -s -p user.info -t "$_log_prefix" "Parsed ports: $actual_ports"
	logger -s -p user.info -t "$_log_prefix" "VLAN type: $vlan_type, cvid: $cvid, tagged_ports_spec: $svid"

	# Collect ports for shared bridge (strip :u suffix if present)
	for port in $actual_ports; do
		port="${port%:u}"
		add_to_bridge_ports "$port"
	done

	# Create interface (on VLAN sub-interface)
	local if_name="${bridge_name}"
	local br_vlan_dev="${SHARED_BRIDGE_NAME}.${cvid}"
	local brvlan_sec_name="brv_${cvid}_${bridge_name}"

	uci -q delete "network.${if_name}"
	uci -q set "network.${if_name}=interface"
	uci -q set "network.${if_name}.proto=${proto}"
	uci -q set "network.${if_name}.device=${br_vlan_dev}"

	# Set MAC address if provided
	if [ -n "$mac_addr" ]; then
		local resolved_mac=$(resolve_mac_address "$mac_addr")
		uci -q set "network.${if_name}.macaddr=${resolved_mac}"
		logger -s -p user.info -t "$_log_prefix" "Setting MAC address: $mac_addr -> $resolved_mac"
	fi

	[ "$disabled" = "1" ] && uci -q set "network.${if_name}.disabled=1"

	# Create bridge-vlan section (references the shared bridge)
	uci -q delete "network.${brvlan_sec_name}"
	uci -q set "network.${brvlan_sec_name}=bridge-vlan"
	uci -q set "network.${brvlan_sec_name}.device=${SHARED_BRIDGE_NAME}"
	uci -q set "network.${brvlan_sec_name}.vlan=${cvid}"
	uci -q delete "network.${brvlan_sec_name}.ports"

	# Get WAN port for tagging decisions
	local wan_port=$WAN_PORT

	# Add ports with tagging based on VLAN type
	for port in $actual_ports; do
		port="${port%:u}"

		local is_wan="0"
		[ "$port" = "$wan_port" ] && is_wan="1"

		local port_spec=""

		case "$vlan_type" in
			tagged)
				# All ports tagged
				port_spec="${port}:t"
				;;

			wan-tagged)
				# Only WAN tagged, LAN untagged
				if [ "$is_wan" = "1" ]; then
					port_spec="${port}:t"
				else
					port_spec="${port}:u"
				fi
				;;

			mixed)
				# Custom tagging based on tagged_ports_spec
				# If svid contains port list (e.g., "LAN1-WAN"), those ports are tagged
				# Otherwise, default to wan-tagged behavior
				if [ -n "$svid" ]; then
					# Parse tagged ports specification
					local is_tagged="0"
					local IFS='-'
					for tagged_port_spec in $svid; do
						local resolved_tagged=$(resolve_device_name "$tagged_port_spec")
						if [ "$port" = "$resolved_tagged" -o "$port" = "$tagged_port_spec" ]; then
							is_tagged="1"
							break
						fi
					done

					if [ "$is_tagged" = "1" ]; then
						port_spec="${port}:t"
					else
						port_spec="${port}:u"
					fi
				else
					# Default: WAN tagged, LAN untagged
					if [ "$is_wan" = "1" ]; then
						port_spec="${port}:t"
					else
						port_spec="${port}:u"
					fi
				fi
				;;

			*)
				# Default: all untagged
				port_spec="${port}:u"
				;;
		esac

		logger -s -p user.info -t "$_log_prefix" "Adding port to bridge-vlan: $port_spec"
		uci -q add_list "network.${brvlan_sec_name}.ports=${port_spec}"
	done

	logger -s -p user.info -t "$_log_prefix" "Bridge VLAN $bridge_name (VLAN ${cvid}) configured successfully"
}

# Helper: extract tpid value from a port suffix string like "eth0:tpid:34984" or "eth0:u:tpid:34984"
# Returns the numeric tpid, or empty string if not present.
_extract_tpid() {
	echo "$1" | grep -oE ':tpid:[0-9]+' | grep -oE '[0-9]+$'
}

# Helper: strip :tpid:NNNN from a port string
_strip_tpid() {
	echo "$1" | sed 's/:tpid:[0-9]*//'
}

# Create standalone interface (no bridge, direct VLAN device)
create_standalone_interface() {
	local if_name="$1"
	local vlan_type="$2"
	local vlan_id="$3"
	local mac_addr="$4"
	local proto="$5"
	local base_device="$6"
	local disabled="$7"
	local mtu="$8"

	local _log_prefix="netmode-advanced"

	# Extract and strip tpid from base_device if present
	local tpid=$(_extract_tpid "$base_device")
	base_device=$(_strip_tpid "$base_device")

	logger -s -p user.info -t "$_log_prefix" "Creating standalone interface: $if_name, type: $vlan_type, VLAN: $vlan_id, device: $base_device, mac: $mac_addr, tpid: $tpid"

	case "$vlan_type" in
		vlan)
			# Create VLAN device
			vlan_dev=$(create_vlan_device "$base_device" "$vlan_id" "8021q" "$tpid")
			;;
		none)
			# Direct on base device
			vlan_dev="$base_device"
			;;
	esac

	# Create interface pointing directly to VLAN device (no bridge)
	uci -q delete "network.${if_name}"
	uci -q set "network.${if_name}=interface"
	uci -q set "network.${if_name}.proto=${proto}"
	uci -q set "network.${if_name}.device=${vlan_dev}"

	# Set MAC address if provided
	if [ -n "$mac_addr" ]; then
		local resolved_mac=$(resolve_mac_address "$mac_addr")
		uci -q set "network.${if_name}.macaddr=${resolved_mac}"
		logger -s -p user.info -t "$_log_prefix" "Setting MAC address: $mac_addr -> $resolved_mac"
	fi

	[ "$disabled" = "1" ] && uci -q set "network.${if_name}.disabled=1"

	set_mtu_for_port "${vlan_dev}" "${mtu}"

	logger -s -p user.info -t "$_log_prefix" "Standalone interface $if_name created: device=$vlan_dev"
}

# Create routed interface
create_routed_interface() {
	local if_name="$1"
	local vlan_type="$2"
	local vlan_id="$3"
	local mac_addr="$4"
	local proto="$5"
	local base_device="$6"
	local disabled="$7"
	local purpose="$8"
	local mtu="$9"
	local defaultroute="0"
	local resolved_mac=""

	if [ "$purpose" = "inet" ]; then
		defaultroute="1"
	fi

	local _log_prefix="netmode-advanced"

	# Extract and strip tpid from base_device if present
	local tpid=$(_extract_tpid "$base_device")
	base_device=$(_strip_tpid "$base_device")

	logger -s -p user.info -t "$_log_prefix" "Creating routed interface: $if_name, type: $vlan_type, VLAN: $vlan_id, MAC: $mac_addr, tpid: $tpid"

	local device_name=""

	case "$vlan_type" in
		vlan)
			# Create VLAN device
			device_name=$(create_vlan_device "$base_device" "$vlan_id" "8021q" "$tpid")
			;;
		macvlan)
			# Create MACVLAN device
			# Resolve MAC address macro if needed
			resolved_mac=$(resolve_mac_address "$mac_addr")
			device_name=$(create_macvlan_device "$base_device" "$resolved_mac" "$if_name")
			;;
		none)
			# Direct on base device
			device_name="$base_device"
			;;
	esac

	if [ "$purpose" = "iptv" ]; then
		IPTV_DEVS="$IPTV_DEVS $device_name"
	fi

	# Create routed interface
	uci -q delete "network.${if_name}"
	uci -q set "network.${if_name}=interface"
	uci -q set "network.${if_name}.proto=${proto}"
	uci -q set "network.${if_name}.device=${device_name}"
	uci -q set "network.${if_name}.defaultroute=${defaultroute}"

	if [ "$vlan_type" = "macvlan" ]; then
		# so that macoffset script does not overwrite this mac address
		disable_macoffset_for_iface "${if_name}"
		if [ -n "$resolved_mac" ]; then
			uci -q set "network.${if_name}.macaddr=${resolved_mac}"
		fi
	fi
	# Add MAC address if specified for VLAN interface
	if [ "$vlan_type" = "vlan" -a -n "$mac_addr" ]; then
		uci -q set "network.${if_name}.macaddr=${mac_addr}"
	fi

	[ "$disabled" = "1" ] && uci -q set "network.${if_name}.disabled=1"

	set_mtu_for_port "${device_name}" "${mtu}"

	logger -s -p user.info -t "$_log_prefix" "Routed interface $if_name created: device=$device_name"
}

# Create bridge device and interface
create_bridge() {
	local bridge_name="$1"
	local bridge_type="$2"
	local port_list="$3"
	local mac_addr="$4"
	local mtu="$5"

	local _log_prefix="netmode-advanced"

	logger -s -p user.info -t "$_log_prefix" "Creating bridge: $bridge_name, type: $bridge_type, ports: $port_list, mac: $mac_addr"

	# Parse bridge type
	parse_interface_type "$bridge_type"

	local proto="$PARSE_PROTO"
	local vlan_type="$PARSE_VLAN_TYPE"
	local cvid="$PARSE_CVID"
	local svid="$PARSE_SVID"
	local disabled="$PARSE_DISABLED"

	# Parse ports
	local actual_ports=$(parse_port_list "$port_list")

	logger -s -p user.info -t "$_log_prefix" "Parsed ports: $actual_ports"
	logger -s -p user.info -t "$_log_prefix" "VLAN type: $vlan_type, cvid: $cvid, svid: $svid"

	# Create interface
	local if_name="${bridge_name}"
	local br_dev_name="br-${bridge_name}"
	local br_sec_name

	case "$vlan_type" in
		transparent-qinq | tagged-qinq | qinq)
			br_sec_name="pr_br_${if_name}"
			;;
		*)
			br_sec_name=$(echo "$br_dev_name" | tr '.-' '_')
			;;
	esac

	uci -q delete "network.${if_name}"
	uci -q set "network.${if_name}=interface"
	uci -q set "network.${if_name}.proto=${proto}"
	uci -q set "network.${if_name}.device=${br_dev_name}"

	# Set MAC address if provided
	if [ -n "$mac_addr" ]; then
		local resolved_mac=$(resolve_mac_address "$mac_addr")
		uci -q set "network.${if_name}.macaddr=${resolved_mac}"
		logger -s -p user.info -t "$_log_prefix" "Setting MAC address: $mac_addr -> $resolved_mac"
	fi

	[ "$disabled" = "1" ] && uci -q set "network.${if_name}.disabled=1"

	# Create bridge device
	uci -q delete "network.${br_sec_name}"
	uci -q set "network.${br_sec_name}=device"
	uci -q set "network.${br_sec_name}.name=${br_dev_name}"
	uci -q set "network.${br_sec_name}.type=bridge"
	uci -q set "network.${br_sec_name}.bridge_empty=1"
	uci -q delete "network.${br_sec_name}.ports"

	# Process ports based on VLAN type
	local wan_port=$WAN_PORT

	for port in $actual_ports; do
		local port_to_add="$port"
		local is_wan="0"
		local port_section=""

		# Extract and strip tpid suffix before processing
		local port_tpid=$(_extract_tpid "$port")
		local port=$(_strip_tpid "$port")

		if echo "$port" | grep -q ':u'; then
			# Explicit untagged override
			port_to_add="${port%:u}"
			logger -s -p user.info -t "$_log_prefix" "Port $port_to_add forced untagged"
		else
			# Check if this is the WAN port
			[ "$port" = "$wan_port" ] && is_wan="1"

			case "$vlan_type" in
				none)
					# Transparent bridge - no VLANs
					port_to_add="$port"
					;;

				tagged)
					# All ports tagged with same VID
					port_to_add=$(create_vlan_device "$port" "$cvid" "8021q" "$port_tpid")
					;;

				wan-tagged)
					# Only WAN tagged
					if [ "$is_wan" = "1" ]; then
						port_to_add=$(create_vlan_device "$port" "$cvid" "8021q" "$port_tpid")
					else
						port_to_add="$port"
					fi
					;;

				transparent-qinq)
					# LAN untagged, WAN single-tagged (8021ad)
					if [ "$is_wan" = "1" ]; then
						if [ -n "$cvid" -a -n "$svid" ]; then
							# Double tag on WAN
							local wan_svlan=$(create_vlan_device "$port" "$svid" "8021ad" "$port_tpid")
							port_to_add=$(create_vlan_device "$wan_svlan" "$cvid" "8021q")
						else
							# Single tag on WAN
							port_to_add=$(create_vlan_device "$port" "$svid" "8021ad" "$port_tpid")
						fi
					else
						port_to_add="$port"
					fi
					;;

				tagged-qinq)
					# LAN single-tagged, WAN double-tagged
					if [ "$is_wan" = "1" ]; then
						local wan_svlan=$(create_vlan_device "$port" "$svid" "8021ad" "$port_tpid")
						port_to_add=$(create_vlan_device "$wan_svlan" "$cvid" "8021q")
					else
						port_to_add=$(create_vlan_device "$port" "$cvid" "8021q" "$port_tpid")
					fi
					;;

				qinq)
					# All ports double-tagged
					local svlan=$(create_vlan_device "$port" "$svid" "8021ad" "$port_tpid")
					port_to_add=$(create_vlan_device "$svlan" "$cvid" "8021q")
					;;
			esac
		fi

		logger -s -p user.info -t "$_log_prefix" "Adding port to bridge: $port -> $port_to_add"
		uci -q add_list "network.${br_sec_name}.ports=${port_to_add}"
		set_mtu_for_port "${port_to_add}" "${mtu}"
	done

	logger -s -p user.info -t "$_log_prefix" "Bridge $bridge_name created successfully"
}

# Clean up all existing bridges, VLANs, and routed interfaces
cleanup_interfaces() {
	logger -s -p user.info -t "netmode-advanced" "Cleaning up existing interfaces"

	# Delete all bridge-vlan sections FIRST (before interfaces that reference them)
	logger -s -p user.info -t "netmode-advanced" "Step 1: Deleting bridge-vlan sections"
	for brvlan_sec in $(uci show network | grep "=bridge-vlan" | cut -d'=' -f1 | sort -u); do
		logger -s -p user.info -t "netmode-advanced" "Deleting bridge-vlan: $brvlan_sec"
		uci -q delete "$brvlan_sec"
	done

	# Delete all device sections except physical device sections (LAN, LAN1-8, WAN, EXT)
	logger -s -p user.info -t "netmode-advanced" "Step 2: Deleting device sections"
	for dev_sec in $(uci show network | grep "=device" | cut -d'=' -f1 | sort -u); do
		local dev_name=$(echo "$dev_sec" | cut -d'.' -f2)

		# Skip ONLY physical device sections (LAN, LAN1-8, WAN, EXT)
		case "$dev_name" in
			LAN|LAN1|LAN2|LAN3|LAN4|LAN5|LAN6|LAN7|LAN8|WAN|EXT)
				logger -s -p user.info -t "netmode-advanced" "Preserving physical device: $dev_name"
				;;
			*)
				logger -s -p user.info -t "netmode-advanced" "Deleting device: $dev_sec"
				uci -q delete "$dev_sec"
				;;
		esac
	done

	# Delete all interfaces created by netmode (skip loopback and physical device sections)
	logger -s -p user.info -t "netmode-advanced" "Step 3: Deleting interfaces"
	for iface_sec in $(uci show network | grep "=interface" | cut -d'=' -f1 | sort -u); do
		local iface_name=$(echo "$iface_sec" | cut -d'.' -f2)

		# Skip critical interfaces that should never be deleted
		case "$iface_name" in
			loopback|LAN1|LAN2|LAN3|LAN4|LAN5|LAN6|LAN7|LAN8|WAN|EXT|globals)
				logger -s -p user.info -t "netmode-advanced" "Preserving interface: $iface_name"
				;;
			*)
				logger -s -p user.info -t "netmode-advanced" "Deleting interface: $iface_sec"
				uci -q delete "$iface_sec"
				;;
		esac
	done

	# Delete all VLAN devices (identified by vid option or type 8021q/8021ad)
	logger -s -p user.info -t "netmode-advanced" "Step 4: Deleting VLAN devices"
	for vlandev_sec in $(uci show network | grep -E "\.vid='[0-9]+'" | cut -d'=' -f1 | cut -d'.' -f1-2 | sort -u); do
		local dev_name=$(echo "$vlandev_sec" | cut -d'.' -f2)
		# Skip physical device sections (LAN1-8, WAN, EXT)
		case "$dev_name" in
			LAN1|LAN2|LAN3|LAN4|LAN5|LAN6|LAN7|LAN8|WAN|EXT)
				logger -s -p user.info -t "netmode-advanced" "Preserving physical device: $dev_name"
				;;
			*)
				logger -s -p user.info -t "netmode-advanced" "Deleting VLAN device: $vlandev_sec"
				uci -q delete "$vlandev_sec"
				;;
		esac
	done

	# Also delete devices with type 8021q or 8021ad
	for vlandev_sec in $(uci show network | grep -E "\.type='(8021q|8021ad|macvlan)'" | cut -d'=' -f1 | sort -u); do
		logger -s -p user.info -t "netmode-advanced" "Deleting VLAN device: $vlandev_sec"
		uci -q delete "$vlandev_sec"
	done

	# Delete all MACVLAN devices (identified by mode option or type macvlan)
	logger -s -p user.info -t "netmode-advanced" "Step 5: Deleting MACVLAN devices"
	for macvlandev_sec in $(uci show network | grep -E "\.mode='(passthru|vepa|bridge|private)'" | cut -d'=' -f1 | cut -d'.' -f1-2 | sort -u); do
		local dev_name=$(echo "$macvlandev_sec" | cut -d'.' -f2)
		# Skip physical device sections
		case "$dev_name" in
			LAN1|LAN2|LAN3|LAN4|LAN5|LAN6|LAN7|LAN8|WAN|EXT)
				logger -s -p user.info -t "netmode-advanced" "Preserving physical device: $dev_name"
				;;
			*)
				logger -s -p user.info -t "netmode-advanced" "Deleting MACVLAN device: $macvlandev_sec"
				uci -q delete "$macvlandev_sec"
				;;
		esac
	done

	# Also delete devices with type macvlan
	for macvlandev_sec in $(uci show network | grep "\.type='macvlan'" | cut -d'=' -f1 | sort -u); do
		logger -s -p user.info -t "netmode-advanced" "Deleting MACVLAN device: $macvlandev_sec"
		uci -q delete "$macvlandev_sec"
	done

	logger -s -p user.info -t "netmode-advanced" "Cleanup completed"
}

ensure_firewall_layout() {
	local net=""
	local iface=""
	local is_wan_based_iface

	# ---- 1. add inet + iptv ifaces to wan --------------------------------
	for net in $INET_IFACES $IPTV_IFACES; do
		if [ "$net" != "wan" ] && [ "$net" != "wan6" ]; then
			uci -q add_list firewall.wan.network="$net"
		fi
	done

	for net in $LAN_ARR; do
		is_wan_based_iface=0

		for iface in $WAN_BASED_IFACES; do
			if [ "$net" = "$iface" ]; then
				is_wan_based_iface=1
				break
			fi
		done

		if [ "$net" != "lan" ] && [ "$is_wan_based_iface" -ne 1 ]; then
			uci -q add_list firewall.lan.network="$net"
		fi
	done

	[ -z "$MGMT_IFACES" ] && return

	local mgmt_iface_count=0
	for net in $MGMT_IFACES; do
		if [ "$net" != "wan" ] && [ "$net" != "wan6" ] && [ "$net" != "lan" ]; then
			mgmt_iface_count=$((mgmt_iface_count + 1))
		fi
	done

	[ "$mgmt_iface_count" -eq 0 ] && return

	# ---- 2. create mgmt zone ------------------------------------------
	zone="mgmt"
	uci set firewall.$zone=zone
	uci set firewall.$zone.name='mgmt'
	uci set firewall.$zone.input='REJECT'
	uci set firewall.$zone.output='ACCEPT'
	uci set firewall.$zone.forward='REJECT'
	uci set firewall.$zone.mtu_fix='1'

	for net in $MGMT_IFACES; do
		if [ "$net" != "wan" ] && [ "$net" != "wan6" ] && [ "$net" != "lan" ]; then
			uci add_list firewall.$zone.network="$net"
		fi
	done

	# ---- 3. mgmt rules ---------------------------------------------------
	# DHCPv4
	r="$(uci add firewall rule)"
	uci set firewall.$r.name='Allow-m-DHCP-Renew'
	uci set firewall.$r.src='mgmt'
	uci set firewall.$r.proto='udp'
	uci set firewall.$r.dest_port='68'
	uci set firewall.$r.family='ipv4'
	uci set firewall.$r.target='ACCEPT'

	# Ping IPv4
	r="$(uci add firewall rule)"
	uci set firewall.$r.name='Allow-m-Ping'
	uci set firewall.$r.src='mgmt'
	uci set firewall.$r.proto='icmp'
	uci set firewall.$r.icmp_type='echo-request'
	uci set firewall.$r.family='ipv4'
	uci set firewall.$r.target='ACCEPT'

	# IGMP
	r="$(uci add firewall rule)"
	uci set firewall.$r.name='Allow-m-IGMP'
	uci set firewall.$r.src='mgmt'
	uci set firewall.$r.proto='igmp'
	uci set firewall.$r.family='ipv4'
	uci set firewall.$r.target='ACCEPT'

	# DHCPv6
	r="$(uci add firewall rule)"
	uci set firewall.$r.name='Allow-m-DHCPv6'
	uci set firewall.$r.src='mgmt'
	uci set firewall.$r.proto='udp'
	uci set firewall.$r.dest_port='546'
	uci set firewall.$r.family='ipv6'
	uci set firewall.$r.target='ACCEPT'

	# MLD
	r="$(uci add firewall rule)"
	uci set firewall.$r.name='Allow-m-MLD'
	uci set firewall.$r.src='mgmt'
	uci set firewall.$r.proto='icmp'
	uci set firewall.$r.src_ip='fe80::/10'
	uci add_list firewall.$r.icmp_type='130/0'
	uci add_list firewall.$r.icmp_type='131/0'
	uci add_list firewall.$r.icmp_type='132/0'
	uci add_list firewall.$r.icmp_type='143/0'
	uci set firewall.$r.family='ipv6'
	uci set firewall.$r.target='ACCEPT'

	# ICMPv6 input
	r="$(uci add firewall rule)"
	uci set firewall.$r.name='Allow-ICMPv6-m-Input'
	uci set firewall.$r.src='mgmt'
	uci set firewall.$r.proto='icmp'
	for t in \
		echo-request echo-reply destination-unreachable \
		packet-too-big time-exceeded bad-header \
		unknown-header-type router-solicitation \
		neighbour-solicitation router-advertisement \
		neighbour-advertisement
	do
		uci add_list firewall.$r.icmp_type="$t"
	done
	uci set firewall.$r.limit='1000/sec'
	uci set firewall.$r.family='ipv6'
	uci set firewall.$r.target='ACCEPT'

	uci commit firewall
}

device_has_wan_port() {
	local ifdev="$1"
	local ports name ifname sec p vlan_dev
	local wan_port=$WAN_PORT

	# Direct match
	[[ "$ifdev" == *"$wan_port"* ]] && return 0

	# Check if ifdev is a VLAN interface (e.g., br-lan.200)
	if [[ "$ifdev" == *.* ]]; then
		local base_dev="${ifdev%.*}"
		local vlan_id="${ifdev##*.}"

		# Look for bridge-vlan sections matching this VLAN
		for sec in $(uci -q show network | grep -E "=(bridge-vlan)" | cut -d. -f2 | cut -d= -f1 | tr '\n' ' '); do
			vlan_dev="$(uci -q get network.$sec.device)"
			local vlan_num="$(uci -q get network.$sec.vlan)"

			# If this bridge-vlan matches our base device and VLAN ID
			if [ "$vlan_dev" = "$base_dev" ] && [ "$vlan_num" = "$vlan_id" ]; then
				# Check ports in this bridge-vlan
				for p in $(uci -q get network.$sec.ports 2>/dev/null); do
					# Remove VLAN tagging suffixes (:t, :u, :*)
					local clean_port="${p%:*}"
					[[ "$clean_port" == *"$wan_port"* ]] && return 0
				done
			fi
		done
	fi

	# If device section exists, check its ports
	for sec in $(uci -q show network | grep "=device" | cut -d. -f2 | cut -d= -f1 | tr '\n' ' '); do
		name="$(uci -q get network.$sec.name)"
		if [ "$name" = "$ifdev" ]; then
			# macvlan device?
			ifname="$(uci -q get network.$sec.ifname)"
			[[ "$ifname" == *"$wan_port"* ]] && return 0
			# bridge device?
			for p in $(uci -q get network.$sec.ports 2>/dev/null); do
				[[ "$p" == *"$wan_port"* ]] && return 0
			done
		fi
	done

	return 1
}

collect_interfaces_with_wan_port() {
	local ifaces=""
	local iface dev
	local wan_port=$WAN_PORT

	# Iterate network interfaces
	local uci_ifaces="$(uci -q show network | grep "=interface" | cut -d. -f2 | cut -d= -f1 | tr '\n' ' ')"
	for iface in $uci_ifaces; do
		dev="$(uci -q get network.$iface.device)"
		# If interface has device defined
		if [ -n "$dev" ]; then
			if device_has_wan_port "$dev"; then
				ifaces="$ifaces $iface"
			fi
		fi
	done

	echo "${ifaces# }"
}

create_br_lan_bridge_device() {
	br_lan_section=""

	add_port_to_br_lan() {
		port="$1"
		[ -n "$port" -a -d /sys/class/net/$port ] || continue
		uci add_list network.$br_lan_section.ports="$port"
	}

	if [ -f /etc/board.json ]; then
		for sec in $(uci -q show network | grep "=device" | cut -d. -f2 | cut -d= -f1 | tr '\n' ' '); do
			name="$(uci -q get network.$sec.name)"

			if [ "$name" = "br-lan" ]; then
				br_lan_section="$sec"
			fi
		done

		if [ -z "$br_lan_section" ]; then
			br_lan_section="br_lan"
		fi

		uci -q set network.$br_lan_section=device
		uci -q set network.$br_lan_section.name='br-lan'
		uci -q set network.$br_lan_section.type='bridge'
		uci -q set network.$br_lan_section.bridge_empty='1'
		# remove existing ports and add fresh
		uci -q delete network.$br_lan_section.ports

		json_load_file /etc/board.json
		json_select network
		json_select lan
		if json_is_a ports array; then
			json_for_each_item add_port_to_br_lan ports
		else
			json_get_var device device
			[ -n "$device" ] && uci add_list network.$br_lan_section.ports="$device"
		fi
		json_cleanup
	fi

	uci -q commit network
}

configure_macoffset() {
	local file="/etc/macoffset"

	logger -s -p user.info -t "netmode-advanced" "Writing macoffset configuration to $file"

	cat > "$file" <<'EOF'
{
        "size" : 16,
        "autofill": true,
        "wireless": false,
        "config": [
                "network.lan",
                "network.wan network.wan6",
                "_WLAN0_",
                "_WLAN1_"
        ]
}
EOF

	if [ $? -eq 0 ]; then
		logger -s -p user.info -t "netmode-advanced" "macoffset configuration written successfully"
	else
		logger -s -p user.err -t "netmode-advanced" "Failed to write macoffset configuration"
		return 1
	fi

	return 0
}

