from __future__ import annotations

import requests
from hashlib import md5

from django.http import HttpRequest, HttpResponseRedirect
from django.urls import reverse
from django.utils.http import urlencode
from django.views.decorators.csrf import csrf_exempt

from allauth.socialaccount.adapter import get_adapter
from allauth.socialaccount.helpers import (
    complete_social_login,
    render_authentication_error,
)
from allauth.socialaccount.models import SocialLogin, SocialToken
from allauth.socialaccount.providers.draugiem.provider import DraugiemProvider

from ..base import AuthError


class DraugiemApiError(Exception):
    pass


ACCESS_TOKEN_URL = "https://api.draugiem.lv/json"  # nosec
AUTHORIZE_URL = "https://api.draugiem.lv/authorize"


def login(request: HttpRequest):
    app = get_adapter().get_app(request, DraugiemProvider.id)
    redirect_url = request.build_absolute_uri(reverse(callback))
    # Draugiem mandates a weak hashing algorithm.
    redirect_url_hash = md5(f"{app.secret}{redirect_url}".encode()).hexdigest()  # nosec
    params = {
        "app": app.client_id,
        "hash": redirect_url_hash,
        "redirect": redirect_url,
    }
    SocialLogin.stash_state(request)
    return HttpResponseRedirect(f"{AUTHORIZE_URL}?{urlencode(params)}")


@csrf_exempt
def callback(request: HttpRequest):
    adapter = get_adapter()
    provider = adapter.get_provider(request, DraugiemProvider.id)

    if "dr_auth_status" not in request.GET:
        return render_authentication_error(request, provider, error=AuthError.UNKNOWN)

    if request.GET["dr_auth_status"] != "ok":
        return render_authentication_error(request, provider, error=AuthError.DENIED)

    if "dr_auth_code" not in request.GET:
        return render_authentication_error(request, provider, error=AuthError.UNKNOWN)

    ret = None
    auth_exception = None
    try:
        app = provider.app
        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, provider, exception=auth_exception)

    return ret


def draugiem_complete_login(request: HttpRequest, app, code):
    provider = get_adapter().get_provider(request, DraugiemProvider.id)
    with get_adapter().get_requests_session() as sess:
        params = {"action": "authorize", "app": app.secret, "code": code}
        response = sess.get(ACCESS_TOKEN_URL, params=params)
        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
