from urlparse import urlparse
import random
import json
import hmac
import hashlib
import math

import mohawk
from requests.auth import AuthBase

from loads.case import TestCase


class TestLoop(TestCase):

    def test_all(self):
        self.register()
        token = self.generate_token()
        call_id, session, caller_token, api_key = self.initiate_call(token)
        calls = self.list_pending_calls()
        for call in calls:
            # We want to reject 30% of the calls.
            status = 200
            if random.randint(0, 100) <= 30:
                self.discard_call(call['callId'])
                status = 404
            self.get_call_status(call['callId'], status)

    def _get_json(self, resp):
        try:
            return resp.json()
        except Exception:
            print resp.text
            raise

    def register(self):
        resp = self.session.post(
            self.server_url + '/registration',
            data={'simple_push_url': 'http://localhost/push'})
        self.assertEquals(200, resp.status_code)

        try:
            self.hawk_auth = HawkAuth(
                self.server_url,
                resp.headers['hawk-session-token'])
        except KeyError:
            print resp
            raise

    def generate_token(self):
        resp = self.session.post(
            self.server_url + '/call-url',
            data=json.dumps({'callerId': 'alexis@mozilla.com'}),
            headers={'Content-type': 'application/json'},
            auth=self.hawk_auth
        )
        self.assertEquals(resp.status_code, 200)
        data = self._get_json(resp)
        call_url = data['call_url']
        return call_url.split('/').pop()

    def initiate_call(self, token):
        # This happens when not authenticated.
        resp = self.session.post(self.server_url + '/calls/%s' % token)
        self.assertEquals(resp.status_code, 200)

        data = self._get_json(resp)
        return (data['callId'], data['sessionId'], data['sessionToken'],
                data['apiKey'])

    def list_pending_calls(self):
        resp = self.session.get(
            self.server_url + '/calls?version=200',
            auth=self.hawk_auth)
        data = self._get_json(resp)
        return data['calls']

    def revoke_token(self, token):
        # You don't need to be authenticated to revoke a token.
        self.session.delete(self.server_url + '/call-url/%s' % token)

    def discard_call(self, call_id):
        self.session.delete(self.server_url + '/calls/id/%s' % call_id)

    def get_call_status(self, call_id, status):
        resp = self.session.get(self.server_url + '/calls/id/%s' % call_id)
        self.assertEqual(resp.status_code, status)


def HKDF_extract(salt, IKM, hashmod=hashlib.sha256):
    """HKDF-Extract; see RFC-5869 for the details."""
    if salt is None:
        salt = b"\x00" * hashmod().digest_size
    return hmac.new(salt, IKM, hashmod).digest()


def HKDF_expand(PRK, info, L, hashmod=hashlib.sha256):
    """HKDF-Expand; see RFC-5869 for the details."""
    digest_size = hashmod().digest_size
    N = int(math.ceil(L * 1.0 / digest_size))
    assert N <= 255
    T = b""
    output = []
    for i in xrange(1, N + 1):
        data = T + info + chr(i)
        T = hmac.new(PRK, data, hashmod).digest()
        output.append(T)
    return b"".join(output)[:L]


def HKDF(secret, salt, info, size, hashmod=hashlib.sha256):
    """HKDF-extract-and-expand as a single function."""
    PRK = HKDF_extract(salt, secret, hashmod)
    return HKDF_expand(PRK, info, size, hashmod)


class HawkAuth(AuthBase):
    def __init__(self, server_url, tokendata):
        hawk_session = tokendata.decode('hex')
        self.server_url = server_url
        keyInfo = 'identity.mozilla.com/picl/v1/sessionToken'
        keyMaterial = HKDF(hawk_session, "", keyInfo, 32*3)
        self.credentials = {
            'id': keyMaterial[:32].encode("hex"),
            'key': keyMaterial[32:64].encode("hex"),
            'algorithm': 'sha256'
        }

    def __call__(self, r):
        r.headers['Host'] = urlparse(self.server_url).netloc
        sender = mohawk.Sender(
            self.credentials,
            r.url,
            r.method,
            content=r.body or '',
            content_type=r.headers.get('Content-Type', '')
        )

        r.headers['Authorization'] = sender.request_header
        return r

@@ -990,22 +990,24 @@
 p://
-localhost/push
+httpbin.org/deny
 '%7D)%0A
@@ -1053,16 +1053,84 @@
 tus_code
+,%0A                          %22Registration failed: %25s%22 %25 resp.content
 )%0A%0A     
@@ -1647,16 +1647,89 @@
 ode, 200
+,%0A                          %22Call-Url creation failed: %25s%22 %25 resp.content
 )%0A      
@@ -1777,25 +1777,49 @@
 l_url = data
-%5B
+.get('callUrl', data.get(
 'call_url'%5D%0A
@@ -1816,17 +1816,18 @@
 all_url'
-%5D
+))
 %0A       
@@ -2018,57 +2018,194 @@
 oken
-)%0A        self.assertEquals(resp.status_code, 200
+,%0A                                 %7B%22callType%22: %22audio-video%22%7D)%0A        self.assertEquals(resp.status_code, 200,%0A                          %22Call Initialization failed: %25s%22 %25 resp.content
 )%0A%0A 
@@ -3016,16 +3016,91 @@
 , status
+,%0A                         %22Call status retrieval failed %25s%22 %25 resp.content
 )%0A%0A%0Adef 

Improve loadtest debugging.