from xml.etree import ElementTree

from parallels.core.utils.common import default
from parallels.core.utils.pmm.ignored_pmm_messages import ignored_codes
from parallels.core import messages
from parallels.core.logging import get_logger

logger = get_logger(__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)
            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 means no errors
            return False
        for message_object in self._response_xml.findall('.//message'):
            if self._is_ignore_message(message_object):
                continue
            return True
        return False

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

        Enhance message wording, when it is too cryptic or too verbose.
        """
        if self._response_xml is None:
            # no valid response XML means no errors
            return []

        replacements = {
            "It will be disabled for the following objects: %s" % self._domain_name:
                messages.FEATURE_WILL_BE_DISABLED,
            'Backup file has wrong signature for this server': '',
            'managed_runtime_version': messages.DOT_NET_RUNTIME
        }

        # retrieve message nodes from response
        messages_raw = []
        for message_node in self._response_xml.findall('.//message'):
            if self._is_ignore_message(message_node):
                continue
            messages_raw.append(default(message_node.find('description').text, '').strip())

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

        return restore_messages

    @staticmethod
    def _is_ignore_message(message_node):
        if message_node.attrib.get('code') in ignored_codes:
            return True
        elif (
            message_node.attrib.get('code') == 'resolver' and
            message_node.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
