# By: Riasat Ullah
# This file contains all the views related to an organization.

from data_syncers import syncer_organizations
from dbqueries import db_organizations, db_users
from exceptions.user_exceptions import InvalidPassword, InvalidRequest, NotUniqueValue, MaliciousSource, \
    UnauthorizedRequest
from modules import credentials
from notices import account_notices
from rest_framework.decorators import api_view
from rest_framework.response import Response
from taskcallrest import settings
from translators import label_translator as _lt
from utils import ban_checker, constants, errors, helpers, info, informational_content, key_manager, logging, mail, \
    permissions, roles, times, tokenizer, var_names
from utils.db_connection import CACHE_CLIENT, CONN_POOL
from validations import request_validator, string_validator
import datetime
import configuration as configs
import jwt
import requests


@api_view(['POST'])
def create_organization(request, conn=None):
    '''
    Creates an organization.
    :param request: Http request
    :param conn: db connection
    :return: Http response -> (str)
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.organization_name, var_names.address, var_names.city, var_names.state,
                           var_names.zip_code, var_names.country, var_names.sector, var_names.subdomain,
                           var_names.job_title, var_names.email, var_names.password,
                           var_names.first_name, var_names.last_name, var_names.iso_country_code, var_names.phone,
                           var_names.preferred_username, var_names.timezone, var_names.language,
                           var_names.token, var_names.ip_address, var_names.profile_picture]
        optional_fields = [var_names.billing_currency]
        try:
            conn = CONN_POOL.get_db_conn() if conn is None else conn
            request_validator.validate_fields(request, expected_fields, optional_fields)

            org_iso = request.data[var_names.country]
            subdomain = request.data[var_names.subdomain]
            bill_curr = request.data[var_names.billing_currency] if var_names.billing_currency in request.data else None
            if bill_curr is None:
                bill_curr = constants.eur_curr if org_iso in constants.eu_union_country_codes else constants.usd_curr

            # make sure that the request contains a system generated valid registration token
            tk_email, tk_ip = tokenizer.read_registration_token(request.data[var_names.token])

            if tk_email != request.data[var_names.email] or tk_ip != request.data[var_names.ip_address]:
                raise InvalidRequest(errors.err_invalid_request)

            ip_address = request.data[var_names.ip_address]
            logging.info('Trying to create new organization from ip address - ' + ip_address)
            full_address = '/'.join([constants.ip_library_site + ip_address])
            if ban_checker.is_source_malicious(conn, [(var_names.iso_country_code,
                                                       requests.get(full_address).json()['countryCode'])]):
                raise MaliciousSource(errors.err_malicious_source_restriction)

            current_time = times.get_current_timestamp()

            # Check if the subdomain is available or not.
            if db_organizations.subdomain_is_available(conn, current_time, subdomain):
                onb_flow_id = key_manager.generate_reference_key()
                db_organizations.create_organization(conn, current_time,
                                                     request.data[var_names.organization_name].title(),
                                                     request.data[var_names.address].title(),
                                                     request.data[var_names.city].title(),
                                                     request.data[var_names.state].title(),
                                                     request.data[var_names.zip_code],
                                                     org_iso, request.data[var_names.sector],
                                                     subdomain, bill_curr,
                                                     request.data[var_names.job_title],
                                                     request.data[var_names.email],
                                                     request.data[var_names.password],
                                                     request.data[var_names.first_name].title(),
                                                     request.data[var_names.last_name].title(),
                                                     request.data[var_names.iso_country_code],
                                                     request.data[var_names.phone],
                                                     request.data[var_names.preferred_username],
                                                     request.data[var_names.timezone],
                                                     request.data[var_names.language],
                                                     request.data[var_names.profile_picture],
                                                     settings.REGION, onb_flow_id)

                # send a welcome email to the user
                subject, message = informational_content.welcome_on_board_content()
                mail.AmazonSesDispatcher(subject, message, request.data[var_names.email]).start()

                return Response(str(onb_flow_id))
            else:
                return Response(_lt.get_label(errors.err_subdomain_unavailable, lang), status=401)
        except (InvalidPassword, InvalidRequest, NotUniqueValue) as e:
            logging.exception(str(e))
            return Response(_lt.get_label(str(e), lang), status=400)
        except (UnauthorizedRequest, jwt.ExpiredSignatureError, jwt.InvalidSignatureError) as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_invalid_request, lang), status=401)
        except MaliciousSource as e:
            logging.info(str(e))
            return Response(_lt.get_label(errors.err_malicious_source_restriction, lang), status=403)
        except Exception as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_system_error, lang), status=500)
        finally:
            CONN_POOL.put_db_conn(conn)


@api_view(['POST'])
def get_organization_details(request, conn=None):
    '''
    Get all the information about the organization that a user is part of.
    :param request: Http request
    :param conn: db connection
    :return: Http response -> dict
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        try:
            conn = CONN_POOL.get_db_conn() if conn is None else conn
            user_id, org_id, user_perm, org_perm = tokenizer.authorize_request(request)
            if permissions.is_user_owner(user_perm):
                data = db_organizations.get_organization_details(conn, times.get_current_timestamp(),
                                                                 with_organization_id=org_id)
                if var_names.organization_id in data:
                    del data[var_names.organization_id]
                return Response(data)
            else:
                return Response(_lt.get_label(errors.err_user_rights, lang), status=403)
        except (InvalidRequest, LookupError) as e:
            logging.exception(str(e))
            return Response(_lt.get_label(str(e), lang), status=400)
        except (UnauthorizedRequest, jwt.ExpiredSignatureError, jwt.InvalidSignatureError) as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_authorization, lang), status=401)
        except Exception as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_system_error, lang), status=500)
        finally:
            CONN_POOL.put_db_conn(conn)


