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

from data_syncers import syncer_api_keys, syncer_organizations, syncer_task_instances
from dbqueries import db_business_services, db_events, db_policies, db_services, db_tasks, db_task_instances, \
    db_users, db_workflows
from exceptions.user_exceptions import InvalidRequest, NoOneIsOnCall, UnauthorizedRequest
from modules.alert_logger import AlertLogger
from modules.event_allocator import EventAllocator
from modules.notice_allocator import NoticeAllocator
from modules.router import Router
from modules.workflow_manager import WorkflowManager
from objects.instance_state import InstanceState
from objects.events import AcknowledgeEvent, AddImpactedBusinessServiceEvent, AddRespondersEvent, \
    AddSubscribersEvent, EditTitleEvent, MergeEvent, NotateEvent, ReassignEvent, RemoveImpactedBusinessServiceEvent, \
    RemoveSubscribersEvent, ResolveEvent, SnoozeEvent, StatusUpdateEvent, UnAcknowledgeEvent, UnMergeEvent, \
    UpdateTagsEvent, UrgencyAmendmentEvent
from objects.task_payload import TaskPayload
from rest_framework.decorators import api_view, throttle_classes
from rest_framework.response import Response
from rest_framework.throttling import UserRateThrottle
from translators import label_translator as _lt
from utils import app_notice, constants, errors, info, logging, mail, permissions, \
    times, tokenizer, var_names
from utils.db_connection import CACHE_CLIENT, CONN_POOL
from validations import request_validator
import configuration
import jwt


@api_view(['GET'])
@throttle_classes([UserRateThrottle])
def get_component_list(request, component_type, conn=None, cache=None):
    '''
    Get the list of components given the type of component(s).
    :param request: Http request
    :param component_type: (str) type of component
    :param conn: db connection
    :param cache: cache client
    :return: Http response
    '''
    if request.method == 'GET':
        lang = request_validator.get_user_language(request)
        try:
            conn = CONN_POOL.get_db_conn() if conn is None else conn
            cache = CACHE_CLIENT if cache is None else cache

            current_time = times.get_current_timestamp()
            api_key = tokenizer.extract_api_key(request)
            details = syncer_api_keys.get_api_key_details(conn, cache, current_time, api_key)
            if details is None:
                raise UnauthorizedRequest(errors.err_api_key_request_denied)
            key_id, org_id, perm, for_user, ip_restrictions = details

            request_validator.validate_api_ip_allowed(request, ip_restrictions)
            if not permissions.has_user_permission(perm, permissions.USER_COMPONENTS_VIEW_PERMISSION):
                raise PermissionError(errors.err_api_key_permission)

            # standardize component type
            component_type = component_type.replace('-', '_')

            if component_type == var_names.users:
                data = db_users.get_basic_user_list(conn, current_time, org_id)

            elif component_type == var_names.escalation_policies:
                data = db_policies.get_basic_policies_list(conn, current_time, org_id, group_only=True)

            elif component_type == var_names.policies:
                data = db_policies.get_basic_policies_list(conn, current_time, org_id, group_only=False)

            elif component_type == var_names.services:
                data = db_services.get_basic_services_list(conn, current_time, org_id)

            elif component_type == var_names.business_services:
                data = db_business_services.get_basic_business_services_list(conn, current_time, org_id)

            elif component_type == var_names.response_sets:
                data = []

            elif component_type == {var_names.workflows}:
                data = db_workflows.get_basic_workflows_list(conn, current_time, org_id)

            elif component_type == var_names.incidents:
                data = db_task_instances.get_org_active_instances_basic_list(conn, current_time, org_id)

            else:
                logging.error(errors.err_unknown_resource + ' - ' + str(component_type))
                raise InvalidRequest(errors.err_unknown_resource)

            return Response(data)
        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 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'])
@throttle_classes([UserRateThrottle])
def list_instances(request, conn=None, cache=None):
    '''
    List the incidents.
    :param request: Http request
    :param conn: db connection
    :param cache: cache client
    :return: Http response -> list of dict
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        optional_fields = [var_names.status, var_names.urgency_level, var_names.service_ref_id, var_names.team_ref_id,
                           var_names.keywords, var_names.start_timestamp, var_names.end_timestamp]
        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, [], optional_fields)

            current_time = times.get_current_timestamp()
            api_key = tokenizer.extract_api_key(request)
            details = syncer_api_keys.get_api_key_details(conn, cache, current_time, api_key)
            if details is None:
                raise UnauthorizedRequest(errors.err_api_key_request_denied)
            key_id, org_id, perm, for_user, ip_restrictions = details
            request_validator.validate_api_ip_allowed(request, ip_restrictions)

            status_type = request.data[var_names.status] if var_names.status in request.data else None
            urgency = request.data[var_names.urgency_level] if var_names.urgency_level in request.data else None
            serv_ref_id = request.data[var_names.service_ref_id] if var_names.service_ref_id in request.data else None
            team_ref_id = request.data[var_names.team_ref_id] if var_names.team_ref_id in request.data else None
            keywords = request.data[var_names.keywords] if var_names.keywords in request.data else None
            check_start = times.get_timestamp_from_string(request.data[var_names.start_timestamp])\
                if var_names.start_timestamp in request.data else None
            check_end = times.get_timestamp_from_string(request.data[var_names.end_timestamp])\
                if var_names.end_timestamp in request.data else None

            only_open_instances = None
            if status_type is not None:
                if status_type in [constants.open_state, constants.acknowledged_state]:
                    only_open_instances = True
                else:
                    only_open_instances = False

            if permissions.has_user_permission(perm, permissions.USER_INCIDENTS_VIEW_PERMISSION):
                incidents = db_task_instances.list_instances(
                    conn, times.get_current_timestamp(), org_id,
                    open_instances=only_open_instances, instance_status=status_type,
                    urgency=urgency, service_ref_id=serv_ref_id, team_ref_id=team_ref_id, search_words=keywords,
                    min_timestamp=check_start, max_timestamp=check_end, row_limit=200
                )
                return Response(incidents)
            else:
                return Response(_lt.get_label(errors.err_api_key_permission, 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'])
@throttle_classes([UserRateThrottle])
def get_instance_details(request, conn=None, cache=None):
    '''
    Get the details of an incident.
    :param request: Http request
    :param conn: db connection
    :param cache: cache client
    :return: Http response -> list of dict
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.instance_id]
        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)

            current_time = times.get_current_timestamp()
            api_key = tokenizer.extract_api_key(request)
            details = syncer_api_keys.get_api_key_details(conn, cache, current_time, api_key)
            if details is None:
                raise UnauthorizedRequest(errors.err_api_key_request_denied)
            key_id, org_id, perm, for_user, ip_restrictions = details
            request_validator.validate_api_ip_allowed(request, ip_restrictions)

            instance_id = request.data[var_names.instance_id]

            if permissions.has_user_permission(perm, permissions.USER_INCIDENTS_VIEW_PERMISSION):
                inst_obj_dict = db_task_instances.get_instance_details(conn, current_time, org_id,
                                                                       instance_id=instance_id)
                inst_obj = list(inst_obj_dict.values())[0]
                return Response(inst_obj.to_dict())
            else:
                return Response(_lt.get_label(errors.err_api_key_permission, lang), status=403)
        except InvalidRequest as 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'])
