# By: Riasat Ullah
# This file contains incident workflow views.

from data_syncers import syncer_workflows
from dbqueries import db_workflows
from exceptions.user_exceptions import InvalidRequest, UnauthorizedRequest
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, logging, permissions, times, tokenizer, var_names
from utils.db_connection import CACHE_CLIENT, CONN_POOL
from validations import request_validator
import jwt


@api_view(['POST'])
def create_workflow(request, conn=None, cache=None):
    '''
    Creates a new incident workflow.
    :param request: Http request
    :param conn: db connection
    :param cache: cache client
    :return: JSON response
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.workflow_name, var_names.description, var_names.is_manual,
                           var_names.trigger_method, var_names.workflow, var_names.run_permissions,
                           var_names.edit_permissions]
        try:
            conn = CONN_POOL.get_db_conn() if conn is None else conn
            cache = CACHE_CLIENT if cache is None else cache
            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_WORKFLOWS_PERMISSION):
                if permissions.is_user_admin(user_perm):
                    current_time = times.get_current_timestamp()

                    db_workflows.create_workflow(
                        conn, current_time, org_id, request.data[var_names.workflow_name],
                        request.data[var_names.description], request.data[var_names.is_manual],
                        request.data[var_names.trigger_method], request.data[var_names.workflow],
                        request.data[var_names.run_permissions], request.data[var_names.edit_permissions]
                    )

                    syncer_workflows.update_organization_workflows_in_cache(conn, cache, current_time, org_id)

                    return Response(_lt.get_label(info.msg_workflow_created, lang))
                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, KeyError, ValueError) 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 edit_workflow(request, conn=None, cache=None):
    '''
    Edits an existing incident workflow.
    :param request: Http request
    :param conn: db connection
    :param cache: cache client
    :return: JSON response
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.workflow_ref_id, var_names.workflow_name, var_names.description,
                           var_names.is_manual, var_names.trigger_method, var_names.workflow, var_names.run_permissions,
                           var_names.edit_permissions]
        try:
            conn = CONN_POOL.get_db_conn() if conn is None else conn
            cache = CACHE_CLIENT if cache is None else cache
            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_WORKFLOWS_PERMISSION):
                current_time = times.get_current_timestamp()
                flow_ref = request.data[var_names.workflow_ref_id]
                flow_id = db_workflows.get_workflow_id_from_ref_id(conn, current_time, org_id, flow_ref)

                if db_workflows.has_edit_permission(conn, current_time, org_id, flow_id, user_id,
                                                    permissions.is_user_admin(user_perm)):
                    db_workflows.edit_workflow(conn, current_time, org_id,
                                               request.data[var_names.workflow_ref_id],
                                               request.data[var_names.workflow_name],
                                               request.data[var_names.description],
                                               request.data[var_names.is_manual],
                                               request.data[var_names.trigger_method],
                                               request.data[var_names.workflow],
                                               request.data[var_names.run_permissions],
                                               request.data[var_names.edit_permissions])

                    syncer_workflows.update_organization_workflows_in_cache(conn, cache, current_time, org_id)

                    return Response(_lt.get_label(info.msg_workflow_edited, lang))
                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, LookupError, ValueError) 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 delete_workflow(request, conn=None, cache=None):
    '''
    Delete an incident workflow.
    :param request: Http request
    :param conn: db connection
    :param cache: cache client
    :return: JSON response
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.workflow_ref_id]
        try:
            conn = CONN_POOL.get_db_conn() if conn is None else conn
            cache = CACHE_CLIENT if cache is None else cache
            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_WORKFLOWS_PERMISSION):
                current_time = times.get_current_timestamp()
                flow_ref = request.data[var_names.workflow_ref_id]
                flow_id = db_workflows.get_workflow_id_from_ref_id(conn, current_time, org_id, flow_ref)

                if db_workflows.has_edit_permission(conn, current_time, org_id, flow_id, user_id,
                                                    permissions.is_user_admin(user_perm)):
                    db_workflows.delete_workflow(conn, current_time, org_id, flow_ref)

                    syncer_workflows.update_organization_workflows_in_cache(conn, cache, current_time, org_id)

                    return Response(_lt.get_label(info.msg_workflow_deleted, lang))
                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, LookupError, ValueError) 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 enable_workflow(request, conn=None, cache=None):
    '''
    Enable or disable workflow.
    :param request: Http request
    :param conn: db connection
    :param cache: cache client
    :return: JSON response
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.workflow_ref_id, var_names.is_enabled]
        try:
            conn = CONN_POOL.get_db_conn() if conn is None else conn
            cache = CACHE_CLIENT if cache is None else cache
            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_WORKFLOWS_PERMISSION):
                current_time = times.get_current_timestamp()
                flow_ref = request.data[var_names.workflow_ref_id]
                flow_id = db_workflows.get_workflow_id_from_ref_id(conn, current_time, org_id, flow_ref)

                if db_workflows.has_edit_permission(conn, current_time, org_id, flow_id, user_id,
                                                    permissions.is_user_admin(user_perm)):
                    db_workflows.enable_workflow(conn, current_time, org_id, flow_ref,
                                                 request.data[var_names.is_enabled])

                    syncer_workflows.update_organization_workflows_in_cache(conn, cache, current_time, org_id)

                    return Response(_lt.get_label(info.msg_workflow_enabled, lang))
                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, LookupError, ValueError) as e:
            logging.exception(str(e))
            return Response(_lt.get_label(str(e), lang), status=400)
        except PermissionError as e:
            logging.exception(str(e))
            return Response(_lt.get_label(str(e), lang), status=403)
        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_workflow_list(request, conn=None):
    '''
    Get the list of incident workflows.
    :param request: Http request
    :param conn: db connection
    :return: JSON 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
            user_id, org_id, user_perm, org_perm = tokenizer.authorize_request(request)

            if permissions.has_org_permission(org_perm, permissions.ORG_WORKFLOWS_PERMISSION):
                if permissions.has_user_permission(user_perm, permissions.USER_COMPONENTS_VIEW_PERMISSION):
                    current_time = times.get_current_timestamp()
                    data = db_workflows.get_workflows_list(conn, current_time, org_id)
                    return Response(data)
                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, LookupError, ValueError) 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_workflow_details(request, conn=None):
    '''
    Get the details of an incident workflow.
    :param request: Http request
    :param conn: db connection
    :return: JSON response
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.workflow_ref_id]
        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_WORKFLOWS_PERMISSION):
                if permissions.has_user_permission(user_perm, permissions.USER_COMPONENTS_VIEW_PERMISSION):
                    current_time = times.get_current_timestamp()
                    data = db_workflows.get_workflows(conn, current_time, org_id,
                                                      workflow_ref_id=request.data[var_names.workflow_ref_id])
                    if len(data) == 0:
                        raise LookupError(errors.err_unknown_resource)

                    flow_dict = data[0]
                    last_run = db_workflows.get_workflow_last_run_time(
                        conn, current_time, org_id, flow_dict[var_names.workflow_id])
                    if last_run is not None:
                        flow_dict[var_names.last_run] = last_run

                    # Don't send the internal ID
                    del flow_dict[var_names.workflow_id]

                    return Response(flow_dict)
                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, LookupError, ValueError) 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)