@api_view(['POST'])
def edit_organization_details(request, conn=None):
    '''
    Edit details of an organization.
    :param request: Http request
    :param conn: db connection
    :return: Http response
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.organization_name, var_names.address, var_names.city, var_names.state,
                           var_names.zip_code, var_names.country, var_names.sector, var_names.timezone,
                           var_names.language]
        try:
            conn = CONN_POOL.get_db_conn() if conn is None else conn
            request_validator.validate_fields(request, expected_fields)
            user_id, org_id, user_perm, org_perm = tokenizer.authorize_request(request)
            if permissions.is_user_owner(user_perm):
                db_organizations.edit_organization_info(conn, times.get_current_timestamp(), org_id,
                                                        request.data[var_names.organization_name].title(),
                                                        request.data[var_names.sector],
                                                        request.data[var_names.address].title(),
                                                        request.data[var_names.city].title(),
                                                        request.data[var_names.state].title(),
                                                        request.data[var_names.zip_code],
                                                        request.data[var_names.country],
                                                        request.data[var_names.timezone],
                                                        request.data[var_names.language])
                return Response(_lt.get_label(info.msg_org_info_updated, lang))
            else:
                return Response(_lt.get_label(errors.err_user_rights, lang), status=403)
        except (InvalidRequest, ValueError) as e:
            logging.exception(str(e))
            return Response(_lt.get_label(str(e), lang), status=400)
        except (UnauthorizedRequest, jwt.ExpiredSignatureError, jwt.InvalidSignatureError) as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_authorization, lang), status=401)
        except Exception as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_system_error, lang), status=500)
        finally:
            CONN_POOL.put_db_conn(conn)


@api_view(['POST'])
def get_requested_accounts(request, conn=None):
    '''
    Gets all the accounts that have been newly requested for an organization.
    :param request: Http request
    :param conn: db connection
    :return: Http response -> dict
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        try:
            conn = CONN_POOL.get_db_conn() if conn is None else conn
            user_id, org_id, user_perm, org_perm = tokenizer.authorize_request(request)
            if permissions.is_user_admin(user_perm):
                requested_accounts = db_organizations.get_new_requested_accounts(conn, times.get_current_timestamp(),
                                                                                 org_id)
                return Response(requested_accounts)
            else:
                return Response(_lt.get_label(errors.err_user_rights, lang), status=403)
        except InvalidRequest as e:
            logging.exception(str(e))
            return Response(_lt.get_label(str(e), lang), status=400)
        except (UnauthorizedRequest, jwt.ExpiredSignatureError, jwt.InvalidSignatureError) as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_authorization, lang), status=401)
        except Exception as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_system_error, lang), status=500)
        finally:
            CONN_POOL.put_db_conn(conn)


