from parallels.ppa_sn import messages
import logging
from collections import namedtuple
import xml.etree.ElementTree as et

from parallels.core.utils.common import ip, format_list
from parallels.ppa_sn.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(messages.LOG_EXCEPTION, exc_info=True)
		errors.append(messages.FAILED_TO_READ_MOVE_LIST_FILE % (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(messages.WEB_SERVICE_OF_SUBSCRIPTION_DOES_NOT_EXIST % (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(
												messages.WEB_SERVICE_NODE_IP_INVALID % (target_ip, domain_name,)
											)
									else:
										errors.append(
											messages.TARGET_WEB_SERVICE_NODE_IP_NOT_SPECIFIED % (domain_name,)
										)
							elif service_xml_node.tag == 'mail':
								try:
									ppa_data.get_subscription_info(domain_name)
								except SubscriptionNotFoundException:
									errors.append(messages.MAIL_SERVICE_DOES_NOT_EXIST % (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(
												messages.MAIL_SERVICE_NODE_IP_INVALID % (target_ip, domain_name,)
											)
									else:
										errors.append(messages.MAIL_SERVICE_NODE_IP_NOT_SPECIFIED % (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(
											messages.MYSQL_DATABASE_SERVICE_HOST_NOT_EXIST % (
												target_host, format_list(db_mysql_hosts)
											)
										)
								else:
									errors.append(
										messages.DATABASE_SERVICE_NODE_HOSTNAME_NOT_SPECIFIED % (domain_name,)
									)
							else:
								errors.append(messages.MOVE_LIST_UNKNOWN_SERVICE % service_xml_node.tag)
					else:
						errors.append(messages.ATTRIBUTE_NAME_OF_DOMAIN_NODE_CAN_NOT_BE_EMPTY)
				else:
					errors.append(messages.ATTRIBUTE_NAME_IS_NOT_SET)
			else:
				errors.append(messages.MOVE_LIST_UNKNOWN_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
		}
	)


