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

from dbqueries import db_change_events, db_integrations
from exceptions.user_exceptions import InvalidRequest
from integrations import github
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 Github.
    :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.github)
            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)

            # pull or push request - check the type of incoming request and extract fields accordingly
            # accept pull request only if it is being merged
            if github.var_pull_request in request.data and github.var_action in request.data and\
                    request.data[github.var_action] == github.state_closed:
                pull_request = request.data[github.var_pull_request]

                git_title = pull_request[github.var_title]\
                    if github.var_title in pull_request else github.default_change_event_title
                git_description = pull_request[github.var_body] if github.var_body in pull_request else None
                git_url = pull_request[github.var_html_url] if github.var_html_url in pull_request else None
                git_id = pull_request[github.var_number] if github.var_number in pull_request else None
                git_branch = pull_request[github.var_base][github.var_ref]\
                    if github.var_base in pull_request and github.var_ref in pull_request[github.var_base] else None
                git_event_status = pull_request[github.var_state] if github.var_state in pull_request else None
                git_event_by = pull_request[github.var_user][github.var_login]\
                    if github.var_user in pull_request and github.var_login in pull_request[github.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=github.var_pull_request,
                        vendor_event_status=git_event_status, vendor_event_by=git_event_by, additional_info=pull_request
                    )
                else:
                    logging.info('Github Change Event - Ignored because request received from non-trackable branch - '
                                 + str(git_branch))

            elif github.var_head_commit in request.data and github.var_commits in request.data and\
                    github.var_pusher in request.data:
                git_title = request.data[github.var_head_commit][github.var_message]\
                    if github.var_head_commit in request.data and\
                    github.var_message in request.data[github.var_head_commit] else github.default_change_event_title
                git_description = None
                git_url = request.data[github.var_head_commit][github.var_url]\
                    if github.var_head_commit in request.data and\
                    github.var_url in request.data[github.var_head_commit] else None
                git_branch = request.data[github.var_ref].split('/')[-1]\
                    if github.var_ref in request.data and request.data[github.var_ref] is not None else None
                git_event_by = request.data[github.var_pusher][github.var_name]\
                    if github.var_name in request.data[github.var_pusher] 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=github.var_push, vendor_event_by=git_event_by,
                        additional_info={github.var_commits: request.data[github.var_commits]}
                    )
                else:
                    logging.info('Github Change Event - Ignored because request received from non-trackable branch - '
                                 + str(git_branch))
            else:
                logging.info('Github 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)
