# By: Riasat Ullah
# This view handles incoming payload for Gitlab Changes events.

from dbqueries import db_change_events, db_integrations
from exceptions.user_exceptions import InvalidRequest
from integrations import gitlab
from rest_framework.decorators import api_view
from rest_framework.response import Response
from translators import label_translator as _lt
from utils import errors, info, integration_type_names as intt, logging, permissions, times, var_names
from utils.db_connection import CONN_POOL
from validations import request_validator


@api_view(['POST'])
def create_change_event(request, integration_key, conn=None):
    '''
    Processes the incoming webhook from Gitlab.
    :param request: Http request
    :param integration_key: integration key passed in the url
    :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

            current_time = times.get_current_timestamp()
            retrieved_integration_information = db_integrations.get_integration_details(
                conn, current_time, integration_key, intt.gitlab)
            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

            if not permissions.has_org_permission(org_perm, permissions.ORG_RECENT_CHANGES_PERMISSION):
                return Response(_lt.get_label(errors.err_subscription_rights, lang), status=403)

            request_type = request.data[gitlab.var_object_kind] if gitlab.var_object_kind in request.data else None

            # pull or push request - check the type of incoming request and extract fields accordingly
            # accept pull request only if it is being merged
            if request_type is not None and request_type == gitlab.req_merge_request:
                obj_attr = request.data[gitlab.var_object_attributes]
                git_title = obj_attr[gitlab.var_title]\
                    if gitlab.var_title in obj_attr else gitlab.default_change_event_title
                git_description = obj_attr[gitlab.var_description] if gitlab.var_description in obj_attr else None
                git_url = obj_attr[gitlab.var_url] if gitlab.var_url in obj_attr else None
                git_id = obj_attr[gitlab.var_iid] if gitlab.var_iid in obj_attr else None
                git_branch = obj_attr[gitlab.var_target_branch] if gitlab.var_target_branch in obj_attr else None
                git_event_status = obj_attr[gitlab.var_state] if gitlab.var_state in obj_attr else None
                git_event_by = request.data[gitlab.var_user][gitlab.var_name]\
                    if gitlab.var_user in request.data and gitlab.var_name in request.data[gitlab.var_user] else None

                if git_branch is not None and git_branch in integ_details[var_names.branch]:
                    db_change_events.store_change_event(
                        conn, current_time, org_id, serv_id, git_title, integration_id=integ_id,
                        integration_type_id=integ_type_id, description=git_description, vendor_url=git_url,
                        vendor_id=git_id, vendor_source=git_branch, vendor_event_type=gitlab.req_merge_request,
                        vendor_event_status=git_event_status, vendor_event_by=git_event_by, additional_info=obj_attr
                    )
                else:
                    logging.info('Gitlab Change Event - Ignored because request received from non-trackable branch - '
                                 + str(git_branch))

            elif request_type is not None and request_type == gitlab.req_push and\
                gitlab.var_commits in request.data and request.data[gitlab.var_commits] is not None and\
                    len(request.data[gitlab.var_commits]) > 0:
                first_commit = request.data[gitlab.var_commits][0]
                git_title = first_commit[gitlab.var_title]\
                    if gitlab.var_title in first_commit else gitlab.default_change_event_title
                git_description = None
                git_url = first_commit[gitlab.var_url] if gitlab.var_url in first_commit else None
                git_branch = request.data[gitlab.var_ref].split('/')[-1]\
                    if gitlab.var_ref in request.data and request.data[gitlab.var_ref] is not None else None
                git_event_by = request.data[gitlab.var_user_name]\
                    if gitlab.var_user_name in request.data[gitlab.var_user_name] else None

                if git_branch is not None and git_branch in integ_details[var_names.branch]:
                    db_change_events.store_change_event(
                        conn, current_time, org_id, serv_id, git_title, integration_id=integ_id,
                        integration_type_id=integ_type_id, description=git_description, vendor_url=git_url,
                        vendor_source=git_branch, vendor_event_type=gitlab.req_push, vendor_event_by=git_event_by,
                        additional_info={gitlab.var_commits: request.data[gitlab.var_commits]}
                    )
                else:
                    logging.info('Gitlab Change Event - Ignored because request received from non-trackable branch - '
                                 + str(git_branch))
            else:
                logging.info('Gitlab Change Event: Could not identify event type. Aborting processing.')
                logging.info(request.data)
            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)
