from parallels.core import safe_format, messages, get_logger
from parallels.core.utils.base64_utils import base64_decode
from parallels.core.utils.common import is_empty
from parallels.core.utils.common.xml import text_elem
from parallels.core.utils.entity import Entity


logger = get_logger(__name__)


class Password(Entity):
    """
    :type _password_type: str | unicode
    :type _password_text: str | unicode
    """
    def __init__(self, password_type, password_text):
        """
        :type password_type: str | unicode
        :type password_text: str | unicode
        """
        self._password_type = password_type
        self._password_text = password_text

    @property
    def password_type(self):
        return self._password_type

    @property
    def password_text(self):
        return self._password_text


def get_password(xml_node):
    """Get password from xml node
    If password is empty, the password type will be ignored and empty plain password will be returned
    :type xml_node: xml.etree.ElementTree.Element
    :rtype: parallels.core.dump.entity.password.Password
    """
    password_node = xml_node.find('password')

    empty_plain = Password(password_text='', password_type='plain')

    # return empty password as plain ''
    if password_node is None or password_node.text is None:
        return empty_plain

    # if password type is not defined, decide it is a plain password
    if 'type' not in password_node.attrib:
        return Password(password_text=password_node.text, password_type='plain')

    # if password type plain and password encoded with base64, try to decode it
    if password_node.attrib['type'] == 'plain' and password_node.attrib.get('encoding') == 'base64':
        try:
            return Password('plain', base64_decode(password_node.text))
        except Exception as e:
            logger.exception()
            logger.warning(safe_format(messages.FAILED_TO_DECODE_PASSWORD, error=e))
            return empty_plain

    return Password(password_node.attrib['type'], password_node.text)


def set_password(xml_node, password):
    """Set password to xml node
    Please note if password node does not exist inside of passed XML node
    it will be created as the first node in a sequence.
    The order of nodes matters for Plesk.

    :type xml_node: xml.etree.ElementTree.Element
    :type password: parallels.core.dump.entity.password.Password
    """
    assert isinstance(password, Password)
    password_node = xml_node.find('password')
    if password_node is not None:
        xml_node.remove(password_node)
    new_password_node = text_elem('password', password.password_text, {'type': password.password_type})
    xml_node.insert(0, new_password_node)


def password_is_empty(password):
    """
    :type password: parallels.core.dump.entity.password.Password | None
    :rtype: bool
    """
    return password is None or is_empty(password.password_text)
