from parallels.common import messages
import logging
import re
from xml.etree import ElementTree
from parallels.common.utils.pmm.ignored_pmm_messages import ignored_codes, ignored_descriptions

logger = logging.getLogger(__name__)

class RestoreResult(object):
	"""Object that represents Plesk restore result XML"""

	def __init__(self, domain_name, plesk_restore_stdout):
		self._response_xml = self._extract_xml_from_stdout(
			plesk_restore_stdout
		)
		self._domain_name = domain_name

	@staticmethod
	def _extract_xml_from_stdout(plesk_restore_stdout):
		"""Get XML out of Plesk restore stdout.

		Returns:
			XML object, if response contains one. Otherwise return None.
		"""
		start_xml_declaration = '<?xml version="1.0" encoding="UTF-8"?>'
		start_xml_declaration_pos = plesk_restore_stdout.find(start_xml_declaration)
		end_tag = "</restore>"
		end_tag_pos = plesk_restore_stdout.find(end_tag)
		if start_xml_declaration_pos != -1 and end_tag_pos != -1:
			xml_str_content = plesk_restore_stdout[
				start_xml_declaration_pos:end_tag_pos+len(end_tag)
			].strip()
			try:
				return ElementTree.fromstring(xml_str_content.encode('utf-8'))
			except Exception as e:
				logger.debug(messages.ERROR_WHILE_PARSING_PLESK_RESTORE_XML, exc_info=e)
				return None
		else:
			return None

	def has_errors(self):
		"""Check if there were restoration error (some known errors are ignored)
		
		Args:
			response: stdout of 'pleskrestore' command
			domain_name: the name of the domain specified in arguments for
			'pleskrestore'
		Returns:
			'True', if errors are found, 'False' otherwise.
		"""

		if self._response_xml is None:
			# no valid response XML -> no errors
			return False

		for message_object in self._response_xml.findall('.//message'):
			if self._is_ignore_message(message_object):
				continue
			if self._is_error_message(message_object):
				return True

		return False

	def _is_ignore_message(self, message_object):
		if message_object.attrib.get('code') in ignored_codes:
			return True
		elif message_object.attrib.get('code') == 'resolver' \
			and message_object.attrib.get('id') == 'component_not_installed_spam_assassin':
			# spamassassin is not available now, but was available before -
			# we do not want to spam customer with such errors
			return True

		return False

	def _is_error_message(self, message_object):
		is_error = True
		for ignored_description in ignored_descriptions:
			if message_object.find('description').text is not None:
				if re.search(ignored_description, message_object.find('description').text) is not None:
					is_error = False

		return is_error

	def get_error_messages(self, error=True):
		"""Get restoration error messages, enhance some of them

		Enhance message wording, when it is too cryptic or too verbose.
		"""
		replacements = {
			"It will be disabled for the following objects: %s" % (self._domain_name,):
				"This feature will be disabled.",
			'Backup file has wrong signature for this server': '',
			'managed_runtime_version': '.NET runtime'
		}

		# find messages in XML
		messages_raw = []
		if self._response_xml is not None:
			for message_node in self._response_xml.findall('.//message'):
				if self._is_ignore_message(message_node):
					continue
				if self._is_error_message(message_node) is error:
					messages_raw.append(
						message_node.find('description').text.strip()
					)

		# perform replacements
		restore_messages = []
		for message in messages_raw:
			for replace_me, new_text in replacements.iteritems():
				message = message.replace(replace_me, new_text)
			restore_messages.append(message)

		return restore_messages