@api_view(['POST'])
def request_new_member_account(request, conn=None):
    '''
    Request new member accounts for an organization. This will send a notice to the email address
    of the new accounts and ask the account holder to register.
    :param request: Http request
    :param conn: db connection
    :return: Http response
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.data]
        try:
            conn = CONN_POOL.get_db_conn() if conn is None else conn
            request_validator.validate_fields(request, expected_fields)
            user_id, org_id, user_perm, org_perm = tokenizer.authorize_request(request)
            new_acc_details = request.data[var_names.data]

            # Check if the organization is about to exceed the maximum number of standard users
            # allowed for its subscription or not. Restrictions only apply for the free subscription
            # and if the organization is in its trial period.
            current_time = times.get_current_timestamp()
            is_trial, max_limit, curr_user_count = db_organizations.org_trial_status_and_user_counts(
                conn, current_time, org_id)

            if max_limit is not None:
                if curr_user_count + len(new_acc_details) > max_limit:
                    raise PermissionError(errors.err_org_max_users)

            if permissions.is_user_admin(user_perm):
                # first - generate the verification codes
                codes_to_send_by_email = dict()
                for item in new_acc_details:
                    code = key_manager.generate_verification_code(constants.verification_code_length)
                    codes_to_send_by_email[item[var_names.email]] = code

                    code_hash, salt = credentials.convert_text_to_hash(code)
                    item[var_names.verification_code] = (code_hash, salt)

                # second - save the codes in the database and then email the codes to the respective users
                start_timestamp = times.get_current_timestamp()
                end_timestamp = start_timestamp + datetime.timedelta(days=configs.new_user_request_lifetime)
                db_organizations.create_new_member_request(conn, org_id, org_perm, start_timestamp, end_timestamp,
                                                           new_acc_details)

                # third - email the codes
                org_name = db_organizations.get_organization_details(
                    conn, start_timestamp, org_id)[var_names.organization_name]
                messages = []
                for acc_email in codes_to_send_by_email:
                    acc_code = codes_to_send_by_email[acc_email]
                    subject, body = account_notices.new_member_verification_content(lang, org_name, acc_email, acc_code)
                    messages.append({var_names.email_subject: subject,
                                     var_names.email_body: body,
                                     var_names.email_to: acc_email})

                mail.AmazonSesBulkDispatcher(messages, with_logo=True).start()
                return Response(_lt.get_label(info.msg_user_request_sent, lang))
            else:
                return Response(_lt.get_label(errors.err_user_rights, lang), status=403)
        except (InvalidRequest, NotUniqueValue, PermissionError) as e:
            logging.exception(str(e))
            return Response(_lt.get_label(str(e), lang), status=400)
        except (UnauthorizedRequest, jwt.ExpiredSignatureError, jwt.InvalidSignatureError) as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_authorization, lang), status=401)
        except Exception as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_system_error, lang), status=500)
        finally:
            CONN_POOL.put_db_conn(conn)


@api_view(['POST'])
def resend_new_member_verification_code(request, conn=None):
    '''
    Re-sends the verification code for an existing new member account request.
    :param request: Http request
    :param conn: db connection
    :return: Http response
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.email]
        try:
            conn = CONN_POOL.get_db_conn() if conn is None else conn
            request_validator.validate_fields(request, expected_fields)
            user_id, org_id, user_perm, org_perm = tokenizer.authorize_request(request)
            new_mem_email = request.data[var_names.email]

            if permissions.is_user_admin(user_perm):
                # first - generate the verification code
                code = key_manager.generate_verification_code(constants.verification_code_length)
                code_hash, salt = credentials.convert_text_to_hash(code)

                # second - save the codes in the database and then email the codes to the respective users
                start_timestamp = times.get_current_timestamp()
                end_timestamp = start_timestamp + datetime.timedelta(days=configs.new_user_request_lifetime)
                db_organizations.resend_new_member_verification_code(
                    conn, org_id, start_timestamp, end_timestamp, new_mem_email, salt, code_hash
                )

                # third - email the codes
                org_name = db_organizations.get_organization_details(
                    conn, start_timestamp, org_id)[var_names.organization_name]
                subject, body = account_notices.new_member_verification_content(lang, org_name, new_mem_email, code)
                mail.AmazonSesDispatcher(subject, body, new_mem_email, with_logo=True).start()

                return Response(_lt.get_label(info.msg_success, lang))
            else:
                return Response(_lt.get_label(errors.err_user_rights, lang), status=403)
        except (InvalidRequest, LookupError) as e:
            logging.exception(str(e))
            return Response(_lt.get_label(str(e), lang), status=400)
        except (UnauthorizedRequest, jwt.ExpiredSignatureError, jwt.InvalidSignatureError, PermissionError) as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_authorization, lang), status=401)
        except Exception as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_system_error, lang), status=500)
        finally:
            CONN_POOL.put_db_conn(conn)


