import sys
from xml.etree import ElementTree

from parallels.core.utils.common import default
from plesk_migrator.compatibility.utils import stringio


def clean_dict_args_for_log(args, dummy_fill='*'):
    """Take dictionary of arguments and replace all parameters that look like password with special text

    :type args: dict | None
    :type dummy_fill: str | unicode
    :rtype: dict
    """
    args = default(args, {})
    clean_args = dict(args)
    for key in clean_args:
        if _is_bad_string(key):
            clean_args[key] = dummy_text(dummy_fill)
    return clean_args


def clean_list_args_for_log(args, dummy_fill='*'):
    """Take list of arguments and replace all parameters that look like password with special text

    :type args: list | None
    :type dummy_fill: str | unicode
    :rtype: list
    """
    args = default(args, [])
    clean_args = args[:]
    for i, arg in enumerate(clean_args[1:], start=1):
        previous_arg = clean_args[i - 1]
        if _is_bad_string(previous_arg):
            clean_args[i] = dummy_text(dummy_fill)
    return clean_args


def clean_xml(xml_string):
    """Take XML as a string, return XML as a string, with nodes that look like password replaced with special text

    :type xml_string: str | unicode
    :rtype: unicode
    """
    def clean_node(node):
        if _is_bad_string(node.tag):
            node.text = dummy_text()

        if node.tag.lower() == 'property':
            name_node_text = node.findtext('name')
            if name_node_text is not None and _is_bad_string(name_node_text):
                value_node = node.find('value')
                if value_node is not None:
                    value_node.text = dummy_text()

        for child in node:
            clean_node(child)

    tree = ElementTree.ElementTree()
    tree.parse(stringio(xml_string, is_unicode=False))
    clean_node(tree.getroot())
    return ElementTree.tostring(tree.getroot(), 'utf-8', 'xml').decode('utf-8')


def dummy_text(dummy_fill='*'):
    """Get text which is a replacement for sensitive data

    :type dummy_fill: str | unicode
    :rtype: unicode
    """
    return u"%shidden%s" % (dummy_fill * 3, dummy_fill * 3)


def _is_bad_string(value):
    if sys.version_info.major >= 3:
        if not isinstance(value, bytes):
            return False
    else:
        if not isinstance(value, basestring):
            return False
    value = value.lower()
    if value in LOGGING_GOOD_WORDS:
        return False
    return any(stop_word in value for stop_word in LOGGING_BAD_WORDS)


LOGGING_BAD_WORDS = {'passwd', 'password', 'pwd', 'psa_password', 'ftp_password', 'mysql_pwd'}
LOGGING_GOOD_WORDS = {'allow_weak_passwords', 'ftp_password_type'}
