import requests
import time

from .exceptions import ConnectionError, TimeoutError
from .parsers import decode_engineIO_content, encode_engineIO_content


ENGINEIO_PROTOCOL = 3
TRANSPORTS = 'websocket', 'xhr-polling'


class AbstractTransport(object):

    def __init__(self, http_session, is_secure, url, engineIO_session=None):
        self.http_session = http_session
        self.is_secure = is_secure
        self.url = url
        self.engineIO_session = engineIO_session

    def recv_packet(self):
        pass

    def send_packet(self, engineIO_packet_type, engineIO_packet_data):
        pass


class XHR_PollingTransport(AbstractTransport):

    def __init__(self, http_session, is_secure, url, engineIO_session=None):
        super(XHR_PollingTransport, self).__init__(
            http_session, is_secure, url, engineIO_session)
        self.http_url = '%s://%s/' % ('https' if is_secure else 'http', url)
        self.params = {
            'EIO': ENGINEIO_PROTOCOL,
            'transport': 'polling',
        }
        if engineIO_session:
            self.request_index = 1
            self.kw_get = dict(timeout=engineIO_session.ping_timeout)
            self.kw_post = dict(headers={
                'content-type': 'application/octet-stream',
            })
            self.params['sid'] = engineIO_session.id
        else:
            self.request_index = 0
            self.kw_get = {}
            self.kw_post = {}

    def recv_packet(self):
        params = dict(self.params)
        params['t'] = self._get_timestamp()
        response = get_response(
            self.http_session.get,
            self.http_url,
            params=params,
            **self.kw_get)
        for engineIO_packet in decode_engineIO_content(response.content):
            yield engineIO_packet

    def send_packet(self, engineIO_packet_type, engineIO_packet_data):
        params = dict(self.params)
        params['t'] = self._get_timestamp()
        response = get_response(
            self.http_session.post,
            self.http_url,
            params=params,
            data=encode_engineIO_content([
                (engineIO_packet_type, engineIO_packet_data),
            ]),
            **self.kw_post)
        assert response.content == b'ok'

    def _get_timestamp(self):
        timestamp = '%s-%s' % (int(time.time() * 1000), self.request_index)
        self.request_index += 1
        return timestamp


def get_response(request, *args, **kw):
    try:
        response = request(*args, **kw)
    except requests.exceptions.Timeout as e:
        raise TimeoutError(e)
    except requests.exceptions.ConnectionError as e:
        raise ConnectionError(e)
    except requests.exceptions.SSLError as e:
        raise ConnectionError('could not negotiate SSL (%s)' % e)
    status_code = response.status_code
    if 200 != status_code:
        raise ConnectionError('unexpected status code (%s)' % status_code)
    return response


def prepare_http_session(kw):
    http_session = requests.Session()
    http_session.headers.update(kw.get('headers', {}))
    http_session.auth = kw.get('auth')
    http_session.proxies.update(kw.get('proxies', {}))
    http_session.hooks.update(kw.get('hooks', {}))
    http_session.params.update(kw.get('params', {}))
    http_session.verify = kw.get('verify')
    http_session.cert = kw.get('cert')
    http_session.cookies.update(kw.get('cookies', {}))
    return http_session
