from urllib.parse import unquote_plus

from django.conf import settings
from django.http import HttpResponseRedirect
from rest_framework.decorators import authentication_classes, api_view, permission_classes
from rest_framework.permissions import IsAuthenticated

from app.encryption.hashid import CFHashIds
from backend.authentication import JWTOrAPIKeyAuthentication, JWTAuthentication

from app.api import api
from app.api.response_builder import ResponseBuilder
from app.loan_application.loan_application import LoanApplication
from app.user.user import User
from app.util import util, datetime_util


def validate_references_inputs(references, loan_application_id):
    if not (references and len(references) == 2):
        return api.INVALID_INPUT, None

    loan_application = LoanApplication.get_loan_application(hashed_loan_application_id=loan_application_id)
    if not loan_application:
        return api.LOAN_APPLICATION_NOT_FOUND, None

    user = LoanApplication(loan_application).get_user()
    user_phone_number = User(user).get_plaintext_phone()

    for reference in references:
        if not ('name' in reference and 'phone_number' in reference and 'relationship' in reference and
                user_phone_number != reference['phone_number']):
            return api.INVALID_INPUT, None

    return api.SUCCESS, loan_application


@api_view(['POST'])
@authentication_classes((JWTOrAPIKeyAuthentication,))
@permission_classes((IsAuthenticated,))
def update_user_references(request):
    data = request.data
    response_builder = ResponseBuilder()

    references = data.get('references')
    loan_application_id = data.get('application_id')

    status, loan_application = validate_references_inputs(references, loan_application_id)
    if util.is_status_failed(status):
        return response_builder.get_ok200_fail_response(status)

    status = LoanApplication(loan_application).update_references(references)
    if util.is_status_failed(status):
        return response_builder.get_ok200_fail_response(status)

    return response_builder.success().ok_200().message('References updated').get_response()


def get_loan_application(request):
    data = request.GET
    application_data = data.get('data')

    status, decrypted_data = LoanApplication.get_decrypted_link_data(unquote_plus(application_data))
    if util.is_status_failed(status):
        return HttpResponseRedirect(
            f"{settings.FRONTEND_HOME_URL}/customer/loan/application/error/?message={api.error_messages.get(status, '')}")

    redirect_response = HttpResponseRedirect(
        f"{settings.FRONTEND_HOME_URL}/customer/?application_id={decrypted_data['application_id']}&&redirect_url=otp")

    return redirect_response


def validate_sign_agreement_inputs(loan_application_id, agreement, load_time, latitude, longitude):
    if not (loan_application_id and agreement and load_time and latitude and longitude):
        return api.INVALID_INPUT, None

    loan_application = LoanApplication.get_loan_application(hashed_loan_application_id=loan_application_id)
    if not loan_application:
        return api.INVALID_INPUT, None

    return api.SUCCESS, loan_application


@api_view(['POST'])
@authentication_classes((JWTOrAPIKeyAuthentication,))
@permission_classes((IsAuthenticated,))
def sign_agreement(request):
    data = request.data
    response_builder = ResponseBuilder()

    bearer_token = api.get_bearer_token(request)
    client_id, client_secret = api.get_client_key_secret(request)

    loan_application_id = data.get("application_id")
    agreement = data.get("agreement")

    os = data.get("os")
    browser = data.get("browser")
    full_version = data.get("full_version")
    major_version = data.get("major_version")
    app_name = data.get("app_name")
    user_agent = data.get("user_agent")
    latitude = data.get('latitude')
    longitude = data.get('longitude')
    user_ip = api.get_client_ip(request)
    load_time = data.get("load_time")
    click_time_string = datetime_util.get_now_ist_string()

    load_time = datetime_util.get_datetime_object(load_time)

    if not latitude or not longitude:
        latitude, longitude = LoanApplication.get_lat_long(loan_application_id)

    status, loan_application = validate_sign_agreement_inputs(
        loan_application_id, agreement, load_time, latitude, longitude)
    if util.is_status_failed(status):
        return response_builder.get_bad_request_response(status)

    status = LoanApplication(loan_application).sign_agreement(
        agreement, bearer_token, os, browser, full_version, major_version, app_name, user_agent, user_ip,
        load_time, click_time_string, latitude, longitude, client_id, client_secret)

    if util.is_status_failed(status):
        return response_builder.get_ok200_fail_response(status)

    return response_builder.success().ok_200().message('Agreement signed').get_response()


def validate_check_bureau_inputs(loan_application_id):
    if not loan_application_id:
        return api.INVALID_INPUT, None

    loan_application = LoanApplication.get_loan_application(hashed_loan_application_id=loan_application_id)
    if not loan_application:
        return api.LOAN_APPLICATION_NOT_FOUND, None

    return api.SUCCESS, loan_application


@api_view(['POST'])
@authentication_classes((JWTOrAPIKeyAuthentication,))
@permission_classes((IsAuthenticated,))
def check_bureau(request):
    data = request.data
    bearer_token = api.get_bearer_token(request)
    client_id, client_secret = api.get_client_key_secret(request)

    response_builder = ResponseBuilder()

    loan_application_id = data.get('application_id')

    status, loan_application = validate_check_bureau_inputs(
        loan_application_id)
    if util.is_status_failed(status):
        return response_builder.get_bad_request_response(status)

    status = LoanApplication(loan_application).fetch_bureau(bearer_token, client_id, client_secret)
    if util.is_status_failed(status):
        return response_builder.get_ok200_fail_response(status)

    status = LoanApplication(loan_application).run_auto_decision(bearer_token, client_id, client_secret)
    if util.is_status_failed(status):
        return response_builder.get_ok200_fail_response(status)

    return response_builder.success().ok_200().message('Check bureau completed').get_response()


def validate_update_customer_consent(application_id, status, user_ip):
    if not (application_id and status and user_ip):
        return api.INVALID_INPUT, None

    loan_application = LoanApplication.get_loan_application(hashed_loan_application_id=application_id)
    if not loan_application:
        return api.INVALID_INPUT, None

    return api.SUCCESS, loan_application


@api_view(['POST'])
@authentication_classes((JWTOrAPIKeyAuthentication,))
@permission_classes((IsAuthenticated,))
def update_customer_consent(request):
    data = request.data
    response_builder = ResponseBuilder()

    application_id = data.get('application_id')
    application_id = CFHashIds().encode(application_id) if CFHashIds().encode(application_id) != '' else application_id
    latitude = data.get('latitude')
    longitude = data.get('longitude')
    status = data.get('status')
    user_ip = api.get_client_ip(request)

    status, loan_application = validate_update_customer_consent(application_id, status, user_ip)
    if util.is_status_failed(status):
        return response_builder.get_bad_request_response_solar()

    status = LoanApplication(loan_application).update_consent(latitude, longitude, status, user_ip, request)
    if util.is_status_failed(status):
        return response_builder.get_ok200_fail_response(status)

    if request.path == '/v1/save-consent/':
        return response_builder.solar_success().ok_200().message('Consent updated').get_solar_response()
    return response_builder.success().ok_200().message('Consent updated').get_response()
