# By: Riasat Ullah
# This file contains views related to the TeamDynamix integration.

from data_syncers import syncer_task_instances
from dbqueries import db_integrations
from dbqueries.integrations import db_team_dynamix
from exceptions.user_exceptions import InvalidRequest, UnauthorizedRequest
from integrations import team_dynamix
from modules.router import Router
from objects.events import AcknowledgeEvent, CustomActionEvent, 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_team_dynamix_accounts(request, conn=None):
    '''
    Get the TeamDynamix accounts associated to an organization (not accounts in TeamDynamix).
    :param request: Http request
    :param conn: db connection
    :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
            request_validator.validate_fields(request, [])
            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):
                    return Response(db_team_dynamix.get_team_dynamix_accounts(conn, times.get_current_timestamp(),
                                                                              org_id))
                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 get_team_dynamix_account_details(request, conn=None):
    '''
    Get the domain and username of a TeamDynamix integration.
    :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.integration_key]
        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)
            integ_key = request.data[var_names.integration_key]

            if permissions.has_org_permission(org_perm, permissions.ORG_INTEGRATION_ITSM_PERMISSION):
                if permissions.is_user_admin(user_perm):
                    srv_det = db_team_dynamix.get_team_dynamix_account_details(
                        conn, times.get_current_timestamp(), org_id, integration_key=integ_key)
                    del srv_det[var_names.password]
                    return Response(srv_det)
                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 get_team_dynamix_meta_data(request, conn=None):
    '''
    Get the available department accounts, applications, ticket types, statuses and priorities
    for a TeamDynamix domain account.
    :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.vendor_endpoint_name, var_names.username, var_names.password,
                           var_names.integration_key, var_names.application_id]
        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)

            acc_domain = request.data[var_names.vendor_endpoint_name]\
                if var_names.vendor_endpoint_name in request.data else None
            acc_admin = request.data[var_names.username] if var_names.username in request.data else None
            acc_pwd = request.data[var_names.password] if var_names.password in request.data else None
            integ_key = request.data[var_names.integration_key] if var_names.integration_key in request.data else None
            app_id = request.data[var_names.application_id] if var_names.application_id in request.data else None

            if acc_domain is None and integ_key is None:
                raise InvalidRequest(errors.err_invalid_request)

            current_time = times.get_current_timestamp()
            if permissions.has_org_permission(org_perm, permissions.ORG_INTEGRATION_ITSM_PERMISSION):
                if permissions.is_user_admin(user_perm):
                    # Get the TeamDynamix admin details if it is not present.
                    if integ_key is not None or (acc_domain is not None and (acc_admin is None or acc_pwd is None)):
                        tm_dy_det = db_team_dynamix.get_team_dynamix_account_details(
                            conn, current_time, org_id, integration_key=integ_key, account_domain=acc_domain)

                        acc_domain = tm_dy_det[var_names.vendor_endpoint_name]
                        acc_admin = tm_dy_det[var_names.username]
                        acc_pwd = tm_dy_det[var_names.password]

                    token = team_dynamix.get_jwt_token(acc_domain, acc_admin, acc_pwd)

                    fmt_accounts = team_dynamix.get_accounts(token, acc_domain)
                    fmt_applications = team_dynamix.get_applications(token, acc_domain)
                    if app_id is None:
                        app_id = fmt_applications[0][var_names.item_id]

                    fmt_ticket_types = team_dynamix.get_ticket_types(token, acc_domain, app_id)
                    fmt_statuses = team_dynamix.get_statuses(token, acc_domain, app_id)
                    fmt_priorities = team_dynamix.get_priorities(token, acc_domain, app_id)

                    return Response([fmt_accounts, fmt_applications, fmt_ticket_types, fmt_statuses, fmt_priorities])
                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 the incoming webhook from TeamDynamix.
    :param request: Http request
    :param integration_key: integration key
    :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

            # alert identifier keys
            if request.data['metadata']['Type'] != 'Ticket':
                logging.error('Payload is not of type Ticket')
                logging.error(request.data)
                return Response(_lt.get_label(errors.err_invalid_request, lang), status=400)

            is_new_ticket = True if request.data['metadata']['ChangeType'] == 'Created' else False
            ticket_body = request.data['data']
            tkt_id = ticket_body['ID']
            tkt_type = ticket_body['TypeName']
            tkt_category = ticket_body['TypeCategoryName']
            account_name = ticket_body['AccountName']
            tkt_priority = ticket_body['PriorityID']
            tkt_status = ticket_body['StatusID']
            tkt_title = ticket_body['Title']
            tkt_description = ticket_body['Description']

            inc_description = '(Type: {0} | Category: {1} | Account: {2}) {3}'.format(
                tkt_type, tkt_category, account_name, tkt_description)

            # get the integration details
            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.team_dynamix)

            # internalize attributes sent in the webhook
            urgency = constants.critical_urgency
            if 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 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_status:
                            status = key
                            break

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

            # Create a new task if it is a new issue.
            if len(synced_insts) == 0:
                if is_new_ticket:
                    payload = TaskPayload(
                        current_time, org_id, current_time.date(), tkt_title, configuration.standard_timezone,
                        current_time.time(), text_msg=inc_description, urgency_level=urgency,
                        trigger_method=constants.integrations_api, trigger_info=request.data, integration_id=integ_id,
                        service_id=serv_id
                    )
                    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=str(tkt_id), vendor_url=None, 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:
                    inst = syncer_task_instances.get_single_instance(conn, cache, current_time, inst_id)

                    if urgency != inst.task.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)

                    if status != inst.status:
                        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,
                                skip_syncing=[intt.team_dynamix], org_perm=org_perm)
                        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,
                                skip_syncing=[intt.team_dynamix], org_perm=org_perm)
                        else:
                            msg = 'Already synced TeamDynamix ticket received with state - ' + str(tkt_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.error(request.data)
            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_team_dynamix_credentials(request, conn=None):
    '''
    Update a TeamDynamix account credentials. Ensure there is an existing record for a TeamDynamix account details.
    :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.vendor_endpoint_name, var_names.username, var_names.password]
        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):
                    acc_domain = request.data[var_names.vendor_endpoint_name]
                    admin_user = request.data[var_names.username]
                    admin_password = request.data[var_names.password]
                    admin_uid = team_dynamix.get_admin_uid(acc_domain, admin_user, admin_password)
                    if admin_uid is None:
                        return Response(_lt.get_label(errors.err_team_dynamix_invalid_admin_credentials, lang),
                                        status=403)
                    else:
                        return Response(db_team_dynamix.update_team_dynamix_credentials(
                            conn, times.get_current_timestamp(), org_id, acc_domain, admin_user,
                            admin_password, admin_uid
                        ))
                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)