@throttle_classes([UserRateThrottle])
def list_instance_alerts(request, conn=None, cache=None):
    '''
    List the alerts that have occurred on an incident.
    :param request: Http request
    :param conn: db connection
    :param cache: cache client
    :return: Http response -> list of dict
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.instance_id]
        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)

            current_time = times.get_current_timestamp()
            api_key = tokenizer.extract_api_key(request)
            details = syncer_api_keys.get_api_key_details(conn, cache, current_time, api_key)
            if details is None:
                raise UnauthorizedRequest(errors.err_api_key_request_denied)
            key_id, org_id, perm, for_user, ip_restrictions = details
            request_validator.validate_api_ip_allowed(request, ip_restrictions)

            instance_id = request.data[var_names.instance_id]

            if permissions.has_user_permission(perm, permissions.USER_INCIDENTS_CREATE_PERMISSION):
                alert_list = db_tasks.get_task_alerts(conn, current_time, org_id, instance_id=instance_id)
                return Response(alert_list)
            else:
                return Response(_lt.get_label(errors.err_api_key_permission, 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'])
@throttle_classes([UserRateThrottle])
def create_incident(request, conn=None, cache=None):
    '''
    Gets a list of incidents with basic information.
    :param request: Http request
    :param conn: db connection
    :param cache: cache client
    :return: Http response -> list of dict
    '''
    if request.method == 'POST':
        expected_fields = [var_names.title]
        optional_fields = [var_names.service_ref_id, var_names.users, var_names.policy_ref_id,
                           var_names.description, var_names.urgency_level, var_names.notes,
                           var_names.tags, var_names.dedup_key, var_names.snapshots]

        lang = request_validator.get_user_language(request)
        try:
            conn = CONN_POOL.get_db_conn() if conn is None else conn
            cache = CACHE_CLIENT if cache is None else cache

            current_time = times.get_current_timestamp()
            api_key = tokenizer.extract_api_key(request)
            details = syncer_api_keys.get_api_key_details(conn, cache, current_time, api_key)
            if details is None:
                raise UnauthorizedRequest(errors.err_api_key_request_denied)
            key_id, org_id, perm, for_user, ip_restrictions = details

            request_validator.validate_api_ip_allowed(request, ip_restrictions)
            if not permissions.has_user_permission(perm, permissions.USER_INCIDENTS_CREATE_PERMISSION):
                raise PermissionError(errors.err_api_key_permission)

            request_validator.validate_fields(request, expected_fields, optional_fields)

            serv_ref_id = request.data[var_names.service_ref_id] if var_names.service_ref_id in request.data else None
            pol_ref_ids = request.data[var_names.policy_ref_id] if var_names.policy_ref_id in request.data else None
            user_prefs = request.data[var_names.users] if var_names.users in request.data else None
            title = request.data[var_names.title]
            description = request.data[var_names.description] if var_names.description in request.data else None
            urgency = request.data[var_names.urgency_level]\
                if var_names.urgency_level in request.data else configuration.default_urgency_level
            notes = request.data[var_names.notes] if var_names.notes in request.data else None
            tags = request.data[var_names.tags] if var_names.tags in request.data else None
            dedup_key = request.data[var_names.dedup_key] if var_names.dedup_key in request.data else None
            snap_images = request.data[var_names.snapshots]\
                if var_names.snapshots in request.data and request.data[var_names.snapshots] is not None else None
            if snap_images is not None and not isinstance(snap_images, list):
                snap_images = [snap_images]

            if (serv_ref_id is not None and not isinstance(serv_ref_id, str)) or\
                (pol_ref_ids is not None and not isinstance(pol_ref_ids, list)) or\
                (user_prefs is not None and not isinstance(user_prefs, list)) or\
                (urgency is not None and not isinstance(urgency, int)) or\
                    (tags is not None and not isinstance(tags, list)):
                raise InvalidRequest(errors.err_invalid_request)

            user_pol_ids = []
            if user_prefs is not None and len(user_prefs) > 0:
                user_pol_ids = db_users.get_user_policy_ids_from_preferred_usernames(
                    conn, current_time, org_id, user_prefs)
                user_pol_ids = user_pol_ids if len(user_pol_ids) > 0 else None

            if serv_ref_id is None and pol_ref_ids is None and len(user_pol_ids) == 0:
                raise InvalidRequest(errors.err_invalid_request)

            source_payload = request.data

            payload = TaskPayload(current_time, org_id, current_time.date(), title,
                                  configuration.standard_timezone, current_time.time(),
                                  text_msg=description, urgency_level=urgency, notes=notes, tags=tags,
                                  dedup_key=dedup_key, api_key_id=key_id,
                                  trigger_method=constants.incidents_api, trigger_info=source_payload,
                                  service_ref_id=serv_ref_id, assignees_ref=pol_ref_ids, assignees=user_pol_ids,
                                  snapshots=snap_images)

            inst_id = Router(conn, cache, payload).run()
            resp_data = {
                var_names.instance_id: inst_id,
                var_names.timestamp: current_time
            }
            return Response(resp_data)
        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 NoOneIsOnCall as e:
            logging.exception(str(e))
            return Response(_lt.get_label(str(e), lang), status=412)
        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'])
@throttle_classes([UserRateThrottle])
def acknowledge(request, conn=None, cache=None):
    '''
    Acknowledge an incident.
    :param request: Http request
    :param conn: db connection
    :param cache: cache client
    :return: Http response -> list of dict
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.instance_id]
        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)

            current_time = times.get_current_timestamp()
            api_key = tokenizer.extract_api_key(request)
            details = syncer_api_keys.get_api_key_details(conn, cache, current_time, api_key)
            if details is None:
                raise UnauthorizedRequest(errors.err_api_key_request_denied)
            key_id, org_id, perm, for_user, ip_restrictions = details
            request_validator.validate_api_ip_allowed(request, ip_restrictions)

            instance_id = request.data[var_names.instance_id]
            access_method = constants.incidents_api
            if for_user is not None:
                has_comp_perm, has_team_perm, is_sys_action = True, True, False
            else:
                has_comp_perm, has_team_perm, is_sys_action = False, False, True

            if permissions.can_user_respond_to_incidents(perm):
                org_perm = syncer_organizations.get_single_organization_permission(conn, cache, current_time, org_id)
                event = AcknowledgeEvent(instance_id, current_time, access_method, for_user)
                updated_next_alert = syncer_task_instances.acknowledge(
                    conn, cache, event, org_id=org_id, has_comp_perm=has_comp_perm, has_team_perm=has_team_perm,
                    is_sys_action=is_sys_action, org_perm=org_perm
                )
                resp_data = {var_names.next_alert_timestamp: updated_next_alert}
                return Response(resp_data)
            else:
                return Response(_lt.get_label(errors.err_api_key_permission, 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'])
@throttle_classes([UserRateThrottle])
def resolve(request, conn=None, cache=None):
    '''
    Resolve an incident.
    :param request: Http request
    :param conn: db connection
    :param cache: cache client
    :return: Http response -> list of dict
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.instance_id]
        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)

            current_time = times.get_current_timestamp()
            api_key = tokenizer.extract_api_key(request)
            details = syncer_api_keys.get_api_key_details(conn, cache, current_time, api_key)
            if details is None:
                raise UnauthorizedRequest(errors.err_api_key_request_denied)
            key_id, org_id, perm, for_user, ip_restrictions = details

            request_validator.validate_api_ip_allowed(request, ip_restrictions)

            instance_id = request.data[var_names.instance_id]
            access_method = constants.incidents_api
            if for_user:
                has_comp_perm, has_team_perm, is_sys_action = True, True, False
            else:
                has_comp_perm, has_team_perm, is_sys_action = False, False, True

            event = ResolveEvent(instance_id, current_time, access_method, for_user)
            syncer_task_instances.resolve(conn, cache, event, org_id=org_id, has_comp_perm=has_comp_perm,
                                          has_team_perm=has_team_perm, is_sys_action=is_sys_action)
            return Response(_lt.get_label(info.msg_success, lang))
        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'])
@throttle_classes([UserRateThrottle])
def escalate(request, conn=None, cache=None):
    '''
    Escalate an incident.
    :param request: Http request
    :param conn: db connection
    :param cache: cache client
    :return: Http response -> list of dict
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.instance_id]
        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)

            event_timestamp = times.get_current_timestamp()
            api_key = tokenizer.extract_api_key(request)
            details = syncer_api_keys.get_api_key_details(conn, cache, event_timestamp, api_key)
            if details is None:
                raise UnauthorizedRequest(errors.err_api_key_request_denied)
            key_id, org_id, perm, for_user, ip_restrictions = details

            request_validator.validate_api_ip_allowed(request, ip_restrictions)

            instance_id = request.data[var_names.instance_id]
            access_method = constants.incidents_api
            if for_user:
                has_comp_perm, has_team_perm, is_sys_action = True, True, False
            else:
                has_comp_perm, has_team_perm, is_sys_action = False, False, True

            if permissions.can_user_respond_to_incidents(perm):
                instance = db_task_instances.get_instances(conn, event_timestamp, instance_id=instance_id)[instance_id]

                # handle group escalations
                all_policies = []
                policy_ids_from_instances = [assignee.for_policy_id for assignee in instance.assignees]
                if len(policy_ids_from_instances) > 0:
                    all_policies = db_policies.get_policies(conn, event_timestamp,
                                                            with_policyid=policy_ids_from_instances)

                allocator = EventAllocator(all_policies, None, None, None, None)
                allocator.handle_instance_escalation(event_timestamp, instance, escalated_by=for_user,
                                                     access_method=access_method)
                if len(allocator.escalations) == 0:
                    logging.error(errors.err_policy_no_escalations_available)
                    return Response(_lt.get_label(errors.err_policy_no_escalations_available, lang), status=400)
                else:
                    syncer_task_instances.escalate(conn, cache, allocator.escalations, org_id=org_id,
                                                   has_comp_perm=has_comp_perm, has_team_perm=has_team_perm,
                                                   is_sys_action=is_sys_action)
                    return Response(_lt.get_label(info.msg_success, lang))
            else:
                return Response(_lt.get_label(errors.err_api_key_permission, 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'])
@throttle_classes([UserRateThrottle])
def reassign(request, conn=None, cache=None):
    '''
    Reassign an incident.
    :param request: Http request
    :param conn: db connection
    :param cache: cache client
    :return: Http response -> list of dict
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.instance_id, var_names.reassign_to]
        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)

            current_time = times.get_current_timestamp()
            api_key = tokenizer.extract_api_key(request)
            details = syncer_api_keys.get_api_key_details(conn, cache, current_time, api_key)
            if details is None:
                raise UnauthorizedRequest(errors.err_api_key_request_denied)
            key_id, org_id, perm, for_user, ip_restrictions = details

            request_validator.validate_api_ip_allowed(request, ip_restrictions)

            instance_id = request.data[var_names.instance_id]
            reassign_to = request.data[var_names.reassign_to]
            access_method = constants.incidents_api
            if for_user:
                has_comp_perm, has_team_perm, is_sys_action = True, True, False
            else:
                has_comp_perm, has_team_perm, is_sys_action = False, False, True

            if permissions.can_user_respond_to_incidents(perm):

                new_pol_ids = db_policies.list_policy_ids_from_ref_ids(conn, current_time, org_id, reassign_to,
                                                                       unmasked=False)
                event = ReassignEvent(instance_id, current_time, access_method, for_user, new_pol_ids)
                syncer_task_instances.reassign(conn, cache, event, org_id=org_id, has_comp_perm=has_comp_perm,
                                               has_team_perm=has_team_perm, is_sys_action=is_sys_action)
                return Response(_lt.get_label(info.msg_success, lang))
            else:
                return Response(_lt.get_label(errors.err_api_key_permission, 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'])
@throttle_classes([UserRateThrottle])
def snooze(request, conn=None, cache=None):
    '''
    Snooze an incident.
    :param request: Http request
    :param conn: db connection
    :param cache: cache client
    :return: Http response -> list of dict
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.instance_id, var_names.snooze_for]
        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)

            current_time = times.get_current_timestamp()
            api_key = tokenizer.extract_api_key(request)
            details = syncer_api_keys.get_api_key_details(conn, cache, current_time, api_key)
            if details is None:
                raise UnauthorizedRequest(errors.err_api_key_request_denied)
            key_id, org_id, perm, for_user, ip_restrictions = details

            request_validator.validate_api_ip_allowed(request, ip_restrictions)

            instance_id = request.data[var_names.instance_id]
            snooze_for = request.data[var_names.snooze_for]
            access_method = constants.incidents_api
            if for_user:
                has_comp_perm, has_team_perm, is_sys_action = True, True, False
            else:
                has_comp_perm, has_team_perm, is_sys_action = False, False, True

            if permissions.can_user_respond_to_incidents(perm):
                event = SnoozeEvent(instance_id, current_time, access_method, for_user, snooze_for)
                syncer_task_instances.snooze(conn, cache, event, org_id=org_id, has_comp_perm=has_comp_perm,
                                             has_team_perm=has_team_perm, is_sys_action=is_sys_action)
                return Response(_lt.get_label(info.msg_success, lang))
            else:
                return Response(_lt.get_label(errors.err_api_key_permission, 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'])
@throttle_classes([UserRateThrottle])
def un_acknowledge(request, conn=None, cache=None):
    '''
    Un-acknowledge an incident.
    :param request: Http request
    :param conn: db connection
    :param cache: cache client
    :return: Http response -> list of dict
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.instance_id]
        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)

            current_time = times.get_current_timestamp()
            api_key = tokenizer.extract_api_key(request)
            details = syncer_api_keys.get_api_key_details(conn, cache, current_time, api_key)
            if details is None:
                raise UnauthorizedRequest(errors.err_api_key_request_denied)
            key_id, org_id, perm, for_user, ip_restrictions = details

            request_validator.validate_api_ip_allowed(request, ip_restrictions)

            instance_id = request.data[var_names.instance_id]
            access_method = constants.incidents_api
            if for_user:
                has_comp_perm, has_team_perm, is_sys_action = True, True, False
            else:
                has_comp_perm, has_team_perm, is_sys_action = False, False, True

            if permissions.can_user_respond_to_incidents(perm):
                instance = db_task_instances.get_instances(conn, current_time, instance_id=instance_id)[instance_id]
                pol_dict = db_policies.get_policies(conn, current_time, with_policyid=list(instance.for_policy_ids()))
                for_policies = list(pol_dict.values()) if len(pol_dict) > 0 else []
                next_alert_time = InstanceState.calculate_next_alert_timestamp(instance.level, for_policies,
                                                                               current_time)

                event = UnAcknowledgeEvent(instance_id, current_time, access_method, for_user, next_alert_time)
                syncer_task_instances.un_acknowledge(conn, cache, event, org_id=org_id, has_comp_perm=has_comp_perm,
                                                     has_team_perm=has_team_perm, is_sys_action=is_sys_action)
                return Response(_lt.get_label(info.msg_success, lang))
            else:
                return Response(_lt.get_label(errors.err_api_key_permission, 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'])
@throttle_classes([UserRateThrottle])
def amend_urgency(request, conn=None, cache=None):
    '''
    Amend the urgency of an incident.
    :param request: Http request
    :param conn: db connection
    :param cache: cache client
    :return: Http response -> list of dict
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.instance_id, var_names.urgency_level]
        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)

            current_time = times.get_current_timestamp()
            api_key = tokenizer.extract_api_key(request)
            details = syncer_api_keys.get_api_key_details(conn, cache, current_time, api_key)
            if details is None:
                raise UnauthorizedRequest(errors.err_api_key_request_denied)
            key_id, org_id, perm, for_user, ip_restrictions = details

            request_validator.validate_api_ip_allowed(request, ip_restrictions)

            instance_id = request.data[var_names.instance_id]
            new_urgency = request.data[var_names.urgency_level]
            access_method = constants.incidents_api
            if for_user:
                has_comp_perm, has_team_perm, is_sys_action = True, True, False
            else:
                has_comp_perm, has_team_perm, is_sys_action = False, False, True

            if permissions.can_user_respond_to_incidents(perm):
                event = UrgencyAmendmentEvent(instance_id, current_time, access_method, new_urgency, for_user)
                syncer_task_instances.amend_urgency(conn, cache, [event], org_id=org_id, has_comp_perm=has_comp_perm,
                                                    has_team_perm=has_team_perm, is_sys_action=is_sys_action)
                return Response(_lt.get_label(info.msg_success, lang))
            else:
                return Response(_lt.get_label(errors.err_api_key_permission, 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'])
@throttle_classes([UserRateThrottle])
def add_responders(request, conn=None, cache=None):
    '''
    Add new responders to an incident.
    :param request: Http request
    :param conn: db connection
    :param cache: cache client
    :return: Http response -> list of dict
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.instance_id, var_names.assignees]
        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)

            current_time = times.get_current_timestamp()
            api_key = tokenizer.extract_api_key(request)
            details = syncer_api_keys.get_api_key_details(conn, cache, current_time, api_key)
            if details is None:
                raise UnauthorizedRequest(errors.err_api_key_request_denied)
            key_id, org_id, perm, for_user, ip_restrictions = details

            request_validator.validate_api_ip_allowed(request, ip_restrictions)

            instance_id = request.data[var_names.instance_id]
            new_assignees = request.data[var_names.assignees]
            access_method = constants.incidents_api
            if for_user:
                has_comp_perm, has_team_perm, is_sys_action = True, True, False
            else:
                has_comp_perm, has_team_perm, is_sys_action = False, False, True

            org_perm = syncer_organizations.get_single_organization_permission(conn, cache, current_time, org_id)
            if not permissions.has_org_permission(org_perm, permissions.ORG_ADD_RESPONDERS_PERMISSION):
                return Response(_lt.get_label(errors.err_subscription_rights, lang), status=403)

            if not permissions.can_user_respond_to_incidents(perm):
                return Response(_lt.get_label(errors.err_api_key_permission, lang), status=403)

            new_pol_ids = db_policies.list_policy_ids_from_ref_ids(conn, current_time, org_id, new_assignees,
                                                                   unmasked=False)

            event = AddRespondersEvent(instance_id, current_time, access_method, new_pol_ids, for_user)
            notifier = NoticeAllocator()
            notifier = syncer_task_instances.add_responders(
                conn, cache, event, org_id=org_id, has_comp_perm=has_comp_perm,
                has_team_perm=has_team_perm, is_sys_action=is_sys_action, notifier=notifier
            )

            if len(notifier.email_messages) > 0:
                mail.AmazonSesBulkDispatcher(notifier.email_messages).start()

            if len(notifier.push_notices) > 0:
                app_notice.NoticeSender(notifier.push_notices).start()

            if len(notifier.alert_logs) > 0:
                AlertLogger(conn, notifier.alert_logs).start()

            return Response(_lt.get_label(info.msg_success, lang))
        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'])
@throttle_classes([UserRateThrottle])
def notate(request, conn=None, cache=None):
    '''
    Add a note to an incident.
    :param request: Http request
    :param conn: db connection
    :param cache: cache client
    :return: Http response -> list of dict
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.instance_id, var_names.notes]
        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)

            current_time = times.get_current_timestamp()
            api_key = tokenizer.extract_api_key(request)
            details = syncer_api_keys.get_api_key_details(conn, cache, current_time, api_key)
            if details is None:
                raise UnauthorizedRequest(errors.err_api_key_request_denied)
            key_id, org_id, perm, for_user, ip_restrictions = details

            request_validator.validate_api_ip_allowed(request, ip_restrictions)

            instance_id = request.data[var_names.instance_id]
            note = request.data[var_names.notes]
            access_method = constants.incidents_api
            if for_user:
                has_comp_perm, has_team_perm, is_sys_action = True, True, False
            else:
                has_comp_perm, has_team_perm, is_sys_action = False, False, True

            if permissions.can_user_respond_to_incidents(perm):
                event = NotateEvent(instance_id, current_time, access_method, for_user, note)
                db_events.book_notate_event(conn, event, org_id, has_comp_perm, has_team_perm, is_sys_action)
                return Response(_lt.get_label(info.msg_success, lang))
            else:
                return Response(_lt.get_label(errors.err_api_key_permission, 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'])
@throttle_classes([UserRateThrottle])
def merge(request, conn=None, cache=None):
    '''
    Merge two incidents together.
    :param request: Http request
    :param conn: db connection
    :param cache: cache client
    :return: Http response -> list of dict
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.instance_id, var_names.related_instance_id]
        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)

            current_time = times.get_current_timestamp()
            api_key = tokenizer.extract_api_key(request)
            details = syncer_api_keys.get_api_key_details(conn, cache, current_time, api_key)
            if details is None:
                raise UnauthorizedRequest(errors.err_api_key_request_denied)
            key_id, org_id, perm, for_user, ip_restrictions = details

            request_validator.validate_api_ip_allowed(request, ip_restrictions)

            instance_id = request.data[var_names.instance_id]
            rel_instance_id = request.data[var_names.related_instance_id]
            access_method = constants.incidents_api
            if for_user:
                has_comp_perm, has_team_perm, is_sys_action = True, True, False
            else:
                has_comp_perm, has_team_perm, is_sys_action = False, False, True

            if permissions.can_user_respond_to_incidents(perm):
                if instance_id != rel_instance_id:
                    event = MergeEvent(instance_id, current_time, access_method, rel_instance_id, for_user)
                    syncer_task_instances.merge(conn, cache, event, org_id=org_id, has_comp_perm=has_comp_perm,
                                                has_team_perm=has_team_perm, is_sys_action=is_sys_action)
                return Response(_lt.get_label(info.msg_success, lang))
            else:
                return Response(_lt.get_label(errors.err_api_key_permission, 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'])
@throttle_classes([UserRateThrottle])
def un_merge(request, conn=None, cache=None):
    '''
    Un-merge an alert from an incident.
    :param request: Http request
    :param conn: db connection
    :param cache: cache client
    :return: Http response -> list of dict
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.instance_id, var_names.task_ref_id]
        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)

            current_time = times.get_current_timestamp()
            api_key = tokenizer.extract_api_key(request)
            details = syncer_api_keys.get_api_key_details(conn, cache, current_time, api_key)
            if details is None:
                raise UnauthorizedRequest(errors.err_api_key_request_denied)
            key_id, org_id, perm, for_user, ip_restrictions = details

            request_validator.validate_api_ip_allowed(request, ip_restrictions)

            instance_id = request.data[var_names.instance_id]
            access_method = constants.incidents_api
            task_ref_id = request.data[var_names.task_ref_id]
            task_ref_id_map = db_tasks.get_task_ids_from_ref_ids(conn, current_time, org_id, [task_ref_id],
                                                                 unmasked=False)
            if len(task_ref_id_map) == 0:
                raise InvalidRequest(errors.err_unknown_resource)
            task_id = list(task_ref_id_map.values())[0]

            if for_user:
                has_comp_perm, has_team_perm, is_sys_action = True, True, False
            else:
                has_comp_perm, has_team_perm, is_sys_action = False, False, True

            if permissions.can_user_respond_to_incidents(perm):
                event = UnMergeEvent(instance_id, current_time, access_method, task_id, for_user)
                new_inst_id = syncer_task_instances.un_merge(conn, cache, event, org_id=org_id,
                                                             has_comp_perm=has_comp_perm, has_team_perm=has_team_perm,
                                                             is_sys_action=is_sys_action)
                resp_data = {
                    var_names.instance_id: new_inst_id,
                    var_names.timestamp: current_time
                }
                return Response(resp_data)
            else:
                return Response(_lt.get_label(errors.err_api_key_permission, 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'])
@throttle_classes([UserRateThrottle])
def add_subscribers(request, conn=None, cache=None):
    '''
    Add subscribers to an incident.
    :param request: Http request
    :param conn: db connection
    :param cache: cache client
    :return: Http response -> list of dict
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.instance_id, var_names.subscribers]
        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)

            current_time = times.get_current_timestamp()
            api_key = tokenizer.extract_api_key(request)
            details = syncer_api_keys.get_api_key_details(conn, cache, current_time, api_key)
            if details is None:
                raise UnauthorizedRequest(errors.err_api_key_request_denied)
            key_id, org_id, perm, for_user, ip_restrictions = details

            request_validator.validate_api_ip_allowed(request, ip_restrictions)

            instance_id = request.data[var_names.instance_id]
            access_method = constants.incidents_api
            subscriber_pref_names = request.data[var_names.subscribers]
            subscriber_user_ids = db_users.get_user_ids_from_preferred_usernames(
                conn, current_time, org_id, subscriber_pref_names)

            if for_user:
                has_comp_perm, has_team_perm, is_sys_action = True, True, False
            else:
                has_comp_perm, has_team_perm, is_sys_action = False, False, True

            org_perm = syncer_organizations.get_single_organization_permission(conn, cache, current_time, org_id)
            if not permissions.has_org_permission(org_perm, permissions.ORG_INCIDENT_STATUS_PERMISSION):
                return Response(_lt.get_label(errors.err_subscription_rights, lang), status=403)

            if not permissions.can_user_respond_to_incidents(perm):
                return Response(_lt.get_label(errors.err_api_key_permission, lang), status=403)

            if len(subscriber_user_ids) > 0:
                event = AddSubscribersEvent(instance_id, current_time, access_method, subscriber_user_ids, for_user)
                syncer_task_instances.add_subscribers(conn, cache, event, org_id=org_id,
                                                      has_comp_perm=has_comp_perm, has_team_perm=has_team_perm,
                                                      is_sys_action=is_sys_action)
            return Response(_lt.get_label(info.msg_success, lang))
        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'])
@throttle_classes([UserRateThrottle])
def remove_subscribers(request, conn=None, cache=None):
    '''
    Remove subscribers from an incident.
    :param request: Http request
    :param conn: db connection
    :param cache: cache client
    :return: Http response -> list of dict
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.instance_id, var_names.subscribers]
        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)

            current_time = times.get_current_timestamp()
            api_key = tokenizer.extract_api_key(request)
            details = syncer_api_keys.get_api_key_details(conn, cache, current_time, api_key)
            if details is None:
                raise UnauthorizedRequest(errors.err_api_key_request_denied)
            key_id, org_id, perm, for_user, ip_restrictions = details

            request_validator.validate_api_ip_allowed(request, ip_restrictions)

            instance_id = request.data[var_names.instance_id]
            access_method = constants.incidents_api
            subscriber_pref_names = request.data[var_names.subscribers]
            subscriber_user_ids = db_users.get_user_ids_from_preferred_usernames(
                conn, current_time, org_id, subscriber_pref_names)

            if for_user:
                has_comp_perm, has_team_perm, is_sys_action = True, True, False
            else:
                has_comp_perm, has_team_perm, is_sys_action = False, False, True

            org_perm = syncer_organizations.get_single_organization_permission(conn, cache, current_time, org_id)
            if not permissions.has_org_permission(org_perm, permissions.ORG_INCIDENT_STATUS_PERMISSION):
                return Response(_lt.get_label(errors.err_subscription_rights, lang), status=403)

            if not permissions.can_user_respond_to_incidents(perm):
                return Response(_lt.get_label(errors.err_api_key_permission, lang), status=403)

            if len(subscriber_user_ids) > 0:
                event = RemoveSubscribersEvent(instance_id, current_time, access_method, subscriber_user_ids,
                                               for_user)
                syncer_task_instances.remove_subscribers(conn, cache, event, org_id=org_id,
                                                         has_comp_perm=has_comp_perm, has_team_perm=has_team_perm,
                                                         is_sys_action=is_sys_action)
            return Response(_lt.get_label(info.msg_success, lang))
        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'])
