# By: Riasat Ullah
# This file contains api access views.

from data_syncers import syncer_api_keys
from dbqueries import db_api_keys
from exceptions.user_exceptions import InvalidRequest, NotUniqueValue, UnauthorizedRequest
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 constants, errors, info, key_manager, logging, permissions, times, tokenizer, var_names
from utils.db_connection import CACHE_CLIENT, CONN_POOL
from validations import request_validator
import jwt


@api_view(['POST'])
def get_global_api_keys(request, conn=None):
    '''
    Gets the global API keys 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)
        try:
            conn = CONN_POOL.get_db_conn() if conn is None else conn
            request_validator.validate_fields(request, [])
            user_id, org_id, user_perm, org_perm = tokenizer.authorize_request(request)

            if permissions.has_org_permission(org_perm, permissions.ORG_INCIDENTS_API_PERMISSION):
                current_time = times.get_current_timestamp()

                if permissions.has_user_permission(user_perm, permissions.USER_GLOBAL_REST_API_KEY_EDIT_PERMISSION):
                    api_keys = db_api_keys.get_api_keys(conn, current_time, org_id, only_global=True)
                    return Response(api_keys)
                else:
                    return Response(_lt.get_label(errors.err_user_rights, lang), status=403)
            else:
                return Response(_lt.get_label(errors.err_subscription_rights, lang), status=403)
        except (UnauthorizedRequest, jwt.ExpiredSignatureError, jwt.InvalidSignatureError) as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_authorization, lang), status=401)
        except PermissionError as e:
            logging.exception(str(e))
            return Response(_lt.get_label(str(e), 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 create_api_key(request, conn=None, cache=None):
    '''
    Creates an API key that can be used with the incidents api.
    :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.key_name, var_names.ip_address]
        optional_fields = [var_names.for_user]
        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, optional_fields)
            user_id, org_id, user_perm, org_perm = tokenizer.authorize_request(request)

            if permissions.has_org_permission(org_perm, permissions.ORG_INCIDENTS_API_PERMISSION):

                current_time = times.get_current_timestamp()
                key_name = request.data[var_names.key_name]
                is_user_specific = request.data[var_names.for_user] if var_names.for_user in request.data else False
                for_user_id = user_id if is_user_specific else None
                ip_restrictions = request.data[var_names.ip_address]

                user_perm_needed = permissions.USER_SPECIFIC_REST_API_KEY_EDIT_PERMISSION if is_user_specific\
                    else permissions.USER_GLOBAL_REST_API_KEY_EDIT_PERMISSION

                if permissions.has_user_permission(user_perm, user_perm_needed):

                    api_key = key_manager.generate_api_key(settings.REGION)
                    api_access = constants.api_full_access
                    api_version = constants.api_version_incidents_one

                    syncer_api_keys.create_api_key(conn, cache, current_time, user_id, org_id, api_key, key_name,
                                                   api_access, api_version, for_user_id=for_user_id,
                                                   ip_restrictions=ip_restrictions)
                    return Response(api_key)
                else:
                    return Response(_lt.get_label(errors.err_user_rights, lang), status=403)
            else:
                return Response(_lt.get_label(errors.err_subscription_rights, lang), status=403)
        except (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_authorization, lang), status=401)
        except PermissionError as e:
            logging.exception(str(e))
            return Response(_lt.get_label(str(e), 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 edit_api_key(request, conn=None, cache=None):
    '''
    Edits an API key.
    :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.key, var_names.key_name]
        optional_fields = [var_names.is_enabled, 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, optional_fields)
            user_id, org_id, user_perm, org_perm = tokenizer.authorize_request(request)

            if permissions.has_org_permission(org_perm, permissions.ORG_INCIDENTS_API_PERMISSION):
                current_time = times.get_current_timestamp()

                api_key_id = db_api_keys.get_api_key_id_from_sub_key_and_name(
                    conn, current_time, org_id, request.data[var_names.key], request.data[var_names.key_name]
                )
                to_enable = request.data[var_names.is_enabled] if var_names.is_enabled in request.data else None
                ip_rest = request.data[var_names.ip_address] if var_names.ip_address in request.data else None
                key_owner = db_api_keys.get_owner_if_api_key_user_specific(conn, current_time, api_key_id)

                if key_owner is None and not permissions.has_user_permission(
                        user_perm, permissions.USER_GLOBAL_REST_API_KEY_EDIT_PERMISSION):
                    raise PermissionError(errors.err_user_rights)
                if key_owner is not None:
                    if key_owner != user_id or not permissions.is_user_admin(user_perm):
                        raise PermissionError(errors.err_user_rights)

                syncer_api_keys.edit_api_key(conn, cache, current_time, org_id, api_key_id,
                                             to_enable=to_enable, ip_restrictions=ip_rest)
                return Response(_lt.get_label(info.msg_api_key_edited, lang))
            else:
                return Response(_lt.get_label(errors.err_subscription_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 PermissionError as e:
            logging.exception(str(e))
            return Response(_lt.get_label(str(e), 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 delete_api_key(request, conn=None, cache=None):
    '''
    Deletes an API key.
    :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.key, var_names.key_name]
        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.has_org_permission(org_perm, permissions.ORG_INCIDENTS_API_PERMISSION):
                current_time = times.get_current_timestamp()

                api_key_id = db_api_keys.get_api_key_id_from_sub_key_and_name(
                    conn, current_time, org_id, request.data[var_names.key], request.data[var_names.key_name]
                )
                key_owner = db_api_keys.get_owner_if_api_key_user_specific(conn, current_time, api_key_id)

                if key_owner is None and not permissions.has_user_permission(
                        user_perm, permissions.USER_GLOBAL_REST_API_KEY_EDIT_PERMISSION):
                    raise PermissionError(errors.err_user_rights)
                if key_owner is not None:
                    if key_owner != user_id or not permissions.is_user_admin(user_perm):
                        raise PermissionError(errors.err_user_rights)

                syncer_api_keys.delete_api_key(conn, cache, current_time, org_id, api_key_id)
                return Response(_lt.get_label(info.msg_api_key_deleted, lang))
            else:
                return Response(_lt.get_label(errors.err_subscription_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 PermissionError as e:
            logging.exception(str(e))
            return Response(_lt.get_label(str(e), 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)