@api_view(['POST'])
def cancel_new_member_request(request, conn=None):
    '''
    Cancels a new member account request that may have been made before.
    :param request: Http request
    :param conn: db connection
    :return: Http response -> str
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.email]
        try:
            conn = CONN_POOL.get_db_conn() if conn is None else conn
            request_validator.validate_fields(request, expected_fields)
            user_id, org_id, user_perm, org_perm = tokenizer.authorize_request(request)

            if permissions.is_user_admin(user_perm):
                email = request.data[var_names.email]
                db_organizations.cancel_new_member_request(conn, times.get_current_timestamp(), org_id, email)
                return Response(_lt.get_label(info.msg_success, lang))
            else:
                return Response(_lt.get_label(errors.err_user_rights, lang), status=403)
        except (InvalidRequest, LookupError) as e:
            logging.exception(str(e))
            return Response(_lt.get_label(str(e), lang), status=400)
        except (UnauthorizedRequest, jwt.ExpiredSignatureError, jwt.InvalidSignatureError) as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_authorization, lang), status=401)
        except Exception as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_system_error, lang), status=500)
        finally:
            CONN_POOL.put_db_conn(conn)


@api_view(['POST'])
def is_organization_owner(request):
    '''
    Checks if a user is an admin of a given organization or not.
    :param request: Http request
    :return: Http response -> bool
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        try:
            user_perm = tokenizer.authorize_request(request)[2]
            return Response(permissions.is_user_owner(user_perm))
        except (UnauthorizedRequest, jwt.ExpiredSignatureError, jwt.InvalidSignatureError) as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_authorization, lang), status=401)
        except Exception as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_system_error, lang), status=500)


@api_view(['POST'])
def update_subdomain(request, conn=None, cache=None):
    '''
    Update an organization's subdomain.
    :param request: Http request
    :param conn: db connection
    :param cache: cache client
    :return: Http response
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.subdomain]
        try:
            conn = CONN_POOL.get_db_conn() if conn is None else conn
            cache = CACHE_CLIENT if cache is None else cache
            request_validator.validate_fields(request, expected_fields)
            user_id, org_id, user_perm, org_perm = tokenizer.authorize_request(request)

            if permissions.is_user_owner(user_perm):
                subdomain = request.data[var_names.subdomain]
                current_time = times.get_current_timestamp()

                if db_organizations.subdomain_is_available(conn, current_time, subdomain):
                    syncer_organizations.update_subdomain(conn, cache, times.get_current_timestamp(),
                                                          org_id, subdomain)
                    return Response(_lt.get_label(info.msg_org_subdomain_updated, lang))
                else:
                    return Response(_lt.get_label(errors.err_subdomain_unavailable, lang), status=401)
            else:
                return Response(_lt.get_label(errors.err_user_rights, lang), status=403)
        except (InvalidRequest, NotUniqueValue, ValueError) as e:
            logging.exception(str(e))
            return Response(_lt.get_label(str(e), lang), status=400)
        except (UnauthorizedRequest, jwt.ExpiredSignatureError, jwt.InvalidSignatureError) as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_authorization, lang), status=401)
        except Exception as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_system_error, lang), status=500)
        finally:
            CONN_POOL.put_db_conn(conn)


@api_view(['POST'])
def is_email_and_subdomain_unique(request, conn=None):
    '''
    Checks if a subdomain and email are unique or not.
    :param request: Http request
    :param conn: db connection
    :return: Http response -> boolean
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        try:
            conn = CONN_POOL.get_db_conn() if conn is None else conn
            expected_fields = [var_names.email, var_names.subdomain]
            request_validator.validate_fields(request, expected_fields)

            email = request.data[var_names.email].lower()
            subdomain = request.data[var_names.subdomain]

            if not string_validator.is_email_address(email):
                return Response(_lt.get_label(errors.err_invalid_email, lang), status=400)

            if ban_checker.is_source_malicious(conn, [(var_names.subdomain, email.split('@')[1])]):
                raise MaliciousSource(errors.err_malicious_source_restriction)

            if db_users.email_is_unique(conn, email, times.get_current_timestamp()):
                if db_organizations.subdomain_is_available(conn, times.get_current_timestamp(), subdomain):
                    return Response(_lt.get_label(info.msg_success, lang), status=200)
                else:
                    return Response(_lt.get_label(errors.err_subdomain_unavailable, lang), status=400)
            else:
                return Response(_lt.get_label(errors.err_requested_user_account_exists, lang), status=400)
        except InvalidRequest as e:
            logging.exception(str(e))
            return Response(_lt.get_label(str(e), lang), status=400)
        except Exception as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_system_error, lang), status=500)
        finally:
            CONN_POOL.put_db_conn(conn)


