# Copyright (c) Facebook, Inc. and its affiliates.

# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

from __future__ import absolute_import, division, print_function, unicode_literals

from enum import Enum

from wadebug import results
from wadebug.wa_actions import docker_utils, network_utils
from wadebug.wa_actions.base import WAAction


class WA_SERVER_TYPE(Enum):
    WA_SERVER = "WhatsApp Server"
    WA_REPOSITORY = "WhatsApp Docker Repository"


DEFAULT_PORT = 5222
HTTPS_PORT = 443

WHATSAPP_SERVERS = [
    ("g.whatsapp.net", WA_SERVER_TYPE.WA_SERVER, DEFAULT_PORT, HTTPS_PORT),
    ("v.whatsapp.net", WA_SERVER_TYPE.WA_SERVER, HTTPS_PORT, DEFAULT_PORT),
    ("mmg.whatsapp.net", WA_SERVER_TYPE.WA_SERVER, HTTPS_PORT, DEFAULT_PORT),
    ("graph.facebook.com", WA_SERVER_TYPE.WA_SERVER, HTTPS_PORT, DEFAULT_PORT),
    ("docker.whatsapp.biz", WA_SERVER_TYPE.WA_REPOSITORY, HTTPS_PORT, DEFAULT_PORT),
    ("dt.bintray.com", WA_SERVER_TYPE.WA_REPOSITORY, HTTPS_PORT, DEFAULT_PORT),
    ("akamai.bintray.com", WA_SERVER_TYPE.WA_REPOSITORY, HTTPS_PORT, DEFAULT_PORT),
]

CONNECTION_TIMEOUT = 1


class CheckNetworkAction(WAAction):
    user_facing_name = "check_network"
    short_description = (
        "Test if required hosts can be reached on specific port "
        "from the first running coreapp container."
    )

    @classmethod
    def _run(cls, config, *args, **kwargs):
        wacore_containers = docker_utils.get_running_wacore_containers()

        if not wacore_containers:
            return results.Skipped(
                cls,
                "Network connectivity check was skipped",
                "There is no wacore container running. Action skipped.",
                "If this is unexpected, check results from other actions to diagnose. "
                "If this is expected (e.g.: an HA/MC setup "
                "across multiple hosts is under test), no actions are required.",
            )

        container = wacore_containers[0]

        hosts_not_reachable = get_hosts_not_reachable_from_container(
            container, WHATSAPP_SERVERS
        )

        TROUBLESHOOTING_URL = (
            "https://developers.facebook.com/docs/whatsapp/network-debugging"
        )

        if hosts_not_reachable:
            error_message = format_error_message(hosts_not_reachable)
            return results.Problem(
                cls,
                "Network connectivity check fails",
                error_message,
                "Please refer to {} for network requirements details.".format(
                    TROUBLESHOOTING_URL
                ),
            )

        return results.OK(cls)


def get_hosts_not_reachable_from_container(container, hosts):
    hosts_not_reachable = []

    for host in hosts:
        hostname, server_type, primary_port, secondary_port = host
        if not is_host_reachable_from_container(
            container, hostname, primary_port, secondary_port
        ):
            hosts_not_reachable.append((hostname, server_type))

    return hosts_not_reachable


def is_host_reachable_from_container(container, hostname, primary_port, secondary_port):
    return network_utils.hostname_reachable_from_container(
        container, hostname, primary_port, CONNECTION_TIMEOUT
    ) or network_utils.hostname_reachable_from_container(
        container, hostname, secondary_port, CONNECTION_TIMEOUT
    )


def format_error_message(hosts):
    message = ""
    for hostname, server_type in hosts:
        # error message to users doesn't need to differentiate primary/secondary
        # ports for different hosts
        message += "Cannot reach {} {} on port {} or {}.\n".format(
            server_type.value, hostname, DEFAULT_PORT, HTTPS_PORT
        )
    return message
