# By: Riasat Ullah
# This file contains ManageEngine ServiceDesk Plus related views.

from data_syncers import syncer_task_instances
from dbqueries import db_integrations
from dbqueries.integrations import db_haloitsm
from exceptions.user_exceptions import InvalidRequest, UnauthorizedRequest
from integrations import haloitsm
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, 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
import configuration
import jwt

@api_view(['POST'])
def get_haloitsm_account_info(request, conn=None):
    '''
    Get the domain and api key of HaloITSM accounts associated to an organization or integration.
    :param request: Http request
    :param conn: db connection
    :return: Http response
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        optional_fields = [var_names.integration_key]
        try:
            conn = CONN_POOL.get_db_conn() if conn is None else conn
            request_validator.validate_fields(request, [], optional_fields)
            user_id, org_id, user_perm, org_perm = tokenizer.authorize_request(request)

            integ_key = request.data[var_names.integration_key] if var_names.integration_key in request.data else None
            if permissions.has_org_permission(org_perm, permissions.ORG_INTEGRATION_ITSM_PERMISSION):
                if permissions.is_user_admin(user_perm):
                    return Response(db_haloitsm.get_haloitsm_accounts(
                        conn, times.get_current_timestamp(), org_id, integ_key))
                else:
                    return Response(_lt.get_label(errors.err_user_rights, lang), status=403)
            else:
                return Response(_lt.get_label(errors.err_subscription_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 update_haloitsm_account_info(request, conn=None):
    '''
    Checks if a given HaloITSM 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.service_ref_id, var_names.integration_key,
                           var_names.vendor_endpoint_name, var_names.secret_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.has_org_permission(org_perm, permissions.ORG_INTEGRATION_ITSM_PERMISSION):
                if permissions.is_user_admin(user_perm):
                    current_time = times.get_current_timestamp()
                    success = db_haloitsm.update_haloitsm_account(
                        conn, current_time, org_id, request.data[var_names.service_ref_id],
                        request.data[var_names.integration_key], request.data[var_names.vendor_endpoint_name],
                        request.data[var_names.secret_token]
                    )
                    return Response(success)
                else:
                    return Response(_lt.get_label(errors.err_user_rights, lang), status=403)
            else:
                return Response(_lt.get_label(errors.err_subscription_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_haloitsm_webhook(request, integration_key, conn=None, cache=None):
    '''
    Processes the incoming webhook from haloITSM.
    :param request: Http request
    :param integration_key: integration key passed in the url
    :param conn: db connection
    :param cache: cache client
    :return: Http response
    '''
    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

            ticket_id = request.data[haloitsm.var_ticket][haloitsm.var_id] if haloitsm.var_ticket \
                                                                             in request.data else None
            ticket_title = request.data[haloitsm.var_ticket][haloitsm.var_summary] if haloitsm.var_ticket \
                                                                        in request.data else 'HaloITSM Ticket'
            ticket_details = request.data[haloitsm.var_ticket][haloitsm.var_details] if haloitsm.var_ticket in request.data \
                else str(request.data)
            ticket_url = request.data[var_names.vendor_url] if var_names.vendor_url in request.data else None
            ticket_priority = haloitsm.get_field_value(haloitsm.var_priority_id, request.data)
            ticket_status = haloitsm.get_field_value(haloitsm.var_status_id, request.data)

            tc_urgency = constants.critical_urgency
            if haloitsm.var_ticket in request.data:
                if ticket_priority in haloitsm.halo_priority_mapping:
                    tc_urgency = haloitsm.halo_priority_mapping[ticket_priority]

            tc_state = constants.open_state
            if haloitsm.var_ticket in request.data:
                if ticket_status in haloitsm.halo_status_mapping:
                    tc_state = haloitsm.halo_status_mapping[ticket_status]

            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.haloitsm)

            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]
                    src_alert_id = source_payload[haloitsm.var_ticket][haloitsm.var_id] if haloitsm.var_ticket in source_payload else None
                    src_priority = haloitsm.get_field_value(haloitsm.var_priority_id, source_payload)

                    if ticket_id is not None and src_alert_id == ticket_id:
                        match_count += 1
                        to_group = True

                        if ticket_priority is not None:
                            if ticket_priority != src_priority:
                                to_group = False
                                event = UrgencyAmendmentEvent(
                                    inst_id, current_time, constants.integrations_api, tc_urgency)
                                syncer_task_instances.amend_urgency(conn, cache, [event], org_id,
                                                                    is_sys_action=True)

                        if tc_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_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:
                            if to_group:
                                payload = TaskPayload(
                                    current_time, org_id, current_time.date(), ticket_title,
                                    configuration.standard_timezone, current_time.time(), text_msg=ticket_details,
                                    urgency_level=tc_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, vendor_incident_url=ticket_url
                                )
                                Router(conn, cache, payload).start()

            # Create a new task if this is a new alert.
            if match_count == 0:
                payload = TaskPayload(
                    current_time, org_id, current_time.date(), ticket_title, configuration.standard_timezone,
                    current_time.time(), text_msg=ticket_details, urgency_level=tc_urgency,
                    trigger_method=constants.integrations_api, trigger_info=request.data, integration_id=integ_id,
                    integration_key=integration_key, service_id=serv_id, vendor_incident_url=ticket_url
                )
                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)