@throttle_classes([UserRateThrottle])
def update_status(request, conn=None, cache=None):
    '''
    Update the status of an incident.
    :param request: Http request
    :param conn: db connection
    :param cache: cache client
    :return: Http response -> list of dict
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.instance_id, var_names.status_update]
        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)

            current_time = times.get_current_timestamp()
            api_key = tokenizer.extract_api_key(request)
            details = syncer_api_keys.get_api_key_details(conn, cache, current_time, api_key)
            if details is None:
                raise UnauthorizedRequest(errors.err_api_key_request_denied)
            key_id, org_id, perm, for_user, ip_restrictions = details

            request_validator.validate_api_ip_allowed(request, ip_restrictions)

            instance_id = request.data[var_names.instance_id]
            new_status = request.data[var_names.status_update]
            access_method = constants.incidents_api

            if for_user:
                has_comp_perm, has_team_perm, is_sys_action = True, True, False
            else:
                has_comp_perm, has_team_perm, is_sys_action = False, False, True

            org_perm = syncer_organizations.get_single_organization_permission(conn, cache, current_time, org_id)
            if not permissions.has_org_permission(org_perm, permissions.ORG_INCIDENT_STATUS_PERMISSION):
                return Response(_lt.get_label(errors.err_subscription_rights, lang), status=403)

            if not permissions.can_user_respond_to_incidents(perm):
                return Response(_lt.get_label(errors.err_api_key_permission, lang), status=403)

            event = StatusUpdateEvent(instance_id, current_time, access_method, new_status, for_user)
            syncer_task_instances.update_status(
                conn, cache, event, org_id, org_perm=org_perm, has_comp_perm=has_comp_perm,
                has_team_perm=has_team_perm, is_sys_action=is_sys_action
            )
            return Response(_lt.get_label(info.msg_success, lang))
        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'])
@throttle_classes([UserRateThrottle])
def add_impacting_business_service(request, conn=None, cache=None):
    '''
    Add a business impact manually.
    :param request: Http request
    :param conn: db connection
    :param cache: cache client
    :return: Http response -> list of dict
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.instance_id, var_names.business_service_ref_id]
        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)

            current_time = times.get_current_timestamp()
            api_key = tokenizer.extract_api_key(request)
            details = syncer_api_keys.get_api_key_details(conn, cache, current_time, api_key)
            if details is None:
                raise UnauthorizedRequest(errors.err_api_key_request_denied)
            key_id, org_id, perm, for_user, ip_restrictions = details

            request_validator.validate_api_ip_allowed(request, ip_restrictions)

            instance_id = request.data[var_names.instance_id]
            bus_ref_id = request.data[var_names.business_service_ref_id]
            access_method = constants.incidents_api

            if for_user:
                has_comp_perm, has_team_perm, is_sys_action = True, True, False
            else:
                has_comp_perm, has_team_perm, is_sys_action = False, False, True

            org_perm = syncer_organizations.get_single_organization_permission(conn, cache, current_time, org_id)
            if not permissions.has_org_permission(org_perm, permissions.ORG_INCIDENT_STATUS_PERMISSION):
                return Response(_lt.get_label(errors.err_subscription_rights, lang), status=403)

            if not permissions.can_user_respond_to_incidents(perm):
                return Response(_lt.get_label(errors.err_api_key_permission, lang), status=403)

            bus_id, bus_name = db_business_services.get_business_service_id_and_name_from_ref_id(
                conn, current_time, org_id, bus_ref_id
            )

            event = AddImpactedBusinessServiceEvent(instance_id, current_time, access_method, bus_id, for_user)
            syncer_task_instances.add_impacted_business_service(conn, cache, event, bus_name, org_id=org_id,
                                                                has_comp_perm=has_comp_perm,
                                                                has_team_perm=has_team_perm,
                                                                is_sys_action=is_sys_action)
            return Response(_lt.get_label(info.msg_success, lang))
        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'])
