import asyncio
from urllib import parse
import random
import string
import json
import re

import aiohttp
from . import exceptions


REQ_ACCESS_URL = "https://github.com/login/oauth/authorize"
REQ_TOKEN_URL = "https://github.com/login/oauth/access_token"
API_URL = "https://api.github.com/"

SAFE_NAME_RE = re.compile(r"[a-z\d][a-z\d\-]+[a-z\d]", re.IGNORECASE)


def safe_bit(bit, value):
    if "--" in value:
        raise ValueError("Double dashes are not allowed.")
    if not SAFE_NAME_RE.match(value):
        raise ValueError("Value is not safe")
    return value


def format_uri(uri, args):
    r = ""
    for bit in uri.split("/"):
        if not bit:
            continue
        r += "/"
        if bit.startswith(":"):
            r += safe_bit(bit, args.pop(0))
        else:
            r += bit
    if args:
        raise ValueError("Not all arguments formatted")
    return r[1:]


class Response:
    """Wrapper class for aiohttp response"""

    def __init__(self, response):
        self._response = response

    @property
    def status(self):
        return self._response.status

    @asyncio.coroutine
    def json(self):
        return (yield from self._response.json())


class Client:
    """Represent github api client.

    Usage:
        from aiogh import github
        token = "token"  # get token somehow
        c = github.Client(token)
        data = c.get("user")
        email = data["email"]

    """

    def __init__(self, token=None, scopes=None):
        self.token = token
        self.scopes = scopes
        self.post_headers = {
            "Accept": "application/json",
            "Content-Type": "application/json",
        }
        self.headers = {
            "Accept": "application/json",
        }
        if token:
            self.headers.update({"Authorization": "token " + token})
            self.post_headers.update({"Authorization": "token " + token})

    @asyncio.coroutine
    def post(self, uri, *args, full_response=False, **params):
        if args:
            uri = format_uri(uri, list(args))
        print(API_URL + uri)
        resp = yield from aiohttp.post(API_URL + uri,
                                       data=json.dumps(params),
                                       headers=self.post_headers)
        if 200 > resp.status > 300:
            raise exceptions.HttpError(Response(resp))
        if full_response:
            return Response(resp)
        return (yield from resp.json())

    @asyncio.coroutine
    def get(self, uri, *args, full_response=False, **params):
        if args:
            uri = format_uri(uri, list(args))
        print(API_URL + uri)
        resp = yield from aiohttp.get(API_URL + uri,
                                      params=params,
                                      headers=self.headers)
        if 200 > resp.status > 300:
            raise exceptions.HttpError(Response(resp))
        if full_response:
            return Response(resp)
        return (yield from resp.json())


class OAuth:
    """Handle OAuth github protocol.

    Usage:
        from aiogh import github

        ...
        # somewhere in your app
        self.oauth = github.Oauth("my_client_id", "secret")
        ...

        ...
        # somewhere in web page handler
        url = self.oauth.generate_request_url(("put", "scopes", "here"))
        # make user open it
        return http_redirect(url)
        ...

        ...
        # somewhere in oauth callback handler
        code = request.GET["code"]
        state = request.GET["state"]
        client = oauth.oauth(code, state)
        # save user token if needed
        self.db.save_token(client.token)
        ...

    """

    def __init__(self, client_id, client_secret):
        """Init github oauth app.

        :param string client_id: Client ID
        :param string client_secret: Client Secret
        """
        self._client_id = client_id
        self._client_secret = client_secret
        self._requested_scopes = {}

    def generate_request_url(self, scopes: tuple):
        """Generate OAuth request url.

        :param scopes: github access scopes
                       (https://developer.github.com/v3/oauth/#scopes)
        :param callback: callback to be called when user authorize request
                         (may be coroutine)
        """
        state = ''.join(random.sample(string.ascii_letters, 16))
        self._requested_scopes[state] = scopes
        qs = parse.urlencode({
            "client_id": self._client_id,
            "scope": ",".join(scopes),
            "state": state,
        })
        return "%s?%s" % (REQ_ACCESS_URL, qs)

    @asyncio.coroutine
    def oauth(self, code, state):
        """Handler for Authorization callback URL.

        'code' and 'state' are GET variables given by github

        :param string code:
        :param string state:
        :return: Client instance
        :raises: exceptions.GithubException
        """
        try:
            scopes = self._requested_scopes.pop(state)
        except KeyError:
            raise exceptions.UnknownState(state)
        data = {
            "client_id": self._client_id,
            "client_secret": self._client_secret,
            "code": code,
            "state": state,
        }
        headers = {
            "accept": "application/json"
        }
        response = yield from aiohttp.post(REQ_TOKEN_URL,
                                           data=data,
                                           headers=headers)
        data = yield from response.json()
        return Client(data["access_token"], scopes)

@@ -12,40 +12,29 @@
 cio%0A
-from urllib 
 import 
-parse
+base64
 %0Aimport 
 rand
@@ -33,22 +33,20 @@
 ort 
-random
+json
 %0Aimport 
 stri
@@ -45,22 +45,18 @@
 ort 
+o
 s
-tring
 %0Aimport 
 json
@@ -55,21 +55,34 @@
 ort 
-json%0A
+re%0Afrom urllib 
 import 
-r
+pars
 e%0A%0Ai
@@ -2058,37 +2058,8 @@
 s))%0A
-        print(API_URL + uri)%0A
     
@@ -2582,37 +2582,8 @@
 s))%0A
-        print(API_URL + uri)%0A
     
@@ -2932,24 +2932,451 @@
 sp.json())%0A%0A
+    @asyncio.coroutine%0A    def delete(self, uri, *args, full_response=False, **params):%0A        if args:%0A            uri = format_uri(uri, list(args))%0A        resp = yield from aiohttp.delete(API_URL + uri,%0A                                         params=params,%0A                                         headers=self.headers)%0A        if full_response:%0A            return Response(resp)%0A        return (yield from resp.json())%0A%0A
 %0Aclass OAuth
@@ -4713,55 +4713,55 @@
 e = 
-''.join(random.sample(string.ascii_letters, 16)
+base64.b64encode(os.urandom(15)).decode(%22ascii%22
 )%0A  

add delete method; use os.urandom