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

from data_syncers import syncer_services, syncer_task_instances
from dbqueries import db_integrations
from exceptions.user_exceptions import InvalidRequest, UnauthorizedRequest
from integrations import datadog
from modules.router import Router
from objects.events import CustomActionEvent, 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 constants, errors, info, integration_type_names as intt, logging, permissions, times,\
    tokenizer, var_names
from utils.db_connection import CACHE_CLIENT, CONN_POOL
from validations import request_validator, string_validator
import configuration
import jwt


@api_view(['POST'])
def update_datadog_account_tokens(request, conn=None):
    '''
    Checks if a given Datadog account is already mapped to a given TaskCall organization or not.
    This is an internal check. Only TaskCall web server should have access to this process.
    :param request: Http request
    :param conn: db connection
    :return: Http response
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.external_id, var_names.vendor_endpoint, var_names.access_token,
                           var_names.refresh_token]
        try:
            conn = CONN_POOL.get_db_conn() if conn is None else conn
            request_validator.validate_fields(request, expected_fields)
            user_id, org_id, user_perm, org_perm = tokenizer.authorize_request(request)
            if permissions.can_user_edit_components(user_perm, org_perm):
                external_info = {var_names.vendor_endpoint: request.data[var_names.vendor_endpoint],
                                 var_names.access_token: request.data[var_names.access_token],
                                 var_names.refresh_token: request.data[var_names.refresh_token]}
                return Response(db_integrations.check_and_update_organization_integration_details(
                    conn, times.get_current_timestamp(), org_id, intt.datadog,
                    request.data[var_names.external_id], external_info
                ))
            else:
                return Response(_lt.get_label(errors.err_user_rights, 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 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'])
def process_incoming_webhook(request, integration_key, conn=None, cache=None):
    '''
    Processes the incoming webhook from DataDog.
    :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

            inc_title, text_msg, alert_id, alert_scope, alert_status = 'Datadog Incident', None, None, None, None

            if datadog.var_incident_title in request.data and\
                isinstance(request.data[datadog.var_incident_title], str) and\
                    not string_validator.is_empty_string(request.data[datadog.var_incident_title]):
                inc_title = request.data[datadog.var_incident_title]

            elif datadog.var_title in request.data and isinstance(request.data[datadog.var_title], str) and\
                    not string_validator.is_empty_string(request.data[datadog.var_title]):
                inc_title = request.data[datadog.var_title]

            elif datadog.var_alert in request.data and datadog.var_title in request.data[datadog.var_alert] and\
                isinstance(request.data[datadog.var_alert][datadog.var_title], str) and\
                    not string_validator.is_empty_string(request.data[datadog.var_alert][datadog.var_title]):
                inc_title = request.data[datadog.var_alert][datadog.var_title]

            if datadog.var_text_msg in request.data:
                text_msg = request.data[datadog.var_text_msg]

            if datadog.var_alert in request.data:
                alert_attr = request.data[datadog.var_alert]
                alert_id = request.data[datadog.var_alert][datadog.var_id] if datadog.var_id in alert_attr else None
                alert_scope = request.data[datadog.var_alert][datadog.var_scope]\
                    if datadog.var_scope in alert_attr else None
                alert_status = request.data[datadog.var_alert][datadog.var_transition]\
                    if datadog.var_transition in alert_attr else None

            # incident uuid
            dg_inc_id = request.data[datadog.var_incident_uuid] if datadog.var_incident_uuid in request.data else None
            dg_addn_info = {var_names.display_name: request.data[datadog.var_incident_public_id]} \
                if datadog.var_incident_public_id in request.data else None

            # standardize the urgency
            urgency = constants.critical_urgency
            if datadog.var_incident_severity in request.data:
                urgency = datadog.get_standardized_severity(request.data[datadog.var_incident_severity])
            elif datadog.var_priority in request.data:
                urgency = datadog.get_standardized_alert_priority(request.data[datadog.var_priority])

            # snapshot, vendor_url
            dg_snapshot = [request.data[datadog.var_snapshot]]\
                if datadog.var_snapshot in request.data and request.data[datadog.var_snapshot] is not None and\
                request.data[datadog.var_snapshot] != 'null' else None

            dg_url = None
            if datadog.var_incident_url in request.data:
                dg_url = request.data[datadog.var_incident_url]
            elif datadog.var_link in request.data:
                dg_url = request.data[datadog.var_link]

            current_time = times.get_current_timestamp()
            retrieved_integration_information = db_integrations.get_integration_details(
                conn, current_time, integration_key, intt.datadog)
            if retrieved_integration_information is None:
                logging.error(errors.err_integration_not_found)
                return Response(_lt.get_label(errors.err_integration_not_found, lang), status=404)
            else:
                org_id, org_perm, serv_id, integ_id, integ_type_id, integ_details = retrieved_integration_information

            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_alert_attr = source_payload[datadog.var_alert]

                    if (source_alert_attr[datadog.var_id] == alert_id and
                        source_alert_attr[datadog.var_scope] == alert_scope) or (
                        datadog.var_incident_uuid in source_payload and
                            source_payload[datadog.var_incident_uuid] == dg_inc_id):

                        match_count += 1
                        if alert_status == datadog.recovered_status:
                            event = ResolveEvent(inst_id, current_time, constants.integrations_api)
                            syncer_task_instances.resolve(conn, cache, event, org_id, is_sys_action=True)
                        else:
                            # Group the alert if one already exists
                            payload = TaskPayload(
                                current_time, org_id, current_time.date(), inc_title, configuration.standard_timezone,
                                current_time.time(), text_msg=text_msg, 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()

            if match_count == 0 and alert_status in datadog.starting_statuses:
                payload = TaskPayload(
                    current_time, org_id, current_time.date(), inc_title, configuration.standard_timezone,
                    current_time.time(), text_msg=text_msg, 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=dg_snapshot, vendor_incident_url=dg_url
                )
                inst_id = Router(conn, cache, payload).run()

                if inst_id is not None and dg_inc_id is not None:
                    event = CustomActionEvent(
                        inst_id, current_time, constants.integrations_api, integration_id=integ_id,
                        integration_type_id=integ_type_id, vendor_id=dg_inc_id, vendor_url=dg_url, is_synced=True,
                        additional_info=dg_addn_info
                    )
                    syncer_task_instances.execute_custom_action(conn, cache, event, org_id=org_id, is_sys_action=True)
            else:
                msg = 'Alert received from DataDog with status - ' + alert_status + '. No action taken.'
                logging.info(msg)
            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)
