# By: Riasat Ullah
# This file contains all Sentry service integration related views.

from data_syncers import syncer_services, syncer_task_instances
from dbqueries import db_integrations
from dbqueries.integrations import db_sentry
from exceptions.user_exceptions import InvalidRequest
from integrations import sentry
from modules.router import Router
from objects.events import ResolveEvent
from objects.task_payload import TaskPayload
from rest_framework.decorators import api_view
from rest_framework.response import Response
from translators import label_translator as _lt
from utils import cdn_enablers, constants, errors, info, integration_type_names as intt, logging, times, var_names
from utils.db_connection import CACHE_CLIENT, CONN_POOL
from validations import request_validator
import configuration


@api_view(['POST'])
def process_incoming_webhook(request, conn=None, cache=None):
    '''
    Processes the incoming webhook from Sentry.
    :param request: Http request
    :param conn: db connection
    :param cache: cache client
    :return: Http response
    '''
    logging.info(request.headers)
    logging.info(request.data)
    if request.method == 'POST':
        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()
            installation_id = request.data[sentry.var_installation][sentry.var_uuid]
            alert_action = request.data[sentry.var_action]
            body_data = request.data[sentry.var_data]
            alert_type = sentry.get_alert_type(body_data)

            if alert_action == sentry.created_action and alert_type == sentry.installation_alert_type:
                logging.info('Overlooking installation alert from Sentry. Installation ID: ' + str(installation_id))
                return Response(info.msg_internal_success)

            org_id, serv_id, integ_id, integ_key = db_sentry.get_sentry_integration_details(conn, current_time,
                                                                                            installation_id)
            if org_id is None or serv_id is None or integ_id is None:
                return Response(_lt.get_label(errors.err_unknown_resource, lang), status=400)

            # If integration is being deleted.
            if alert_action == sentry.deleted_action and alert_type == sentry.installation_alert_type:
                logging.info('Installation deletion alert from Sentry. Installation ID: ' + str(installation_id))
                cdn_key_info = (
                    intt.sentry, cdn_enablers.create_cdn_cache_key(intt.sentry, installation_id)
                )
                syncer_services.delete_service_integration(conn, cache, times.get_current_timestamp(), org_id, None,
                                                           integ_key, check_adv_perm=False, cdn_key_info=cdn_key_info)
                return Response(info.msg_internal_success)

            alert_id = sentry.get_alert_id(body_data, alert_type)
            alert_title = sentry.get_alert_title(body_data, alert_type)
            alert_body = sentry.get_alert_description(body_data, alert_type)
            urgency = constants.critical_urgency

            integ_insts = db_integrations.get_integration_open_instances_trigger_info(
                conn, current_time, org_id, serv_id, integ_id)

            match_count = 0
            for inst_id, task_id, trig_info in integ_insts:
                if trig_info is not None:
                    source_body_data = trig_info[var_names.source_payload][sentry.var_data]
                    source_alert_type = sentry.get_alert_type(source_body_data)
                    source_alert_id = sentry.get_alert_id(source_body_data, source_alert_type)

                    # Update the status of the instance created if this is a recovery alert.
                    if source_alert_type == alert_type and source_alert_id == alert_id:

                        # Group the alert if one already exists
                        if alert_action in [sentry.created_action, sentry.triggered_action, sentry.critical_action,
                                            sentry.warning_action]:
                            payload = TaskPayload(
                                current_time, org_id, current_time.date(), alert_title, configuration.standard_timezone,
                                current_time.time(), text_msg=alert_body, urgency_level=urgency,
                                trigger_method=constants.integrations_api, trigger_info=request.data,
                                integration_id=integ_id, integration_key=integ_key, service_id=serv_id,
                                instantiate=False, alert=False, related_task_id=task_id,
                                task_status=constants.grouped_state
                            )
                            Router(conn, cache, payload).start()
                        elif alert_action == sentry.resolved_action:
                            event = ResolveEvent(inst_id, current_time, constants.integrations_api)
                            syncer_task_instances.resolve(conn, cache, event, org_id, is_sys_action=True)

                        break

            # Create a new task if this is a new alert.
            if match_count == 0:
                payload = TaskPayload(
                    current_time, org_id, current_time.date(), alert_title, configuration.standard_timezone,
                    current_time.time(), text_msg=alert_body, urgency_level=urgency,
                    trigger_method=constants.integrations_api, trigger_info=request.data, integration_id=integ_id,
                    integration_key=integ_key, service_id=serv_id
                )
                Router(conn, cache, payload).start()

            return Response(info.msg_internal_success)
        except InvalidRequest as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_invalid_request, 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)
