from typing import Any, Mapping, Optional, Tuple

from zerver.lib.exceptions import UnsupportedWebhookEventType

SUPPORTED_BOARD_ACTIONS = [
    "removeMemberFromBoard",
    "addMemberToBoard",
    "createList",
    "updateBoard",
]

REMOVE_MEMBER = "removeMemberFromBoard"
ADD_MEMBER = "addMemberToBoard"
CREATE_LIST = "createList"
CHANGE_NAME = "changeName"

TRELLO_BOARD_URL_TEMPLATE = "[{board_name}]({board_url})"

ACTIONS_TO_MESSAGE_MAPPER = {
    REMOVE_MEMBER: "removed {member_name} from {board_url_template}.",
    ADD_MEMBER: "added {member_name} to {board_url_template}.",
    CREATE_LIST: "added {list_name} list to {board_url_template}.",
    CHANGE_NAME: "renamed the board from {old_name} to {board_url_template}.",
}


def process_board_action(
    payload: Mapping[str, Any], action_type: Optional[str]
) -> Optional[Tuple[str, str]]:
    action_type = get_proper_action(payload, action_type)
    if action_type is not None:
        return get_subject(payload), get_body(payload, action_type)
    return None


def get_proper_action(payload: Mapping[str, Any], action_type: Optional[str]) -> Optional[str]:
    if action_type == "updateBoard":
        data = get_action_data(payload)
        # we don't support events for when a board's background
        # is changed
        if data["old"].get("prefs", {}).get("background") is not None:
            return None
        elif data["old"]["name"]:
            return CHANGE_NAME
        raise UnsupportedWebhookEventType(action_type)
    return action_type


def get_subject(payload: Mapping[str, Any]) -> str:
    return get_action_data(payload)["board"]["name"]


def get_body(payload: Mapping[str, Any], action_type: str) -> str:
    message_body = ACTIONS_TO_FILL_BODY_MAPPER[action_type](payload, action_type)
    creator = payload["action"]["memberCreator"]["fullName"]
    return f"{creator} {message_body}"


def get_managed_member_body(payload: Mapping[str, Any], action_type: str) -> str:
    data = {
        "member_name": payload["action"]["member"]["fullName"],
    }
    return fill_appropriate_message_content(payload, action_type, data)


def get_create_list_body(payload: Mapping[str, Any], action_type: str) -> str:
    data = {
        "list_name": get_action_data(payload)["list"]["name"],
    }
    return fill_appropriate_message_content(payload, action_type, data)


def get_change_name_body(payload: Mapping[str, Any], action_type: str) -> str:
    data = {
        "old_name": get_action_data(payload)["old"]["name"],
    }
    return fill_appropriate_message_content(payload, action_type, data)


def fill_appropriate_message_content(
    payload: Mapping[str, Any], action_type: str, data: Mapping[str, Any] = {}
) -> str:
    data = dict(data)
    if "board_url_template" not in data:
        data["board_url_template"] = get_filled_board_url_template(payload)
    message_body = get_message_body(action_type)
    return message_body.format(**data)


def get_filled_board_url_template(payload: Mapping[str, Any]) -> str:
    return TRELLO_BOARD_URL_TEMPLATE.format(
        board_name=get_board_name(payload), board_url=get_board_url(payload)
    )


def get_board_name(payload: Mapping[str, Any]) -> str:
    return get_action_data(payload)["board"]["name"]


def get_board_url(payload: Mapping[str, Any]) -> str:
    return "https://trello.com/b/{}".format(get_action_data(payload)["board"]["shortLink"])


def get_message_body(action_type: str) -> str:
    return ACTIONS_TO_MESSAGE_MAPPER[action_type]


def get_action_data(payload: Mapping[str, Any]) -> Mapping[str, Any]:
    return payload["action"]["data"]


ACTIONS_TO_FILL_BODY_MAPPER = {
    REMOVE_MEMBER: get_managed_member_body,
    ADD_MEMBER: get_managed_member_body,
    CREATE_LIST: get_create_list_body,
    CHANGE_NAME: get_change_name_body,
}
