import uuid
from urllib.parse import urljoin, urlencode, parse_qsl
from urllib.request import urlopen

from django.http import HttpResponseRedirect
from django.views import View

from libs.json_utils import loads

ERR_INVALID_STATE = 'An error occurred while validating your request.'


class OAuth2LoginView(View):
    authorize_url = None
    client_id = None
    scope = ''

    def __init__(self, authorize_url=None, client_id=None, scope=None, *args, **kwargs):
        super(OAuth2LoginView, self).__init__(*args, **kwargs)
        if authorize_url is not None:
            self.authorize_url = authorize_url
        if client_id is not None:
            self.client_id = client_id
        if scope is not None:
            self.scope = scope

    def get_scope(self):
        return self.scope

    def get_authorize_url(self):
        return self.authorize_url

    def get_authorize_params(self, state, redirect_uri):
        return {
            'client_id': self.client_id,
            'response_type': "code",
            'scope': self.get_scope(),
            'state': state,
            'redirect_uri': redirect_uri,
        }

    def dispatch(self, request, wizard, *args, **kwargs):
        if 'code' in request.GET:
            return wizard.next_step()

        state = uuid.uuid4().hex

        params = self.get_authorize_params(
            state=state,
            redirect_uri=urljoin('/', wizard.redirect_url()),
        )
        redirect_uri = '{}?{}'.format(self.get_authorize_url(), urlencode(params))

        wizard.bind_state('state', state)

        return self.redirect(redirect_uri)

    def redirect(self, url):
        return HttpResponseRedirect(url)


class OAuth2CallbackView(View):
    access_token_url = None
    client_id = None
    client_secret = None

    def __init__(self, access_token_url=None, client_id=None, client_secret=None, *args, **kwargs):
        super(OAuth2CallbackView, self).__init__(*args, **kwargs)
        if access_token_url is not None:
            self.access_token_url = access_token_url
        if client_id is not None:
            self.client_id = client_id
        if client_secret is not None:
            self.client_secret = client_secret

    def get_token_params(self, code, redirect_uri):
        return {
            'grant_type': 'authorization_code',
            'code': code,
            'redirect_uri': redirect_uri,
            'client_id': self.client_id,
            'client_secret': self.client_secret,
        }

    def exchange_token(self, request, wizard, code):
        data = self.get_token_params(
            code=code,
            redirect_uri=urljoin('/', wizard.redirect_url()),
        )
        response = urlopen(self.access_token_url, data=data)
        content = response.content
        if response.headers['Content-Type'].startswith('application/x-www-form-urlencoded'):
            return dict(parse_qsl(content))
        return loads(content)

    def dispatch(self, request, wizard, *args, **kwargs):
        error = request.GET.get('error')
        state = request.GET.get('state')
        code = request.GET.get('code')

        if error:
            wizard.logger.info('identity.token-exchange-error', extra={'error': error})
            return wizard.error(error)

        if state != wizard.fetch_state('state'):
            wizard.logger.info('identity.token-exchange-error', extra={'error': 'invalid_state'})
            return wizard.error(ERR_INVALID_STATE)

        data = self.exchange_token(request, wizard, code)

        if 'error_description' in data:
            error = data.get('error')
            wizard.logger.info('identity.token-exchange-error', extra={'error': error})
            return wizard.error(data['error_description'])

        if 'error' in data:
            wizard.logger.info('identity.token-exchange-error', extra={'error': data['error']})
            return wizard.error('Failed to retrieve token from the upstream service.')

        wizard.bind_state('data', data)

        return wizard.next_step()

@@ -5,16 +5,17 @@
 rt uuid%0A
+%0A
 from url
@@ -34,17 +34,8 @@
 port
- urljoin,
  url
@@ -55,43 +55,8 @@
 _qsl
-%0Afrom urllib.request import urlopen
 %0A%0Afr
@@ -143,103 +143,66 @@
 ibs.
-json_utils import loads%0A%0AERR_INVALID_STATE = 'An error occurred while validating your request.'
+http import safe_urlopen%0Afrom libs.json_utils import loads
 %0A%0A%0Ac
@@ -1310,37 +1310,24 @@
 edirect_uri=
-urljoin('/', 
 wizard.redir
@@ -1326,33 +1326,47 @@
 rd.redirect_url(
-)
+request=request
 ),%0A        )%0A   
@@ -1488,17 +1488,16 @@
  state)%0A
-%0A
         
@@ -2559,21 +2559,8 @@
 uri=
-urljoin('/', 
 wiza
@@ -2575,17 +2575,31 @@
 ect_url(
-)
+request=request
 ),%0A     
@@ -2622,16 +2622,21 @@
 ponse = 
+safe_
 urlopen(
@@ -2703,16 +2703,25 @@
 .content
+.decode()
 %0A       
@@ -3390,25 +3390,58 @@
 ror(
-ERR_INVALID_STATE
+'An error occurred while validating your request.'
 )%0A%0A 

Update oauth views