# By: Riasat Ullah
# This file contains Instana 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 instana
from modules.router import Router
from objects.events import 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, info, key_manager, 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, integration_key, conn=None, cache=None):
    '''
    Processes the incoming webhook from Instana.
    :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)

            issue_body = request.data[instana.var_issue]
            alert_type = issue_body[instana.var_type] if instana.var_type in issue_body else None

            if alert_type == instana.issue_alert or alert_type == instana.incident_alert or alert_type is None:

                alert_id = issue_body[instana.var_id]
                alert_state = issue_body[instana.var_state]

                # For closed incident notifications there is no alert type and they lack details
                if alert_type is not None:
                    alert_title = issue_body[instana.var_text]
                    alert_body = issue_body[instana.var_suggestion] + '\n\n' + str(issue_body)
                    alert_severity = issue_body[instana.var_severity]
                    urgency = instana.severity_mapping[alert_severity]
                else:
                    alert_title, alert_body, alert_severity, urgency = None, None, None, 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)

                # Update the status of the instance created if this is a recovery alert.
                # Create a new task if this is a new alert.
                integ_insts = db_integrations.get_integration_open_instances_trigger_info(
                    conn, current_time, org_id, serv_id, integ_id)

                for inst_id, task_id, trig_info in integ_insts:
                    if trig_info is not None:
                        source_issue = trig_info[var_names.source_payload][instana.var_issue]
                        source_severity = source_issue[instana.var_severity]

                        if source_issue[instana.var_id] == alert_id:
                            if alert_state == instana.status_closed:
                                event = ResolveEvent(inst_id, current_time, constants.integrations_api)
                                syncer_task_instances.resolve(conn, cache, event, org_id, is_sys_action=True)
                            elif alert_state == instana.status_open and source_severity == alert_severity:
                                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=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()
                            elif alert_state == instana.status_open and source_severity != alert_severity:
                                event = UrgencyAmendmentEvent(inst_id, current_time, constants.integrations_api,
                                                              instana.severity_mapping[alert_severity])
                                syncer_task_instances.amend_urgency(conn, cache, [event], org_id, is_sys_action=True)

                            break
                else:
                    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=integration_key, service_id=serv_id
                    )
                    Router(conn, cache, payload).start()
            else:
                logging.error('Unknown or non-processable alert type received from Instana - ' + alert_type)

            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)
