import requests
from hashlib import md5

from django.http import HttpResponseRedirect
from django.urls import reverse
from django.utils.http import urlencode
from django.views.decorators.csrf import csrf_exempt

from allauth.socialaccount import providers
from allauth.socialaccount.helpers import (
    complete_social_login,
    render_authentication_error,
)
from allauth.socialaccount.models import SocialLogin, SocialToken

from ..base import AuthError
from .provider import DraugiemProvider


class DraugiemApiError(Exception):
    pass


ACCESS_TOKEN_URL = 'http://api.draugiem.lv/json'
AUTHORIZE_URL = 'http://api.draugiem.lv/authorize'


def login(request):
    app = providers.registry.by_id(
        DraugiemProvider.id, request).get_app(request)
    redirect_url = request.build_absolute_uri(reverse(callback))
    redirect_url_hash = md5((
        app.secret + redirect_url).encode('utf-8')).hexdigest()
    params = {
        'app': app.client_id,
        'hash': redirect_url_hash,
        'redirect': redirect_url,
    }
    SocialLogin.stash_state(request)
    return HttpResponseRedirect('%s?%s' % (AUTHORIZE_URL, urlencode(params)))


@csrf_exempt
def callback(request):
    if 'dr_auth_status' not in request.GET:
        return render_authentication_error(
            request, DraugiemProvider.id, error=AuthError.UNKNOWN)

    if request.GET['dr_auth_status'] != 'ok':
        return render_authentication_error(
            request, DraugiemProvider.id, error=AuthError.DENIED)

    if 'dr_auth_code' not in request.GET:
        return render_authentication_error(
            request, DraugiemProvider.id, error=AuthError.UNKNOWN)

    ret = None
    auth_exception = None
    try:
        app = providers.registry.by_id(
            DraugiemProvider.id, request).get_app(request)
        login = draugiem_complete_login(
            request, app, request.GET['dr_auth_code'])
        login.state = SocialLogin.unstash_state(request)

        ret = complete_social_login(request, login)
    except (requests.RequestException, DraugiemApiError) as e:
        auth_exception = e

    if not ret:
        ret = render_authentication_error(
            request, DraugiemProvider.id, exception=auth_exception)

    return ret


def draugiem_complete_login(request, app, code):
    provider = providers.registry.by_id(DraugiemProvider.id, request)
    response = requests.get(ACCESS_TOKEN_URL, {
        'action': 'authorize',
        'app': app.secret,
        'code': code
    })
    response.raise_for_status()
    response_json = response.json()

    if 'error' in response_json:
        raise DraugiemApiError(response_json['error'])

    token = SocialToken(app=app, token=response_json['apikey'])

    login = provider.sociallogin_from_response(request, response_json)
    login.token = token
    return login