@api_view(['POST'])
def is_subdomain_available(request, conn=None):
    '''
    Checks if a subdomain is available or not.
    :param request: Http request
    :param conn: db connection
    :return: Http response -> boolean
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        try:
            conn = CONN_POOL.get_db_conn() if conn is None else conn
            expected_fields = [var_names.subdomain]
            request_validator.validate_fields(request, expected_fields)
            subdomain = request.data[var_names.subdomain]
            return Response(db_organizations.subdomain_is_available(conn, times.get_current_timestamp(), subdomain))
        except InvalidRequest as e:
            logging.exception(str(e))
            return Response(_lt.get_label(str(e), lang), status=400)
        except Exception as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_system_error, lang), status=500)
        finally:
            CONN_POOL.put_db_conn(conn)


@api_view(['POST'])
def get_organization_full_domain(request, conn=None):
    '''
    Gets the subdomain of an organization appended to the taskcall domain name.
    :param request: Http request
    :param conn: db connection
    :return: Http response -> str if there is one, None otherwise
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        try:
            conn = CONN_POOL.get_db_conn() if conn is None else conn
            user_id, org_id, user_permission, org_permission = tokenizer.authorize_request(request)

            subdomain = db_organizations.get_organization_subdomain(conn, times.get_current_timestamp(),
                                                                    organization_id=org_id)
            full_domain = helpers.construct_organization_full_domain(subdomain)
            return Response(full_domain)
        except (UnauthorizedRequest, jwt.ExpiredSignatureError, jwt.InvalidSignatureError) as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_authorization, lang), status=401)
        except Exception as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_system_error, lang), status=500)
        finally:
            CONN_POOL.put_db_conn(conn)


