diff --git a/Makefile b/Makefile
index e130bec70..4400cbfdc 100644
--- a/Makefile
+++ b/Makefile
@@ -1,140 +1,137 @@
 TMPL_DIR := templates-cfg
 OP_TMPL_DIR := templates-op
 BUILD_DIR := build
 DATA_DIR := data
 SHIM_DIR := src/shim
 XDP_DIR := src/xdp
 LIBS := -lzmq
 CFLAGS :=
 BUILD_ARCH := $(shell dpkg-architecture -q DEB_BUILD_ARCH)
 
 J2LINT := $(shell command -v j2lint 2> /dev/null)
 
 config_xml_src = $(wildcard interface-definitions/*.xml.in)
 config_xml_obj = $(config_xml_src:.xml.in=.xml)
 op_xml_src = $(wildcard op-mode-definitions/*.xml.in)
 op_xml_obj = $(op_xml_src:.xml.in=.xml)
 
 %.xml: %.xml.in
 	@echo Generating $(BUILD_DIR)/$@ from $<
 	mkdir -p $(BUILD_DIR)/$(dir $@)
 	$(CURDIR)/scripts/transclude-template $< > $(BUILD_DIR)/$@
 
 .PHONY: interface_definitions
 .ONESHELL:
 interface_definitions: $(config_xml_obj)
 	mkdir -p $(TMPL_DIR)
 
 	$(CURDIR)/scripts/override-default $(BUILD_DIR)/interface-definitions
 
 	find $(BUILD_DIR)/interface-definitions -type f -name "*.xml" | xargs -I {} $(CURDIR)/scripts/build-command-templates {} $(CURDIR)/schema/interface_definition.rng $(TMPL_DIR) || exit 1
 
 	# XXX: delete top level node.def's that now live in other packages
 	# IPSec VPN EAP-RADIUS does not support source-address
 	rm -rf $(TMPL_DIR)/vpn/ipsec/remote-access/radius/source-address
 
 	# T2472 - EIGRP support
 	rm -rf $(TMPL_DIR)/protocols/eigrp
 	# T2773 - EIGRP support for VRF
 	rm -rf $(TMPL_DIR)/vrf/name/node.tag/protocols/eigrp
 
-	# T4518, T4470 Load-balancing wan
-	rm -rf $(TMPL_DIR)/load-balancing
-
 	# XXX: test if there are empty node.def files - this is not allowed as these
 	# could mask help strings or mandatory priority statements
 	find $(TMPL_DIR) -name node.def -type f -empty -exec false {} + || sh -c 'echo "There are empty node.def files! Check your interface definitions." && exit 1'
 
 ifeq ($(BUILD_ARCH),arm64)
 	# There is currently no telegraf support in VyOS for ARM64, remove CLI definitions
 	rm -rf $(TMPL_DIR)/service/monitoring/telegraf
 endif
 
 .PHONY: op_mode_definitions
 .ONESHELL:
 op_mode_definitions: $(op_xml_obj)
 	mkdir -p $(OP_TMPL_DIR)
 
 	find $(BUILD_DIR)/op-mode-definitions/ -type f -name "*.xml" | xargs -I {} $(CURDIR)/scripts/build-command-op-templates {} $(CURDIR)/schema/op-mode-definition.rng $(OP_TMPL_DIR) || exit 1
 
 	# XXX: delete top level op mode node.def's that now live in other packages
 	rm -f $(OP_TMPL_DIR)/add/node.def
 	rm -f $(OP_TMPL_DIR)/clear/interfaces/node.def
 	rm -f $(OP_TMPL_DIR)/clear/node.def
 	rm -f $(OP_TMPL_DIR)/delete/node.def
 	rm -f $(OP_TMPL_DIR)/generate/node.def
 	rm -f $(OP_TMPL_DIR)/monitor/node.def
 	rm -f $(OP_TMPL_DIR)/set/node.def
 	rm -f $(OP_TMPL_DIR)/show/node.def
 	rm -f $(OP_TMPL_DIR)/show/system/node.def
 	rm -f $(OP_TMPL_DIR)/show/tech-support/node.def
 
 	# XXX: ping and traceroute must be able to recursivly call itself as the
 	# options are provided from the script itself
 	ln -s ../node.tag $(OP_TMPL_DIR)/ping/node.tag/node.tag/
 	ln -s ../node.tag $(OP_TMPL_DIR)/traceroute/node.tag/node.tag/
 
 	# XXX: test if there are empty node.def files - this is not allowed as these
 	# could mask help strings or mandatory priority statements
 	find $(OP_TMPL_DIR) -name node.def -type f -empty -exec false {} + || sh -c 'echo "There are empty node.def files! Check your interface definitions." && exit 1'
 
 .PHONY: vyshim
 vyshim:
 	$(MAKE) -C $(SHIM_DIR)
 
 .PHONY: vyxdp
 vyxdp:
 	$(MAKE) -C $(XDP_DIR)
 
 .PHONY: all
 all: clean interface_definitions op_mode_definitions check test j2lint vyshim
 
 .PHONY: check
 .ONESHELL:
 check:
 	@echo "Checking which CLI scripts are not enabled to work with vyos-configd..."
 	@for file in `ls src/conf_mode -I__pycache__`
 	do
 		if ! grep -q $$file data/configd-include.json; then
 			echo "* $$file"
 		fi
 	done
 
 .PHONY: clean
 clean:
 	rm -rf $(BUILD_DIR)
 	rm -rf $(TMPL_DIR)
 	rm -rf $(OP_TMPL_DIR)
 	$(MAKE) -C $(SHIM_DIR) clean
 	$(MAKE) -C $(XDP_DIR) clean
 
 .PHONY: test
 test:
 	set -e; python3 -m compileall -q -x '/vmware-tools/scripts/, /ppp/' .
 	PYTHONPATH=python/ python3 -m "nose" --with-xunit src --with-coverage --cover-erase --cover-xml --cover-package src/conf_mode,src/op_mode,src/completion,src/helpers,src/validators,src/tests --verbose
 
 .PHONY: j2lint
 j2lint:
 ifndef J2LINT
 	$(error "j2lint binary not found, consider installing: pip install git+https://github.com/aristanetworks/j2lint.git@341b5d5db86")
 endif
 	$(J2LINT) data/
 
 .PHONY: sonar
 sonar:
 	sonar-scanner -X -Dsonar.login=${SONAR_TOKEN}
 
 .PHONY: docs
 .ONESHELL:
 docs:
 	sphinx-apidoc -o sphinx/source/  python/
 	cd sphinx/
 	PYTHONPATH=../python make html
 
 deb:
 	dpkg-buildpackage -uc -us -tc -b
 
 .PHONY: schema
 schema:
 	trang -I rnc -O rng schema/interface_definition.rnc schema/interface_definition.rng
 	trang -I rnc -O rng schema/op-mode-definition.rnc schema/op-mode-definition.rng
diff --git a/data/templates/load-balancing/wlb.conf.j2 b/data/templates/load-balancing/wlb.conf.j2
new file mode 100644
index 000000000..d3326b6b8
--- /dev/null
+++ b/data/templates/load-balancing/wlb.conf.j2
@@ -0,0 +1,130 @@
+# Generated by /usr/libexec/vyos/conf_mode/load-balancing-wan.py
+
+{% if disable_source_nat is vyos_defined %}
+disable-source-nat
+{% endif %}
+{% if enable_local_traffic is vyos_defined %}
+enable-local-traffic
+{% endif %}
+{% if sticky_connections is vyos_defined %}
+sticky-connections inbound
+{% endif %}
+{% if flush_connections is vyos_defined %}
+flush-conntrack
+{% endif %}
+{% if hook is vyos_defined %}
+hook "{{ hook }}"
+{% endif %}
+{% if interface_health is vyos_defined %}
+health {
+{%     for interface, interface_config in interface_health.items() %}
+    interface {{ interface }} {
+{%         if interface_config.failure_count is vyos_defined %}
+        failure-ct  {{ interface_config.failure_count }}
+{%         endif %}
+{%         if interface_config.success_count is vyos_defined %}
+        success-ct  {{ interface_config.success_count }}
+{%         endif %}
+{%         if interface_config.nexthop is vyos_defined %}
+        nexthop {{ interface_config.nexthop }}
+{%         endif %}
+{%         if interface_config.test is vyos_defined %}
+{%             for test_rule, test_config in interface_config.test.items() %}
+        rule {{ test_rule }} {
+{%                 if test_config.type is vyos_defined %}
+{%                     set type_translate = {'ping': 'ping', 'ttl': 'udp', 'user-defined': 'user-defined'} %}
+            type {{ type_translate[test_config.type] }} {
+{%                     if test_config.ttl_limit is vyos_defined and test_config.type == 'ttl' %}
+                ttl {{ test_config.ttl_limit }}
+{%                     endif %}
+{%                     if test_config.test_script is vyos_defined and test_config.type == 'user-defined' %}
+                test-script {{ test_config.test_script }}
+{%                     endif %}
+{%                     if test_config.target is vyos_defined %}
+                target {{ test_config.target }} 
+{%                     endif %}
+                resp-time {{ test_config.resp_time | int * 1000 }}
+            }
+{%                 endif %}
+        }
+{%             endfor %}
+{%         endif %}
+    }
+{%     endfor %}
+}
+{% endif %}
+
+{% if rule is vyos_defined %}
+{%     for rule, rule_config in rule.items() %}
+rule {{ rule }} {
+{%         if rule_config.exclude is vyos_defined  %}
+    exclude
+{%         endif %}
+{%         if rule_config.failover is vyos_defined  %}
+    failover
+{%         endif %}
+{%         if rule_config.limit is vyos_defined %}
+    limit {
+{%             if rule_config.limit.burst is vyos_defined %}
+        burst {{ rule_config.limit.burst }}
+{%             endif %}
+{%             if rule_config.limit.rate is vyos_defined %}
+        rate {{ rule_config.limit.rate }}
+{%             endif %}
+{%             if rule_config.limit.period is vyos_defined %}
+        period {{ rule_config.limit.period }}
+{%             endif %}
+{%             if rule_config.limit.threshold is vyos_defined %}
+        thresh {{ rule_config.limit.threshold }}
+{%             endif %}
+        }
+{%         endif %}
+{%         if rule_config.per_packet_balancing is vyos_defined  %}
+    per-packet-balancing
+{%         endif %}
+{%         if rule_config.protocol is vyos_defined  %}
+    protocol {{ rule_config.protocol }}
+{%         endif %}
+{%         if rule_config.destination is vyos_defined %}
+    destination {
+{%             if rule_config.destination.address is vyos_defined  %}
+        address "{{ rule_config.destination.address }}"
+{%             endif %}
+{%             if rule_config.destination.port is vyos_defined  %}
+{%                 if '-' in rule_config.destination.port %}
+        port-ipt "-m multiport  --dports {{ rule_config.destination.port | replace('-', ':') }}"
+{%                 else %}
+        port-ipt " --dport {{ rule_config.destination.port }}"
+{%                 endif %}
+{%             endif %}
+    }
+{%         endif %}
+{%         if rule_config.source is vyos_defined %}
+    source {
+{%             if rule_config.source.address is vyos_defined  %}
+        address "{{ rule_config.source.address }}"
+{%             endif %}
+{%             if rule_config.source.port is vyos_defined  %}
+{%                 if '-' in rule_config.source.port %}
+        port-ipt "-m multiport  --sports {{ rule_config.source.port | replace('-', ':') }}"
+{%                 else %}
+        port.ipt " --sport {{ rule_config.source.port }}"
+{%                 endif %}
+{%             endif %}
+    }
+{%         endif %}
+{%         if rule_config.inbound_interface is vyos_defined  %}
+    inbound-interface {{ rule_config.inbound_interface }}
+{%         endif %}
+{%         if rule_config.interface is vyos_defined  %}
+{%             for interface, interface_config in rule_config.interface.items() %}
+    interface {{ interface }} {
+{%                 if interface_config.weight is vyos_defined %}
+        weight {{ interface_config.weight }}
+{%                 endif %}
+    }
+{%             endfor %}
+{%         endif %}
+}
+{%     endfor %}
+{% endif %}
diff --git a/interface-definitions/load-balancing-wan.xml.in b/interface-definitions/load-balancing-wan.xml.in
index c1d7e2c67..3a2c111ac 100644
--- a/interface-definitions/load-balancing-wan.xml.in
+++ b/interface-definitions/load-balancing-wan.xml.in
@@ -1,387 +1,398 @@
 <?xml version="1.0"?>
 <interfaceDefinition>
   <node name="load-balancing">
     <properties>
       <help>Configure load-balancing</help>
+      <priority>900</priority>
     </properties>
     <children>
       <node name="wan" owner="${vyos_conf_scripts_dir}/load-balancing-wan.py">
         <properties>
           <help>Configure Wide Area Network (WAN) load-balancing</help>
         </properties>
         <children>
           <leafNode name="disable-source-nat">
             <properties>
               <help>Disable source NAT rules from being configured for WAN load balancing</help>
               <valueless/>
             </properties>
           </leafNode>
           <leafNode name="enable-local-traffic">
             <properties>
               <help>Enable WAN load balancing for locally sourced traffic</help>
               <valueless/>
             </properties>
           </leafNode>
           <leafNode name="flush-connections">
             <properties>
               <help>Flush connection tracking tables on connection state change</help>
               <valueless/>
             </properties>
           </leafNode>
           <leafNode name="hook">
             <properties>
               <help>Script to be executed on interface status change</help>
               <valueHelp>
                 <format>txt</format>
                 <description>Script in /config/scripts</description>
               </valueHelp>
               <constraint>
                 <validator name="script"/>
               </constraint>
             </properties>
           </leafNode>
           <tagNode name="interface-health">
             <properties>
               <help>Interface name</help>
               <completionHelp>
                 <script>${vyos_completion_dir}/list_interfaces</script>
               </completionHelp>
             </properties>
             <children>
               <leafNode name="failure-count">
                 <properties>
                   <help>Failure count</help>
                   <valueHelp>
                     <format>u32:1-10</format>
                     <description>Failure count</description>
                   </valueHelp>
                   <constraint>
                     <validator name="numeric" argument="--range 1-10"/>
                   </constraint>
                 </properties>
+                <defaultValue>1</defaultValue>
               </leafNode>
               <leafNode name="nexthop">
                 <properties>
                   <help>Outbound interface nexthop address. Can be 'DHCP or IPv4 address' [REQUIRED]</help>
                   <completionHelp>
                     <list>dhcp</list>
                   </completionHelp>
                   <valueHelp>
                     <format>ipv4</format>
                     <description>Nexthop IP address</description>
                   </valueHelp>
                   <valueHelp>
                     <format>dhcp</format>
                     <description>Set the nexthop via DHCP</description>
                   </valueHelp>
                   <constraint>
                     <validator name="ipv4-address"/>
                     <regex>(dhcp)</regex>
                   </constraint>
                 </properties>
               </leafNode>
               <leafNode name="success-count">
                 <properties>
                   <help>Success count</help>
                   <valueHelp>
                     <format>u32:1-10</format>
                     <description>Success count</description>
                   </valueHelp>
                   <constraint>
                     <validator name="numeric" argument="--range 1-10"/>
                   </constraint>
                 </properties>
+                <defaultValue>1</defaultValue>
               </leafNode>
               <tagNode name="test">
                 <properties>
                   <help>Rule number</help>
                   <valueHelp>
                     <format>u32:0-4294967295</format>
                     <description>Rule number</description>
                   </valueHelp>
                   <constraint>
                     <validator name="numeric" argument="--range 0-4294967295"/>
                   </constraint>
                 </properties>
                 <children>
                   <leafNode name="resp-time">
                     <properties>
                       <help>Ping response time (seconds)</help>
                       <valueHelp>
                         <format>u32:1-30</format>
                         <description>Response time (seconds)</description>
                       </valueHelp>
                       <constraint>
                         <validator name="numeric" argument="--range 1-30"/>
                       </constraint>
                     </properties>
+                    <defaultValue>5</defaultValue>
                   </leafNode>
                   <leafNode name="target">
                     <properties>
                       <help>Health target address</help>
                       <valueHelp>
                         <format>ipv4</format>
                         <description>Health target address</description>
                       </valueHelp>
                       <constraint>
                         <validator name="ipv4-address"/>
                       </constraint>
                     </properties>
                   </leafNode>
                   <leafNode name="test-script">
                     <properties>
                       <help>Path to user-defined script</help>
                       <valueHelp>
                         <format>txt</format>
                         <description>Script in /config/scripts</description>
                       </valueHelp>
                       <constraint>
                         <validator name="script"/>
                       </constraint>
                     </properties>
                   </leafNode>
                   <leafNode name="ttl-limit">
                     <properties>
                       <help>TTL limit (hop count)</help>
                       <valueHelp>
                         <format>u32:1-254</format>
                         <description>Number of hops</description>
                       </valueHelp>
                       <constraint>
                         <validator name="numeric" argument="--range 1-254"/>
                       </constraint>
                     </properties>
+                    <defaultValue>1</defaultValue>
                   </leafNode>
                   <leafNode name="type">
                     <properties>
                       <help>WLB test type</help>
                       <completionHelp>
                         <list>ping ttl user-defined</list>
                       </completionHelp>
                       <valueHelp>
                         <format>ping</format>
                         <description>Test with ICMP echo response</description>
                       </valueHelp>
                       <valueHelp>
                         <format>ttl</format>
                         <description>Test with UDP TTL expired response</description>
                       </valueHelp>
                       <valueHelp>
                         <format>user-defined</format>
                         <description>User-defined test script</description>
                       </valueHelp>
                       <constraint>
                         <regex>(ping|ttl|user-defined)</regex>
                       </constraint>
                     </properties>
                   </leafNode>
                 </children>
               </tagNode>
             </children>
           </tagNode>
           <tagNode name="rule">
             <properties>
               <help>Rule number (1-9999)</help>
               <valueHelp>
                 <format>u32:1-9999</format>
                 <description>Rule number</description>
               </valueHelp>
               <constraint>
                 <validator name="numeric" argument="--range 1-9999"/>
               </constraint>
             </properties>
             <children>
               #include <include/generic-description.xml.i>
               <node name="destination">
                 <properties>
                   <help>Destination</help>
                 </properties>
                 <children>
                   #include <include/ipv4-address-prefix-range.xml.i>
                   #include <include/port-port-range.xml.i>
                 </children>
               </node>
               <leafNode name="exclude">
                 <properties>
                   <help>Exclude packets matching this rule from WAN load balance</help>
                   <valueless/>
                 </properties>
               </leafNode>
               <leafNode name="failover">
                 <properties>
                   <help>Enable failover for packets matching this rule from WAN load balance</help>
                   <valueless/>
                 </properties>
               </leafNode>
               <leafNode name="inbound-interface">
                 <properties>
                   <help>Inbound interface name (e.g., "eth0") [REQUIRED]</help>
                   <completionHelp>
                     <list>any</list>
                     <script>${vyos_completion_dir}/list_interfaces</script>
                   </completionHelp>
                 </properties>
               </leafNode>
               <tagNode name="interface">
                 <properties>
                   <help>Interface name [REQUIRED]</help>
                   <completionHelp>
                     <script>${vyos_completion_dir}/list_interfaces</script>
                   </completionHelp>
                 </properties>
                 <children>
                   <leafNode name="weight">
                     <properties>
                       <help>Load-balance weight</help>
                       <valueHelp>
                         <format>u32:1-255</format>
                         <description>Interface weight</description>
                       </valueHelp>
                       <constraint>
                         <validator name="numeric" argument="--range 1-255"/>
                       </constraint>
                       <constraintErrorMessage>Weight must be between 1 and 255</constraintErrorMessage>
                     </properties>
+                    <defaultValue>1</defaultValue>
                   </leafNode>
                 </children>
               </tagNode>
               <node name="limit">
                 <properties>
                   <help>Enable packet limit for this rule</help>
                 </properties>
                 <children>
                   <leafNode name="burst">
                     <properties>
                       <help>Burst limit for matching packets</help>
                       <valueHelp>
                         <format>u32:0-4294967295</format>
                         <description>Burst limit for matching packets</description>
                       </valueHelp>
                       <constraint>
                         <validator name="numeric" argument="--range 0-4294967295"/>
                       </constraint>
                     </properties>
+                    <defaultValue>5</defaultValue>
                   </leafNode>
                   <leafNode name="period">
                     <properties>
                       <help>Time window for rate calculation</help>
                       <completionHelp>
                         <list>hour minute second</list>
                       </completionHelp>
                       <valueHelp>
                         <format>hour</format>
                         <description>hour</description>
                       </valueHelp>
                       <valueHelp>
                         <format>minute</format>
                         <description>minute</description>
                       </valueHelp>
                       <valueHelp>
                         <format>second</format>
                         <description>second</description>
                       </valueHelp>
                       <constraint>
                         <regex>(hour|minute|second)</regex>
                       </constraint>
                     </properties>
+                    <defaultValue>second</defaultValue>
                   </leafNode>
                   <leafNode name="rate">
                     <properties>
                       <help>Number of packets used for rate limit</help>
                       <valueHelp>
                         <format>u32:0-4294967295</format>
                         <description>Number of packets used for rate limit</description>
                       </valueHelp>
                       <constraint>
                         <validator name="numeric" argument="--range 0-4294967295"/>
                       </constraint>
                     </properties>
+                    <defaultValue>5</defaultValue>
                   </leafNode>
                   <leafNode name="threshold">
                     <properties>
                       <help>Threshold behavior for limit</help>
                       <completionHelp>
                         <list>above below</list>
                       </completionHelp>
                       <valueHelp>
                         <format>above</format>
                         <description>Above limit</description>
                       </valueHelp>
                       <valueHelp>
                         <format>below</format>
                         <description>Below limit</description>
                       </valueHelp>
                       <constraint>
                         <regex>(above|below)</regex>
                       </constraint>
                     </properties>
+                    <defaultValue>below</defaultValue>
                   </leafNode>
                 </children>
               </node>
               <leafNode name="per-packet-balancing">
                 <properties>
                   <help>Option to match traffic per-packet instead of the default, per-flow</help>
                   <valueless/>
                 </properties>
               </leafNode>
               <leafNode name="protocol">
                 <properties>
                   <help>Protocol to match (protocol name, number, or "all")</help>
                   <completionHelp>
                     <script>${vyos_completion_dir}/list_protocols.sh</script>
                     <list>all tcp_udp</list>
                   </completionHelp>
                   <valueHelp>
                     <format>all</format>
                     <description>All IP protocols</description>
                   </valueHelp>
                   <valueHelp>
                     <format>tcp_udp</format>
                     <description>Both TCP and UDP</description>
                   </valueHelp>
                   <valueHelp>
                     <format>u32:0-255</format>
                     <description>IP protocol number</description>
                   </valueHelp>
                   <valueHelp>
                     <format>&lt;protocol&gt;</format>
                     <description>IP protocol name</description>
                   </valueHelp>
                   <valueHelp>
                     <format>!&lt;protocol&gt;</format>
                     <description>IP protocol name</description>
                   </valueHelp>
                   <constraint>
                     <validator name="ip-protocol"/>
                   </constraint>
                 </properties>
+                <defaultValue>all</defaultValue>
               </leafNode>
               <node name="source">
                 <properties>
                   <help>Source information</help>
                 </properties>
                 <children>
                   #include <include/ipv4-address-prefix-range.xml.i>
                   #include <include/port-port-range.xml.i>
                 </children>
               </node>
             </children>
           </tagNode>
           <node name="sticky-connections">
             <properties>
               <help>Configure sticky connections</help>
             </properties>
             <children>
               <leafNode name="inbound">
                 <properties>
                   <help>Enable sticky incoming WAN connections</help>
                   <valueless/>
                 </properties>
               </leafNode>
             </children>
           </node>
         </children>
       </node>
     </children>
   </node>
 </interfaceDefinition>
diff --git a/src/conf_mode/load-balancing-wan.py b/src/conf_mode/load-balancing-wan.py
index 11840249f..2f0cf1293 100755
--- a/src/conf_mode/load-balancing-wan.py
+++ b/src/conf_mode/load-balancing-wan.py
@@ -1,65 +1,180 @@
 #!/usr/bin/env python3
 #
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2023 VyOS maintainers and contributors
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License version 2 or later as
 # published by the Free Software Foundation.
 #
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+import os
 
 from sys import exit
+from shutil import rmtree
 
+from vyos.base import Warning
 from vyos.config import Config
-from vyos.configdict import node_changed
-from vyos.util import call
+from vyos.configdict import dict_merge
+from vyos.util import cmd
+from vyos.template import render
+from vyos.xml import defaults
 from vyos import ConfigError
-from pprint import pprint
 from vyos import airbag
 airbag.enable()
 
+load_balancing_dir = '/run/load-balance'
+load_balancing_conf_file = f'{load_balancing_dir}/wlb.conf'
+
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
 
     base = ['load-balancing', 'wan']
-    lb = conf.get_config_dict(base, get_first_key=True,
-                                       no_tag_node_value_mangle=True)
+    lb = conf.get_config_dict(base,
+                              get_first_key=True,
+                              key_mangling=('-', '_'),
+                              no_tag_node_value_mangle=True)
+
+    # We have gathered the dict representation of the CLI, but there are default
+    # options which we need to update into the dictionary retrived.
+    default_values = defaults(base)
+    # lb base default values can not be merged here - remove and add them later
+    if 'interface_health' in default_values:
+        del default_values['interface_health']
+    if 'rule' in default_values:
+        del default_values['rule']
+    lb = dict_merge(default_values, lb)
+
+    if 'interface_health' in lb:
+        for iface in lb.get('interface_health'):
+            default_values_iface = defaults(base + ['interface-health'])
+            if 'test' in default_values_iface:
+                del default_values_iface['test']
+            lb['interface_health'][iface] = dict_merge(
+                default_values_iface, lb['interface_health'][iface])
+            if 'test' in lb['interface_health'][iface]:
+                for node_test in lb['interface_health'][iface]['test']:
+                    default_values_test = defaults(base +
+                                                   ['interface-health', 'test'])
+                    lb['interface_health'][iface]['test'][node_test] = dict_merge(
+                            default_values_test,
+                            lb['interface_health'][iface]['test'][node_test])
+
+    if 'rule' in lb:
+        for rule in lb.get('rule'):
+            default_values_rule = defaults(base + ['rule'])
+            if 'interface' in default_values_rule:
+                del default_values_rule['interface']
+            lb['rule'][rule] = dict_merge(default_values_rule, lb['rule'][rule])
+            if not conf.exists(base + ['rule', rule, 'limit']):
+                del lb['rule'][rule]['limit']
+            if 'interface' in lb['rule'][rule]:
+                for iface in lb['rule'][rule]['interface']:
+                    default_values_rule_iface = defaults(base + ['rule', 'interface'])
+                    lb['rule'][rule]['interface'][iface] = dict_merge(default_values_rule_iface, lb['rule'][rule]['interface'][iface])
 
-    pprint(lb)
     return lb
 
+
 def verify(lb):
-    return None
+    if not lb:
+        return None
+
+    if 'interface_health' not in lb:
+        raise ConfigError(
+            'A valid WAN load-balance configuration requires an interface with a nexthop!'
+        )
+
+    for interface, interface_config in lb['interface_health'].items():
+        if 'nexthop' not in interface_config:
+            raise ConfigError(
+                f'interface-health {interface} nexthop must be specified!')
+
+        if 'test' in interface_config:
+            for test_rule, test_config in interface_config['test'].items():
+                if 'type' in test_config:
+                    if test_config['type'] == 'user-defined' and 'test_script' not in test_config:
+                        raise ConfigError(
+                            f'test {test_rule} script must be defined for test-script!'
+                        )
+
+    if 'rule' not in lb:
+        Warning(
+            'At least one rule with an (outbound) interface must be defined for WAN load balancing to be active!'
+        )
+    else:
+        for rule, rule_config in lb['rule'].items():
+            if 'inbound_interface' not in rule_config:
+                raise ConfigError(f'rule {rule} inbound-interface must be specified!')
+            if {'failover', 'exclude'} <= set(rule_config):
+                raise ConfigError(f'rule {rule} failover cannot be configured with exclude!')
+            if {'limit', 'exclude'} <= set(rule_config):
+                raise ConfigError(f'rule {rule} limit cannot be used with exclude!')
+            if 'interface' not in rule_config:
+                if 'exclude' not in rule_config:
+                    Warning(
+                        f'rule {rule} will be inactive because no (outbound) interfaces have been defined for this rule'
+                    )
+            for direction in {'source', 'destination'}:
+                if direction in rule_config:
+                    if 'protocol' in rule_config and 'port' in rule_config[
+                            direction]:
+                        if rule_config['protocol'] not in {'tcp', 'udp'}:
+                            raise ConfigError('ports can only be specified when protocol is "tcp" or "udp"')
 
 
 def generate(lb):
     if not lb:
+        # Delete /run/load-balance/wlb.conf
+        if os.path.isfile(load_balancing_conf_file):
+            os.unlink(load_balancing_conf_file)
+        # Delete old directories
+        if os.path.isdir(load_balancing_dir):
+            rmtree(load_balancing_dir, ignore_errors=True)
+        if os.path.exists('/var/run/load-balance/wlb.out'):
+            os.unlink('/var/run/load-balance/wlb.out')
+
         return None
 
+    # Create load-balance dir
+    if not os.path.isdir(load_balancing_dir):
+        os.mkdir(load_balancing_dir)
+
+    render(load_balancing_conf_file, 'load-balancing/wlb.conf.j2', lb)
+
     return None
 
 
 def apply(lb):
+    if not lb:
+        try:
+            cmd('sudo /opt/vyatta/sbin/vyatta-wanloadbalance.init stop')
+        except Exception as e:
+            print(f"Error message: {e}")
+
+    else:
+        cmd('sudo sysctl -w net.netfilter.nf_conntrack_acct=1')
+        cmd(f'sudo /opt/vyatta/sbin/vyatta-wanloadbalance.init restart {load_balancing_conf_file}')
 
     return None
 
+
 if __name__ == '__main__':
     try:
         c = get_config()
         verify(c)
         generate(c)
         apply(c)
     except ConfigError as e:
         print(e)
         exit(1)