"""Utility functions to transfer settings of virtual directories"""

from xml.etree import ElementTree

from parallels.utils.xml import elem
from parallels.utils.xml import replace_root_node_if_name_matches


class VirtualDirectoryXMLConverter(object):
	"""Converts virtual directory XML file to get ready for import

	Could change paths in XML according to target layout,
	remove reserved virtual directories
	(virtual directories that should not be transferred)
	and replace "<vhost>" node with "<vdir>" node so XML
	could be passed to Plesk CLI utilities
	"""
	def __init__(self):
		self.reserved_vdirs = None
		self.path_convert_function = None

	def set_reserved_vdirs(self, reserved_vdirs):
		self.reserved_vdirs = reserved_vdirs

	def set_path_convert_function(self, path_convert_function):
		self.path_convert_function = path_convert_function

	def convert(self, subscription, site, xml_str):
		vdir_xml = ElementTree.XML(xml_str)
		vdir_xml = self._replace_vhost_with_vdir(vdir_xml)

		if self.path_convert_function is not None:
			self._convert_paths(subscription, site, vdir_xml, self.path_convert_function)
		if self.reserved_vdirs is not None:
			self._remove_reserved_vdirs(vdir_xml)

		return ElementTree.tostring(vdir_xml, 'utf-8', 'xml')

	@staticmethod
	def _convert_paths(subscription, site, xml, path_convert_function):
		"""

		Arguments:
		- path_convert_function - function with signature path_convert_function(subscription, site, vdir_path),
		where vdir_path is a path of a virtual directory in source virtual directory XML. The function should return
		corresponding path to the provided directory on the target server.
		"""

		# logic of XML conversion is taken from Plesk's RestoreVirtualDirectories function at windows/binary/plesk-utils/PMM/psadumpagent/RestoreEnv.cs
		# considering some things in Plesk's code are already not necessary or look buggy

		def fix_path_recursive(node):
			if node.tag in ['vdir', 'vhost'] and 'path' in node.attrib:
				node.attrib['path'] = path_convert_function(
					subscription,
					site,
					node.attrib['path'],
				)
			for child in node:
				fix_path_recursive(child)

		fix_path_recursive(xml)

	def _remove_reserved_vdirs(self, vdir_xml):
		"""Remove information about auto-generated virtual directories"""
		vdirs_node = vdir_xml.find('vdirs')
		if vdirs_node is not None:
			for reserved_vdir in self.reserved_vdirs:
				nodes_to_remove = list(vdirs_node.findall('vdir[@name="%s"]' % (reserved_vdir,)))
				for node_to_remove in nodes_to_remove:
					vdirs_node.remove(node_to_remove)

	@staticmethod
	def _replace_vhost_with_vdir(vdir_xml):
		"""In the vhost description xml file, replace vhost element with vdir element.
		"""
		root_vdir = elem('vdir')
		for k, v in vdir_xml.attrib.iteritems():
			root_vdir.attrib[k] = v
		root_vdir.attrib['name'] = ''  # name of the root vdir node should be an empty string - that is a requirement of Plesk

		return replace_root_node_if_name_matches(vdir_xml, 'vhost', root_vdir)


def get_vdirs_list(vdirs_xml_str):
	vdir_xml = ElementTree.XML(vdirs_xml_str.encode('utf-8'))
	vdirs_list = set()
	for node in vdir_xml:
		vdirs_list.update(
			_get_node_vdirs_list(node, None)
		)
	return vdirs_list


def _get_node_vdirs_list(node, parent_name):
	vdirs_list = []
	if node.tag == 'vdir' and 'name' in node.attrib:
		if parent_name is not None:
			vdir_name = parent_name + '\\' + node.attrib['name']
		else:
			vdir_name = node.attrib['name']
		vdirs_list.append(vdir_name)
	else:
		vdir_name = parent_name

	for child in node:
		vdirs_list.extend(
			_get_node_vdirs_list(child, vdir_name)
		)
	return set(vdirs_list)