@api_view(['POST'])
def get_organization_sso_settings(request, conn=None):
    '''
    Get the SSO settings of an organization.
    :param request: Http request
    :param conn: db connection
    :return: Http response
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        optional_fields = [var_names.subdomain]
        try:
            conn = CONN_POOL.get_db_conn() if conn is None else conn
            request_validator.validate_fields(request, [], optional_fields)

            subdomain = request.data[var_names.subdomain] if var_names.subdomain in request.data else None
            org_id = None
            if subdomain is None:
                user_id, org_id, user_permission, org_permission = tokenizer.authorize_request(request)

            sso_settings = db_organizations.get_organization_sso_settings(
                conn, times.get_current_timestamp(), subdomain=subdomain, organization_id=org_id)

            if sso_settings is not None:
                return Response(sso_settings)
            else:
                return Response(_lt.get_label(errors.err_sso_settings_not_found, lang), status=404)
        except InvalidRequest as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_authorization, lang), status=401)
        except (UnauthorizedRequest, jwt.ExpiredSignatureError, jwt.InvalidSignatureError) as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_authorization, lang), status=401)
        except Exception as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_system_error, lang), status=500)
        finally:
            CONN_POOL.put_db_conn(conn)


@api_view(['POST'])
def save_organization_sso_settings(request, conn=None):
    '''
    Saves the SSO settings of an organization.
    :param request: Http request
    :param conn: db connection
    :return: Http response
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = []
        optional_fields = [var_names.integration_type, var_names.direct_login, var_names.auto_provision,
                           var_names.user_role, var_names.saml_certificate, var_names.saml_key,
                           var_names.login_url, var_names.logout_url, var_names.metadata_url,
                           var_names.entity_id, var_names.vendor_id, var_names.vendor_subdomain,
                           var_names.additional_info]
        try:
            conn = CONN_POOL.get_db_conn() if conn is None else conn
            request_validator.validate_fields(request, expected_fields, optional_fields)
            user_id, org_id, user_perm, org_perm = tokenizer.authorize_request(request)

            if not permissions.has_org_permission(org_perm, permissions.ORG_SSO_PERMISSION):
                return Response(_lt.get_label(errors.err_subscription_rights, lang), status=403)

            current_timestamp = times.get_current_timestamp()
            if var_names.integration_type not in request.data:
                db_organizations.delete_organization_sso_settings(conn, current_timestamp, org_id)
            else:
                db_organizations.update_organization_sso_settings(
                    conn, current_timestamp, org_id, request.data[var_names.integration_type],
                    direct_login=request.data[var_names.direct_login]
                    if var_names.direct_login in request.data else False,
                    auto_provision=request.data[var_names.auto_provision]
                    if var_names.auto_provision in request.data else False,
                    role_id=roles.get_role_id_from_name(request.data[var_names.user_role])
                    if var_names.user_role in request.data else None,
                    saml_certificate=request.data[var_names.saml_certificate]
                    if var_names.saml_certificate in request.data else None,
                    saml_key=request.data[var_names.saml_key] if var_names.saml_key in request.data else None,
                    login_url=request.data[var_names.login_url] if var_names.login_url in request.data else None,
                    logout_url=request.data[var_names.logout_url] if var_names.logout_url in request.data else None,
                    metadata_url=request.data[var_names.metadata_url]
                    if var_names.metadata_url in request.data else None,
                    entity_id=request.data[var_names.entity_id] if var_names.entity_id in request.data else None,
                    vendor_id=request.data[var_names.vendor_id] if var_names.vendor_id in request.data else None,
                    vendor_subdomain=request.data[var_names.vendor_subdomain]
                    if var_names.vendor_subdomain in request.data else None,
                    additional_info=request.data[var_names.additional_info]
                    if var_names.additional_info in request.data else None
                )

            return Response(_lt.get_label(info.msg_sso_settings_saved, lang))
        except InvalidRequest as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_authorization, lang), status=401)
        except Exception as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_system_error, lang), status=500)
        finally:
            CONN_POOL.put_db_conn(conn)


