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

from data_syncers import syncer_task_instances
from dbqueries import db_events, db_integrations
from dbqueries.integrations import db_freshservice
from exceptions.user_exceptions import InvalidRequest, UnauthorizedRequest
from integrations import freshservice
from modules.router import Router
from objects.events import AcknowledgeEvent, CustomActionEvent, NotateEvent, 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_freshservice_account_info(request, conn=None):
    '''
    Get the domain and api key of Freshservice 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_freshservice.get_freshservice_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_freshservice_account_info(request, conn=None):
    '''
    Checks if a given Freshservice 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_freshservice.update_freshservice_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_incoming_webhook(request, integration_key, conn=None, cache=None):
    '''
    Processes incoming webhooks from Freshservice.
    :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.data)
        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

            data_body = request.data[freshservice.var_freshservice_webhook]

            tkt_id = str(data_body[freshservice.var_id_numeric])
            tkt_disp_id = data_body[freshservice.var_ticket_id] if freshservice.var_ticket_id in data_body else tkt_id
            tkt_title = data_body[freshservice.var_ticket_subject]\
                if freshservice.var_ticket_subject in data_body else freshservice.default_ticket_subject
            tkt_desc = data_body[freshservice.var_ticket_description]\
                if freshservice.var_ticket_description in data_body else None
            tkt_state = freshservice.frs_label_to_frs_status_map[data_body[freshservice.var_ticket_status]]\
                if freshservice.var_ticket_status in data_body else None
            tkt_priority = data_body[freshservice.var_ticket_priority]\
                if freshservice.var_ticket_priority in data_body else None
            tkt_url = data_body[freshservice.var_ticket_url]\
                if freshservice.var_ticket_url in data_body else None
            tkt_tags = [x.lstrip().rstrip() for x in data_body[freshservice.var_ticket_tags].split(',')]\
                if freshservice.var_ticket_tags in data_body and data_body[freshservice.var_ticket_tags] != '' else None
            tkt_event_type = freshservice.get_event_type(data_body[freshservice.var_triggered_event])\
                if freshservice.var_triggered_event in data_body else None

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

            synced_insts = db_integrations.get_vendor_synced_open_instance_ids(
                conn, current_time, org_id, integ_id, integ_type_id, tkt_id)

            # internalize attributes sent in the webhook
            urgency = constants.critical_urgency
            if tkt_priority is not None and var_names.urgency_level in integ_details:
                urg_map = integ_details[var_names.urgency_level]
                if urg_map is not None:
                    for key in urg_map:
                        if urg_map[key] == tkt_priority:
                            urgency = int(key)
                            break

            status = constants.open_state
            if tkt_state is not None and var_names.status in integ_details:
                stat_map = integ_details[var_names.status]
                if stat_map is not None:
                    for key in stat_map:
                        if stat_map[key] == tkt_state:
                            status = key
                            break

            # Create a new instance if it is a new issue.
            if len(synced_insts) == 0 and tkt_event_type is not None and\
                    tkt_event_type == freshservice.frs_new_ticket_event:
                payload = TaskPayload(
                    current_time, org_id, current_time.date(), tkt_title, configuration.standard_timezone,
                    current_time.time(), text_msg=tkt_desc, urgency_level=urgency,
                    trigger_method=constants.integrations_api, trigger_info=request.data, integration_id=integ_id,
                    service_id=serv_id, tags=tkt_tags
                )
                inst_id = Router(conn, cache, payload).run()

                event = CustomActionEvent(
                    inst_id, current_time, constants.internal, integration_id=integ_id,
                    integration_type_id=integ_type_id, vendor_id=tkt_id, vendor_url=tkt_url,
                    additional_info={var_names.display_name: tkt_disp_id}, is_synced=True,
                    configuration_name=integ_details[var_names.configuration_name]
                )
                syncer_task_instances.execute_custom_action(conn, cache, event, org_id=org_id, is_sys_action=True)
            else:
                for inst_id, task_id, in_sync in synced_insts:
                    if tkt_event_type is not None:
                        if tkt_event_type == freshservice.frs_priority_update_event:
                            event = UrgencyAmendmentEvent(inst_id, current_time, constants.integrations_api, urgency)
                            syncer_task_instances.amend_urgency(conn, cache, [event], org_id, is_sys_action=True)

                        elif tkt_event_type in [freshservice.frs_private_add_note_event,
                                                freshservice.frs_public_add_note_event]:
                            if tkt_event_type == freshservice.frs_private_add_note_event:
                                note_msg = data_body[freshservice.var_ticket_latest_private_comment]
                            else:
                                note_msg = data_body[freshservice.var_ticket_latest_public_comment]
                            event = NotateEvent(inst_id, current_time, constants.integrations_api, None, note_msg)
                            db_events.book_notate_event(conn, event, org_id, False, False, True)

                    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:
                        msg = 'Already synced Freshservice ticket webhook could not be processed - ' + str(request.data)
                        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)
