import requests
from requests.exceptions import ConnectionError
from retrying import retry

try:
    from config import TANK_FINDER_HOST, VALIDATOR_HOST, logger
except ModuleNotFoundError:
    from .config import TANK_FINDER_HOST, VALIDATOR_HOST, logger

try:
    from load.projects.tankapi_cmd.src.client import LunaparkTank
except ModuleNotFoundError:
    from tankapi.client.client import LunaparkTank


def external_call(service, method, url, payload=None):
    error = ''
    result = {}

    @retry(stop_max_attempt_number=3)
    def _request(method, url, payload=None):
        resp = requests.request(method=method, url=url, json=payload, verify=False)
        return resp

    try:
        response = _request('GET', url) if method == 'GET' else _request('POST', url, payload)
        result = response.json()
    except (ConnectionError, ValueError):
        error = 'Reply from {} is not available, check backend logs'.format(service)
        logger.error('Reply from %s is not valid', service, exc_info=True)

    return result, error


def parse_tank(tank):
    host, service, group = '', '', ''
    if tank and tank.startswith('nanny'):
        try:
            service, group = tank.split('#')
        except ValueError:
            service = tank
    else:
        host = tank
    return host, service, group


def parse_tf_response(tank_finder_list):
    target = ''
    target_port = '80'
    tank_list = []
    err = ''

    if isinstance(tank_finder_list, dict) and tank_finder_list.get('result') == 'error':
        err = tank_finder_list.get('error_msg')

    if isinstance(tank_finder_list, list):
        for item in tank_finder_list:
            target = item.get('hostname')
            target_port = item.get('target_port')
            tank_list.append(LunaparkTank(host=item.get('tank'), port=item.get('port')))

    return target, target_port, tank_list, err


def call_tank_finder(tank, tank_port, target, target_port):
    """
    External call to tank finder
    Checks if tank and target are located in the same DC
    :param: tank: tank hostname, ip or nanny group
    :type: tank: str
    :param: target: target hostname, ip or nanny group
    :type: port: str
    :param: port: tank port, can be empty
    :type: target: str
    :return: target, target_port, tank_list, error
    :rtype: set(str, str, list, str)
    """
    base_url = 'https://{tf_host}'.format(tf_host=TANK_FINDER_HOST)
    tank_host, tank_service, tank_group = parse_tank(tank)
    tank_list = []
    error = ''

    if tank_host:
        url = '{base}/check_hosts?tank={tank_host}&target={target}'.format(
            base=base_url, tank_host=tank_host, target=target)
        res, message = external_call('tank_finder', 'GET', url)
        error = message if message else res.get('error_msg', '')
        if res:
            tank_list.append(LunaparkTank(host=tank_host, port=tank_port))
    else:
        url = '{base}/tanks/list.json?target={target}&service={service}&group={group}'.format(
            base=base_url, target=target, service=tank_service, group=tank_group)
        res, message = external_call('tank_finder', 'GET', url)
        target, target_port, tank_list, err = parse_tf_response(res)
        error = message or err

    return target, target_port, tank_list, error


def call_validator(input_config):
    """
    External call to validator
    :param: input_config - Input config
    :type: Dict
    :return validated config, errors
    :rtype set(string, dict)
    Possible errors:
        -  Validator is not available:
          - ConnectionError
        - Validator reply is not valid:
          - validator reply is not 200
          - validator reply is not valid json
          - validator reply doesn't have keys 'config' and 'errors'
    """

    url = 'https://{host}/config/validate.json'.format(host=VALIDATOR_HOST)
    res, message = external_call('validator', 'POST', url, input_config)

    output_config = res.get('config', {})
    errors = ['{}: {}'.format(k, v) for k, v in res.setdefault('errors', {}).items()] if not message else []
    return output_config, errors