@api_view(['POST'])
def save_closure_verification(request, conn=None):
    '''
    Save verification code for closing an organization's account.
    :param request: Http request
    :param conn: db connection
    :return: Http response
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        try:
            conn = CONN_POOL.get_db_conn() if conn is None else conn
            user_id, org_id, user_perm, org_perm = tokenizer.authorize_request(request)

            start_timestamp = times.get_current_timestamp()
            end_timestamp = start_timestamp + datetime.timedelta(minutes=configs.closure_verification_lifetime)
            if permissions.is_user_owner(user_perm):
                org_name = db_organizations.get_organization_details(
                    conn, start_timestamp, with_organization_id=org_id
                )[var_names.organization_name]

                user_email = db_users.get_user_details(conn, start_timestamp, org_id, user_id=user_id)[var_names.email]

                # first - generate the verification code
                code = key_manager.generate_verification_code(constants.verification_code_length)
                code_hash, salt = credentials.convert_text_to_hash(code)

                # second - save the code in the database
                db_users.save_personal_account_verification_code(
                    conn, start_timestamp, end_timestamp, user_email, salt, code_hash,
                    verification_type=constants.account_closure_verification
                )

                # third - email the code
                subject, message = account_notices.account_closure_verification_content(lang, org_name, code)
                mail.AmazonSesDispatcher(subject, message, user_email, with_logo=True).start()
                return Response(_lt.get_label(info.msg_success, lang))
            else:
                return Response(_lt.get_label(errors.err_user_rights, lang), status=403)
        except InvalidRequest as e:
            logging.exception(str(e))
            return Response(_lt.get_label(str(e), lang), status=400)
        except (UnauthorizedRequest, jwt.ExpiredSignatureError, jwt.InvalidSignatureError) as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_authorization, lang), status=401)
        except Exception as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_system_error, lang), status=500)
        finally:
            CONN_POOL.put_db_conn(conn)


@api_view(['POST'])
def verify_closure(request, conn=None):
    '''
    Verify that an account closure request has genuinely come
    from the user whose account the request was made from.
    :param request: Http request
    :param conn: db connection
    :return: Http response
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.verification_code, var_names.ip_address]
        try:
            conn = CONN_POOL.get_db_conn() if conn is None else conn
            request_validator.validate_fields(request, expected_fields)
            user_id, org_id, user_perm, org_perm = tokenizer.authorize_request(request)

            code = request.data[var_names.verification_code]
            ip_addr = request.data[var_names.ip_address]

            if permissions.is_user_owner(user_perm):
                current_time = times.get_current_timestamp()
                user_email = db_users.get_user_details(conn, current_time, org_id, user_id=user_id)[var_names.email]

                if credentials.verification_code_matches(conn, current_time, user_email, code,
                                                         verification_type=constants.account_closure_verification):

                    tk_expiry = current_time + datetime.timedelta(minutes=configs.closure_token_lifetime)
                    closure_token = tokenizer.create_registration_token(user_email, ip_addr, tk_expiry)

                    return Response(closure_token)
                else:
                    return Response(_lt.get_label(errors.err_verification_failed, lang), status=400)
            else:
                return Response(_lt.get_label(errors.err_user_rights, lang), status=403)
        except KeyError as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_verification_failed, lang), status=400)
        except InvalidRequest as e:
            logging.exception(str(e))
            return Response(_lt.get_label(str(e), lang), status=400)
        except (UnauthorizedRequest, jwt.ExpiredSignatureError, jwt.InvalidSignatureError) as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_authorization, lang), status=401)
        except Exception as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_system_error, lang), status=500)
        finally:
            CONN_POOL.put_db_conn(conn)


@api_view(['POST'])
def close_organization(request, conn=None, cache=None):
    '''
    Close an organization account.
    :param request: Http request
    :param conn: db connection
    :param cache: cache client
    :return: Http response
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.token, var_names.ip_address]
        try:
            conn = CONN_POOL.get_db_conn() if conn is None else conn
            cache = CACHE_CLIENT if cache is None else cache
            request_validator.validate_fields(request, expected_fields)
            user_id, org_id, user_perm, org_perm = tokenizer.authorize_request(request)
            current_time = times.get_current_timestamp()

            if permissions.is_user_owner(user_perm):
                user_email = db_users.get_user_details(conn, current_time, org_id, user_id=user_id)[var_names.email]

                # make sure that the request contains a system generated valid closure token
                tk_email, tk_ip = tokenizer.read_registration_token(request.data[var_names.token])
                if tk_email != user_email or tk_ip != request.data[var_names.ip_address]:
                    raise InvalidRequest(errors.err_invalid_request)

                syncer_organizations.close_organization(conn, cache, current_time, org_id)

                # # Delete the items gathered earlier from external sites
                # if not settings.TEST_MODE and not settings.TEST_SERVER:
                #     # Get details of items that need to be deleted from other sites
                #     lcr_del_details = db_live_call_routing.get_live_call_routing_deletion_details(
                #         conn, current_time, org_id)
                #
                #     if len(lcr_del_details) > 0:
                #         for item in lcr_del_details:
                #             if item[var_names.vendor_name] == constants.twilio:
                #                 phone_sid = item[var_names.vendor_phone_id]
                #                 twilio_vendor = Twilio(Twilio.get_authentication())
                #                 twilio_vendor.delete_number(phone_sid)

                return Response(_lt.get_label(info.msg_success, lang))
            else:
                return Response(_lt.get_label(errors.err_user_rights, lang), status=403)
        except InvalidRequest as e:
            logging.exception(str(e))
            return Response(_lt.get_label(str(e), lang), status=400)
        except (UnauthorizedRequest, jwt.ExpiredSignatureError, jwt.InvalidSignatureError) as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_authorization, lang), status=401)
        except Exception as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_system_error, lang), status=500)
        finally:
            CONN_POOL.put_db_conn(conn)
