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

from data_syncers import syncer_task_instances
from dbqueries import db_integrations
from dbqueries.integrations import db_monday
from exceptions.user_exceptions import InvalidRequest, UnauthorizedRequest
from integrations import monday
from modules.router import Router
from objects.events import CustomActionEvent
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 update_monday_account_tokens(request, conn=None):
    '''
    Checks if a given Monday.com 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.access_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_SECONDARY_PERMISSION):
                if permissions.is_user_admin(user_perm):
                    external_info = {var_names.access_token: request.data[var_names.access_token]}
                    return Response(db_integrations.check_and_update_organization_integration_details(
                        conn, times.get_current_timestamp(), org_id, intt.monday,
                        request.data[var_names.external_id], external_info
                    ))
                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_monday_boards(request, conn=None):
    '''
    Get the the available Monday.com boards.
    :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.external_id, var_names.access_token, 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)

            account_id = request.data[var_names.external_id] if var_names.external_id in request.data else None
            access_token = request.data[var_names.access_token] if var_names.access_token in request.data else None
            integ_key = request.data[var_names.integration_key] if var_names.integration_key in request.data else None

            if account_id 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_SECONDARY_PERMISSION):
                if permissions.is_user_admin(user_perm):

                    if integ_key is not None or (account_id is not None and access_token is None):
                        mon_req_det = db_monday.get_monday_request_acceptance_details(
                            conn, current_time, org_id, integration_key=integ_key, account_id=account_id)
                        access_token = mon_req_det[var_names.access_token]

                    return Response(monday.get_monday_boards(access_token))
                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 monday.com.
    :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

            # This process verifies a webhook.
            if monday.var_challenge in request.data:
                return Response({monday.var_challenge: request.data[monday.var_challenge]})

            if monday.var_event not in request.data:
                logging.error('TaskCall is unable to recognize payload from monday.com. Aborting process.')
                logging.error(str(request.data))
                return Response(errors.err_invalid_request)

            monday_event = request.data[monday.var_event]

            # make sure the pulse ID is converted to a str from an int;
            # otherwise database query will fail on vendor ID matching
            pulse_id = str(monday_event[monday.var_pulse_id])
            pulse_name = monday_event[monday.var_pulse_name]
            monday_event_type = monday_event['type']

            if monday_event_type == monday.monday_event_create_pulse:
                inc_title = 'monday.com (new item): ' + pulse_name
                inc_title += ' (' + monday_event[monday.var_groupName] + ')' if monday.var_groupName in monday_event\
                    else ''
            elif monday_event_type == monday.monday_event_update_column_value:
                column_name = monday_event[monday.var_columnTitle]
                inc_title = 'monday.com (' + column_name + ' updated): ' + pulse_name
            elif monday_event_type == monday.monday_event_when_date_arrived:
                inc_title = 'monday.com (date arrived): ' + pulse_name
            else:
                inc_title = 'monday.com: ' + pulse_name

            urgency = constants.medium_urgency
            current_time = times.get_current_timestamp()
            org_id, serv_id, integ_id, integ_type_id, integ_details = db_monday.get_monday_webhook_details(
                conn, current_time, integration_key)
            synced_insts = db_integrations.get_vendor_synced_open_instance_ids(
                conn, current_time, org_id, integ_id, integ_type_id, pulse_id)

            # Create a new task if it is a new item.
            if len(synced_insts) == 0:
                dedup_key = intt.monday + '_' + str(pulse_id)
                payload = TaskPayload(
                    current_time, org_id, current_time.date(), inc_title, configuration.standard_timezone,
                    current_time.time(), text_msg=str(request.data), urgency_level=urgency,
                    trigger_method=constants.integrations_api, trigger_info=str(request.data),
                    integration_id=integ_id, service_id=serv_id, dedup_key=dedup_key
                )
                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=pulse_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)

            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)
