from .context import ToJsonContext
from .message import SaasMessage
from .message_delete import SaasDeleteMessage

import json
import logging
import time
import requests

TMP_CODE = 202

logging.basicConfig()
LOG = logging.getLogger('saas_py_api')
LOG.setLevel(logging.INFO)


def SetSaasApiDebugLogLevel():
    LOG.setLevel(logging.DEBUG)


def SendMessage(url, message, send_type=ToJsonContext.TYPE.json):
    """
    Sends message with POST, one try
    :param url: saas service url
    :param message: json, or str with dumped json, or SaasMessage
    :param send_type: json or json_ref. default json
    :return: json, where 'result': is request successful,
                         'answer': when result is ok,
                         'error': descr when request failed
    """
    context = ToJsonContext(send_type)
    if isinstance(message, SaasDeleteMessage) or isinstance(message, SaasMessage):
        data_str = json.dumps(message.to_json(context))
    elif isinstance(message, str):
        if send_type != ToJsonContext.TYPE.json:
            return {'result': False, 'error': 'Cannot create json_ref from string'}
        data_str = message
    else:
        if send_type != ToJsonContext.TYPE.json:
            return {'result': False, 'error': 'Cannot create json_ref from dict'}
        data_str = json.dumps(message)
    LOG.debug(data_str)
    try:
        if send_type == ToJsonContext.TYPE.json:
            req = requests.post(url, data=data_str)
        elif send_type == ToJsonContext.TYPE.json_ref:
            context.parts['json_message'] = data_str
            req = requests.post(url, files=context.parts)
        else:
            return {'result': False, 'error': 'unknown send_type {}'.format(send_type)}
        req.raise_for_status()
        ans = req.text
        LOG.debug(ans)
        return {'result': True, 'answer': ans, 'code': req.status_code, 'stage': 'first_sending'}
    except requests.HTTPError as e:
        LOG.error('{0} {1} {2}'.format(e, e.response.status_code, e.response.text))
        return {'result': False, 'error': '{0} {1} {2}'.format(e, e.response.status_code, e.response.text)}
    except Exception as e:
        LOG.error('{}'.format(e))
        return {'result': False, 'error': '{}'.format(e)}


def ReSendAsyncMessage(url, message, tries, intervalSec, send_type=ToJsonContext.TYPE.json):
    """
    Sends message and waits until its status is final
    :param url: saas service url
    :param message: one of SaasDeleteMessage types, also may be json, or str with dumped json, or SaasMessage
    :param tries: max 202-s rechecks
    :param intervalSec: time between status rechecks
    :param send_type: json or json_ref. default json
    :return: json, where 'result': is request successful,
                         'answer': when result is ok,
                         'error': descr when request failed
    """
    send_init = SendMessage(url, message, send_type)
    if not send_init.get('result'):
        return send_init
    if send_init['code'] != TMP_CODE:
        LOG.warning('message seems to be not async')
        return send_init
    ans = send_init['answer']

    if '?' in url:
        url += '&trace=yes'
    else:
        url += '?trace=yes'

    for i in range(tries):
        try:
            async_code = json.loads(ans)['async_code']
            req = requests.post(url, data=async_code)
            req.raise_for_status()
            ans = req.text
            LOG.debug(ans)
            if req.status_code != TMP_CODE:
                return {'result': True, 'answer': ans, 'code': req.status_code, 'stage': 'resend_{}'.format(i)}
        except requests.HTTPError as e:
            LOG.error('{0} {1} {2}'.format(e, e.response.status_code, e.response.text))
            return {'result': False, 'error': '{0} {1} {2}'.format(e, e.response.status_code, e.response.text)}
        except Exception as e:
            LOG.error('{}'.format(e))
            return {'result': False, 'error': '{}'.format(e)}
        time.sleep(intervalSec)
    LOG.error('retries attempts expired ({})'.format(tries))
    return {'result': False, 'error': 'retries attempts expired ({})'.format(tries)}