@throttle_classes([UserRateThrottle])
def remove_impacting_business_service(request, conn=None, cache=None):
    '''
    Remove the impacted status of a business service.
    :param request: Http request
    :param conn: db connection
    :param cache: cache client
    :return: Http response -> list of dict
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.instance_id, var_names.business_service_ref_id]
        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)

            current_time = times.get_current_timestamp()
            api_key = tokenizer.extract_api_key(request)
            details = syncer_api_keys.get_api_key_details(conn, cache, current_time, api_key)
            if details is None:
                raise UnauthorizedRequest(errors.err_api_key_request_denied)
            key_id, org_id, perm, for_user, ip_restrictions = details

            request_validator.validate_api_ip_allowed(request, ip_restrictions)

            instance_id = request.data[var_names.instance_id]
            bus_ref_id = request.data[var_names.business_service_ref_id]
            access_method = constants.incidents_api

            if for_user:
                has_comp_perm, has_team_perm, is_sys_action = True, True, False
            else:
                has_comp_perm, has_team_perm, is_sys_action = False, False, True

            org_perm = syncer_organizations.get_single_organization_permission(conn, cache, current_time, org_id)
            if not permissions.has_org_permission(org_perm, permissions.ORG_INCIDENT_STATUS_PERMISSION):
                return Response(_lt.get_label(errors.err_subscription_rights, lang), status=403)

            if not permissions.can_user_respond_to_incidents(perm):
                return Response(_lt.get_label(errors.err_api_key_permission, lang), status=403)

            bus_id, bus_name = db_business_services.get_business_service_id_and_name_from_ref_id(
                conn, current_time, org_id, bus_ref_id
            )

            event = RemoveImpactedBusinessServiceEvent(instance_id, current_time, access_method, bus_id, for_user)
            syncer_task_instances.remove_impacted_business_service(conn, cache, event, org_id=org_id,
                                                                   has_comp_perm=has_comp_perm,
                                                                   has_team_perm=has_team_perm,
                                                                   is_sys_action=is_sys_action)
            return Response(_lt.get_label(info.msg_success, lang))
        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'])
@throttle_classes([UserRateThrottle])
def run_workflow(request, conn=None, cache=None):
    '''
    Run a workflow.
    :param request: Http request
    :param conn: db connection
    :param cache: cache client
    :return: Http response -> list of dict
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.instance_id, var_names.workflow_ref_id]
        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)

            current_time = times.get_current_timestamp()
            api_key = tokenizer.extract_api_key(request)
            details = syncer_api_keys.get_api_key_details(conn, cache, current_time, api_key)
            if details is None:
                raise UnauthorizedRequest(errors.err_api_key_request_denied)
            key_id, org_id, perm, for_user, ip_restrictions = details
            request_validator.validate_api_ip_allowed(request, ip_restrictions)

            org_perm = syncer_organizations.get_single_organization_permission(conn, cache, current_time, org_id)
            if not permissions.has_org_permission(org_perm, permissions.ORG_WORKFLOWS_PERMISSION):
                return Response(_lt.get_label(errors.err_subscription_rights, lang), status=403)

            instance_id = request.data[var_names.instance_id]
            flow_ref_id = request.data[var_names.workflow_ref_id]
            current_time = times.get_current_timestamp()
            flow_id = db_workflows.get_workflow_id_from_ref_id(conn, current_time, org_id, flow_ref_id)

            if not db_workflows.has_run_permission(conn, current_time, org_id, flow_id, for_user,
                                                   permissions.can_user_respond_to_incidents(perm)):
                return Response(_lt.get_label(errors.err_user_rights, lang), status=403)

            WorkflowManager(conn, cache, org_id, org_perm, instance_id, workflow_ids=[flow_id],
                            access_method=constants.incidents_api, lang=lang).execute_workflow()

            return Response(_lt.get_label(info.msg_success, lang))
        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'])
