import logging
from collections import namedtuple
import xml.etree.ElementTree as et

from parallels.utils import ip, format_list
from parallels.move_subscriptions.ppa_data import SubscriptionNotFoundException

MoveList = namedtuple('MoveList', ('web', 'mail', 'db_mysql',))

logger = logging.getLogger(__name__)

def read_move_list(fileobj, ppa_data, db_mysql_hosts):
	"""Read move list from fileobj, return MoveList object, where:
- 'web' is a dict with keys - domain names and values - target service node IP
- 'mail' is a dict with keys - domain names and values - target service node IP
- 'db_mysql' is a dict with keys - domain names and values - target hostnames (not IPs)

Example of move list XML: 
<move>
	<domain name="test.tld">
		<web>10.58.1.1</web>
		<mail>10.58.1.3</mail>
		<db-mysql>localhost</db-mysql>
	</domain>
	<domain name="domain.tld">
		<web>10.58.1.2</web>
		<mail>10.58.1.4</mail>
		<db-mysql>192.168.1.1</db-mysql>
	</domain>
</move>
"""

	move_list = MoveList(web={}, mail = {}, db_mysql={})
	errors = []

	try:
		root_node = et.fromstring(fileobj.read())
	except Exception as err:
		logger.debug(u"Exception:", exc_info=err)
		errors.append("Failed to read move list file. Make sure it is a valid XML. Exception message: %s" % (str(err),))
	else:
		for domain_xml_node in root_node:
			if domain_xml_node.tag == 'domain':
				domain_name = domain_xml_node.attrib.get('name')
				if domain_name is not None:
					if domain_name.strip() != '':
						for service_xml_node in domain_xml_node:
							if service_xml_node.tag == 'web':
								try:
									ppa_data.get_subscription_info(domain_name)
								except SubscriptionNotFoundException:
									errors.append("Web service of a subscription or whole subscription '%s' does not exist on PPA" % (domain_name,))
								else:
									target_ip = service_xml_node.text
									if target_ip is not None:
										if ip.is_ipv4(target_ip) or ip.is_ipv6(target_ip):
											move_list.web[domain_name] = target_ip
										else:
											errors.append("Target web service node IP '%s' specified for domain '%s' is not valid" % (target_ip, domain_name,))
									else:
										errors.append("Target web service node IP is not specified for domain '%s'" % (domain_name,))
							elif service_xml_node.tag == 'mail':
								try:
									ppa_data.get_subscription_info(domain_name)
								except SubscriptionNotFoundException:
									errors.append("Mail service of a subscription or whole subscription '%s' does not exist on PPA" % (domain_name,))
								else:
									target_ip = service_xml_node.text
									if target_ip is not None:
										if ip.is_ipv4(target_ip) or ip.is_ipv6(target_ip):
											move_list.mail[domain_name] = target_ip
										else:
											errors.append("Target mail service node IP '%s' specified for domain '%s' is not valid" % (target_ip, domain_name,))
									else:
										errors.append("Target mail service node IP is not specified for domain '%s'" % (domain_name,))
							elif service_xml_node.tag == 'db-mysql':
								target_host = service_xml_node.text
								if target_host is not None and target_host.strip() != '':
									if target_host in db_mysql_hosts:
										move_list.db_mysql[domain_name] = service_xml_node.text
									else:
										errors.append("Target MySQL database service host '%s' does not exist on PPA. Available hosts: %s" % (target_host, format_list(db_mysql_hosts)))
								else:
									errors.append("Target database service node hostname is not specified for domain '%s'" % (domain_name,))
							else:
								errors.append("Unknown service to migrate: <%s>" % (service_xml_node.tag))
					else:
						errors.append("Attribute 'name' of <domain> node can not be empty")
				else:
					errors.append("Attribute 'name' is not set for <domain> node, but must be set for all <domain> nodes in move list")
			else:
				errors.append('Unknown node <%s>: expected <domain> node' % (domain_xml_node.tag,))

	return move_list, errors

def filter_move_list(move_list, subscriptions):
	return MoveList(
		web={subscription: ip for subscription, ip in move_list.web.iteritems() if subscription in subscriptions},
		mail={subscription: ip for subscription, ip in move_list.mail.iteritems() if subscription in subscriptions},
		db_mysql={subscription: host for subscription, host in move_list.db_mysql.iteritems() if subscription in subscriptions}
	)

