# By: Riasat Ullah
# This file contains N-central integration related views.

from data_syncers import syncer_services, syncer_task_instances
from dbqueries import db_integrations
from exceptions.user_exceptions import InvalidRequest
from integrations import n_central
from modules.router import Router
from objects.events import AcknowledgeEvent, ResolveEvent
from objects.task_payload import TaskPayload
from rest_framework.decorators import api_view, parser_classes
from rest_framework.response import Response
from translators import label_translator as _lt
from utils import constants, errors, info, key_manager, logging, times, var_names
from utils.custom_parsers import XmlParser
from utils.db_connection import CACHE_CLIENT, CONN_POOL
from validations import request_validator
import configuration


@api_view(['POST'])
@parser_classes((XmlParser,))
def process_incoming_webhook(request, integration_key, conn=None, cache=None):
    '''
    Processes the incoming webhook from N-central.
    :param request: Http request
    :param integration_key: integration key passed in the url
    :param conn: db connection
    :param cache: cache client
    :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
            cache = CACHE_CLIENT if cache is None else cache

            unmasked_integ_key = key_manager.unmask_reference_key(integration_key)

            alert = request.data[n_central.var_notification]
            nc_trigger_id = alert[n_central.var_active_notification_trigger_id]\
                if n_central.var_active_notification_trigger_id in alert else None
            nc_service = alert[n_central.var_affected_service] if n_central.var_affected_service in alert else None
            nc_customer = alert[n_central.var_customer_name] if n_central.var_customer_name in alert else None
            nc_device = alert[n_central.var_device_name] if n_central.var_device_name in alert else None
            nc_new_state = alert[n_central.var_qualitative_new_state]\
                if n_central.var_qualitative_new_state in alert else None
            nc_ack_time = alert[n_central.var_acknowledgement_time]\
                if n_central.var_acknowledgement_time in alert else None

            if nc_trigger_id is None:
                logging.error('Rejecting N-central alert: missing ActiveNotificationTriggerID attribute')
                return Response(_lt.get_label(errors.err_invalid_request, lang), status=400)

            title_list = []
            if nc_new_state is not None:
                title_list.append(nc_new_state)
            if nc_customer is not None:
                title_list.append(nc_customer)
            if nc_device is not None:
                title_list.append(nc_device)
            if nc_service is not None:
                title_list.append(nc_service)

            if len(title_list) > 0:
                alert_title = ' | '.join(title_list)
            else:
                alert_title = 'N-central Alert Received'

            tc_conv_state = n_central.get_tc_mapped_status(nc_new_state, nc_ack_time)
            urgency = constants.medium_urgency if nc_new_state.lower() == n_central.warning_state.lower()\
                else constants.critical_urgency

            current_time = times.get_current_timestamp()
            integ_id, serv_id, org_id = syncer_services.get_integration_key_details(
                conn, cache, current_time, unmasked_integ_key)

            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_payload = trig_info[var_names.source_payload]
                    source_trigger_id = source_payload[n_central.var_notification][
                        n_central.var_active_notification_trigger_id]\
                        if n_central.var_notification in source_payload and\
                        n_central.var_active_notification_trigger_id in source_payload[n_central.var_notification]\
                        else None

                    # Update the status of the instance created if this is a recovery alert.
                    if nc_trigger_id == source_trigger_id:
                        match_count += 1

                        # Group the alert if one already exists
                        if tc_conv_state == constants.acknowledged_state:
                            event = AcknowledgeEvent(inst_id, current_time, constants.integrations_api, None)
                            syncer_task_instances.acknowledge(conn, cache, event, org_id, is_sys_action=True)
                        elif tc_conv_state == constants.resolved_state:
                            event = ResolveEvent(inst_id, current_time, constants.integrations_api)
                            syncer_task_instances.resolve(conn, cache, event, org_id, is_sys_action=True)
                        else:
                            payload = TaskPayload(
                                current_time, org_id, current_time.date(), alert_title,
                                configuration.standard_timezone, current_time.time(), text_msg=str(alert),
                                urgency_level=urgency, trigger_method=constants.integrations_api,
                                trigger_info=request.data, integration_id=integ_id, integration_key=integration_key,
                                service_id=serv_id, instantiate=False, alert=False, related_task_id=task_id,
                                task_status=constants.grouped_state
                            )
                            Router(conn, cache, payload).start()

            # Create a new task if this is a new alert.
            if match_count == 0:
                if tc_conv_state != constants.resolved_state:
                    payload = TaskPayload(
                        current_time, org_id, current_time.date(), alert_title, configuration.standard_timezone,
                        current_time.time(), text_msg=str(alert), urgency_level=urgency,
                        trigger_method=constants.integrations_api, trigger_info=request.data, integration_id=integ_id,
                        integration_key=integration_key, service_id=serv_id
                    )
                    Router(conn, cache, payload).start()
                else:
                    msg = 'Alert with Normal state received from N-central that is not associated with an incident. ' +\
                          'No action taken.'
                    logging.info(msg)
                    logging.info(request.data)

            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)