@throttle_classes([UserRateThrottle])
def edit_title(request, conn=None, cache=None):
    '''
    Edit the title of an incident.
    :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.instance_id, var_names.task_title]
        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)

            current_time = times.get_current_timestamp()
            api_key = tokenizer.extract_api_key(request)
            details = syncer_api_keys.get_api_key_details(conn, cache, current_time, api_key)
            if details is None:
                raise UnauthorizedRequest(errors.err_api_key_request_denied)
            key_id, org_id, perm, for_user, ip_restrictions = details
            request_validator.validate_api_ip_allowed(request, ip_restrictions)

            # make sure the rights permissions are present
            org_perm = syncer_organizations.get_single_organization_permission(conn, cache, current_time, org_id)
            if not permissions.has_org_permission(org_perm, permissions.ORG_WORKFLOWS_PERMISSION):
                return Response(_lt.get_label(errors.err_subscription_rights, lang), status=403)

            if not permissions.can_user_respond_to_incidents(perm):
                return Response(_lt.get_label(errors.err_user_rights, lang), status=403)

            instance_id = request.data[var_names.instance_id]
            new_title = request.data[var_names.task_title]
            access_method = constants.incidents_api
            if for_user:
                has_comp_perm, has_team_perm, is_sys_action = True, True, False
            else:
                has_comp_perm, has_team_perm, is_sys_action = False, False, True

            event = EditTitleEvent(instance_id, current_time, access_method, for_user, new_title)
            syncer_task_instances.edit_title(
                conn, cache, event, org_id=org_id, has_comp_perm=has_comp_perm, has_team_perm=has_team_perm,
                is_sys_action=is_sys_action
            )
            return Response(_lt.get_label(info.msg_success, lang))
        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 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'])
@throttle_classes([UserRateThrottle])
def update_tags(request, conn=None, cache=None):
    '''
    Update tags associated to an incident.
    :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.instance_id, var_names.tags]
        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)

            current_time = times.get_current_timestamp()
            api_key = tokenizer.extract_api_key(request)
            details = syncer_api_keys.get_api_key_details(conn, cache, current_time, api_key)
            if details is None:
                raise UnauthorizedRequest(errors.err_api_key_request_denied)
            key_id, org_id, perm, for_user, ip_restrictions = details
            request_validator.validate_api_ip_allowed(request, ip_restrictions)

            # make sure the rights permissions are present
            org_perm = syncer_organizations.get_single_organization_permission(conn, cache, current_time, org_id)
            if not permissions.has_org_permission(org_perm, permissions.ORG_CONTEXTUAL_SEARCH_PERMISSION):
                return Response(_lt.get_label(errors.err_subscription_rights, lang), status=403)

            if not permissions.can_user_respond_to_incidents(perm):
                return Response(_lt.get_label(errors.err_user_rights, lang), status=403)

            instance_id = request.data[var_names.instance_id]
            tags = request.data[var_names.tags]
            access_method = constants.incidents_api
            if for_user:
                has_comp_perm, has_team_perm, is_sys_action = True, True, False
            else:
                has_comp_perm, has_team_perm, is_sys_action = False, False, True

            event = UpdateTagsEvent(instance_id, current_time, access_method, tags, for_user)

            db_events.book_update_tags_event(
                conn, event, org_id=org_id, has_comp_perm=has_comp_perm, has_team_perm=has_team_perm,
                is_sys_action=is_sys_action
            )
            return Response(_lt.get_label(info.msg_success, lang))
        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 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)
