import base64
from collections import namedtuple

from pcs.lib import reports
from pcs.lib.errors import LibraryError

def create_pcmk_remote_actions(action_list):
    return {
        "pacemaker_remote {0}".format(action): service_cmd_format(
            "pacemaker_remote", action
        )
        for action in action_list
    }

def pcmk_authkey_format(authkey_content):
    """
    Return a dict usable in the communication with a remote/put_file
    authkey_content is raw authkey content
    """
    return {
        "data": base64.b64encode(authkey_content).decode("utf-8"),
        "type": "pcmk_remote_authkey",
        "rewrite_existing": True,
    }

def corosync_authkey_format(authkey_content):
    """
    Return a dict usable in the communication with a remote/put_file
    authkey_content is raw authkey content
    """
    return {
        "data": base64.b64encode(authkey_content).decode("utf-8"),
        "type": "corosync_authkey",
        "rewrite_existing": True,
    }

def pcmk_authkey_file(authkey_content):
    return {
        "pacemaker_remote authkey": pcmk_authkey_format(authkey_content)
    }

def corosync_authkey_file(authkey_content):
    return {
        "corosync authkey": corosync_authkey_format(authkey_content)
    }

def corosync_conf_format(corosync_conf_content):
    return {
        "type": "corosync_conf",
        "data": corosync_conf_content,
    }

def corosync_conf_file(corosync_conf_content):
    return {
        "corosync.conf": corosync_conf_format(corosync_conf_content)
    }

def pcs_settings_conf_format(content):
    return {
        "data": content,
        "type": "pcs_settings_conf",
        "rewrite_existing": True,
    }

def pcs_settings_conf_file(content):
    return {
        "pcs_settings.conf": pcs_settings_conf_format(content)
    }

def service_cmd_format(service, command):
    """
    Return a dict usable in the communication with a remote/run_action
    string service is name of requested service (eg. pacemaker_remote)
    string command specifies an action on service (eg. start)
    """
    return {
        "type": "service_command",
        "service": service,
        "command": command,
    }

class Result(namedtuple("Result", "code message")):
    """ Wrapper over some call results """

def unpack_items_from_response(main_response, main_key, node_label):
    """
    Check format of main_response and return main_response[main_key].
    dict main_response has on the key 'main_key' dict with item name as key and
        dict with result as value. E.g.
        {
            "files": {
                "file1": {"code": "success", "message": ""}
            }
        }
    string main_key is name of key under that is a dict with results
    string node_label is a node label for reporting an invalid format
    """
    is_in_expected_format = (
        isinstance(main_response, dict)
        and
        main_key in main_response
        and
        isinstance(main_response[main_key], dict)
    )

    if not is_in_expected_format:
        raise LibraryError(reports.invalid_response_format(node_label))

    return main_response[main_key]

def response_items_to_result(response_items, expected_keys, node_label):
    """
    Check format of response_items and return dict where keys are transformed to
    Result. E.g.
    {"file1": {"code": "success", "message": ""}}
    ->
    {"file1": Result("success", "")}}

    dict resposne_items has item name as key and dict with result as value.
    list expected_keys contains expected keys in a dict main_response[main_key]
    string node_label is a node label for reporting an invalid format
    """
    if set(expected_keys) != set(response_items.keys()):
        raise LibraryError(reports.invalid_response_format(node_label))

    for result in response_items.values():
        if(
            not isinstance(result, dict)
            or
            "code" not in result
            or
            "message" not in result
        ):
            raise LibraryError(reports.invalid_response_format(node_label))

    return {
        file_key: Result(raw_result["code"], raw_result["message"])
        for file_key, raw_result in response_items.items()
    }


def response_to_result(
    main_response, main_key, expected_keys, node_label
):
    """
    Validate response (from remote/put_file or remote/run_action) and transform
    results from dict to Result.

    dict main_response has on the key 'main_key' dict with item name as key and
        dict with result as value. E.g.
        {
            "files": {
                "file1": {"code": "success", "message": ""}
            }
        }
    string main_key is name of key under that is a dict with results
    list expected_keys contains expected keys in a dict main_response[main_key]
    string node_label is a node label for reporting an invalid format
    """
    return response_items_to_result(
        unpack_items_from_response(main_response, main_key, node_label),
        expected_keys,
        node_label
    )

def get_format_result(code_message_map):
    def format_result(result):
        if result.code in code_message_map:
            return code_message_map[result.code]

        return result.message
    return format_result
