# By: Riasat Ullah
# This file contains custom integration related views.

from data_syncers import syncer_task_instances
from dbqueries import db_integrations
from exceptions.user_exceptions import InvalidRequest
from integrations import custom_integration
from modules.router import Router
from objects.events import AcknowledgeEvent, ResolveEvent, UrgencyAmendmentEvent
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 constants, errors, integration_type_names as intt, logging, times, var_names
from utils.db_connection import CACHE_CLIENT, CONN_POOL
from validations import request_validator, string_validator
import configuration


@api_view(['POST'])
def process_incoming_webhook(request, integration_key, conn=None, cache=None):
    '''
    Processes the incoming webhook from custom integrations.
    :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':
        logging.info(request.build_absolute_uri())
        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()
            org_id, org_perm, serv_id, integ_id, integ_type_id, integ_details = \
                db_integrations.get_integration_details(conn, current_time, integration_key, intt.custom_integration)

            if var_names.url_format in integ_details:
                full_url = request.build_absolute_uri()
                if not custom_integration.is_url_format_valid(integ_details[var_names.url_format], full_url):
                    logging.error(errors.err_invalid_url + ' - ' + full_url)
                    return Response(_lt.get_label(errors.err_invalid_url, lang), status=403)

            req_values = custom_integration.get_tc_field_values(integ_details[var_names.payload], request.data)
            title = req_values[var_names.title] if var_names.title in req_values else None
            if title is None or string_validator.is_empty_string(title):
                title = 'Custom Integration - New Incident'

            description = req_values[var_names.description] if var_names.description in req_values else None
            if description is None or string_validator.is_email_address(description):
                description = str(request.data)
            else:
                if integ_details[var_names.show_payload]:
                    description += '\n\n' + str(request.data)

            status = custom_integration.get_status(
                integ_details[var_names.status],
                req_values[var_names.status] if var_names.status in req_values else None
            )
            urgency = custom_integration.get_urgency_level(
                integ_details[var_names.urgency_level],
                req_values[var_names.urgency_level] if var_names.urgency_level in req_values else None
            )
            snapshots = custom_integration.get_snapshots(
                req_values[var_names.snapshots] if var_names.snapshots in req_values else None)
            tags = req_values[var_names.tags] if var_names.tags in req_values else None
            notes = req_values[var_names.notes] if var_names.notes in req_values else None
            dedup_key = req_values[var_names.dedup_key] if var_names.dedup_key in req_values else None

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

            inst_id = None
            match_count = 0
            if var_names.group_by in integ_details and integ_details[var_names.group_by] is not None:
                for inst_id, task_id, trig_info in integ_insts:
                    if trig_info is not None:
                        source_payload = trig_info[var_names.source_payload]
                        if custom_integration.is_alert_similar(source_payload, request.data,
                                                               integ_details[var_names.group_by]):
                            match_count += 1

                            if var_names.urgency_level in trig_info and urgency != trig_info[var_names.urgency_level]:
                                event = UrgencyAmendmentEvent(
                                    inst_id, current_time, constants.integrations_api, urgency)
                                syncer_task_instances.amend_urgency(conn, cache, [event], org_id, is_sys_action=True)

                            # Group the alert if one already exists
                            if status == 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 status == 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(), title,
                                    configuration.standard_timezone, current_time.time(), text_msg=description,
                                    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, snapshots=snapshots, tags=tags,
                                    notes=notes, dedup_key=dedup_key
                                )
                                Router(conn, cache, payload).start()

            # Create a new task if this is a new alert.
            if match_count == 0:
                if status != constants.resolved_state:
                    payload = TaskPayload(
                        current_time, org_id, current_time.date(), title, configuration.standard_timezone,
                        current_time.time(), text_msg=description, urgency_level=urgency,
                        trigger_method=constants.integrations_api, trigger_info=request.data, integration_id=integ_id,
                        integration_key=integration_key, service_id=serv_id, snapshots=snapshots, tags=tags,
                        notes=notes, dedup_key=dedup_key
                    )
                    inst_id = Router(conn, cache, payload).run()
                else:
                    inst_id = None
                    msg = 'Custom Integration (' + str(integration_key) + ') alert received with status ' + status + \
                          'that is not associated with an incident. ' + 'No action taken.'
                    logging.info(msg)
                    logging.info(request.data)

            resp_data = {
                var_names.instance_id: inst_id,
                var_names.timestamp: current_time,
                var_names.status: status
            }
            return Response(resp_data)
        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)
