import json
import ipaddress


def add_host_with_net(host, add_host_with_interface, add_network):
	"""Adds a host with a network. The first network this adds defaults to pxe=True."""
	def _inner(hostname, rack, rank, appliance, interface, ip, network, address, pxe):
		# Add the host with an interface.
		add_host_with_interface(hostname = hostname, rack = rack, rank = rank, appliance = appliance, interface = interface)

		# Add the network.
		add_network(name = network, address = address, pxe = pxe)

		# Associate it to the interface.
		result = host.run(f"stack set host interface network {hostname} network={network} interface={interface}")
		assert result.rc == 0

		# Set the interface IP
		result = host.run(f"stack set host interface ip {hostname} ip={ip} network={network}")
		assert result.rc == 0

		# Add it to the frontend, because a lot of things in stacki expect backends to share networks with
		# frontends.
		result = host.run("stack list host interface a:frontend output-format=json")
		assert result.rc == 0
		# Try to figure out if the frontend has an interface on this network already.
		interface_on_network = False
		for frontend_interface in json.loads(result.stdout):
			if frontend_interface["network"] == network:
				interface_on_network = True
				break

		if interface_on_network:
			return

		# Need to add an interface to the frontend on this network. Make sure we choose the next latest
		# interface name so we don't clash with other interface names.
		latest_interface = max(frontend_interface["interface"] for frontend_interface in json.loads(result.stdout))
		# This should be a string, so we tokenize it into characters
		new_interface = list(latest_interface)
		new_interface[-1] = str(int(new_interface[-1]) + 1)
		new_interface = "".join(new_interface)
		result = host.run(f"stack add host interface a:frontend interface={new_interface} network={network} ip={ipaddress.ip_address(ip) + 1}")
		assert result.rc == 0

	# First use of the add_host_with_interface fixture adds backend-0-0 with interface eth0.
	# The first use of add_network adds a network called test, but that's not PXE so we don't want to use it.
	# So the first call of this fixture needs to remove the test network, recreate it as a PXE network, and
	# associate the network with the host's interface.
	result = host.run(f"stack remove network test")
	assert result.rc == 0
	add_network(name = "test", address = "192.168.0.0", pxe = True)
	result = host.run(f"stack set host interface network backend-0-0 network=test interface=eth0 ip=192.168.0.3")
	assert result.rc == 0

	# Add a frontend interface on the network.
	result = host.run(f"stack add host interface a:frontend interface=eth2 network=test ip=192.168.0.2")
	assert result.rc == 0

	return _inner