# By: Riasat Ullah
# This file contains all the views related to tasks/manual alerts.

from data_syncers import syncer_task_instances
from dbqueries import db_policies, db_services, db_tasks
from exceptions.user_exceptions import InvalidRequest, MaliciousSource, NoOneIsOnCall, ServiceUnavailable,\
    UnauthorizedRequest
from modules.router import Router
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, logging, permissions, times, tokenizer, var_names
from utils.db_connection import CACHE_CLIENT, CONN_POOL
from validations import request_validator
import configuration as configs
import jwt


@api_view(['POST'])
def tasks_owned(request, conn=None):
    '''
    Gets all the tasks owned/created by a user.
    :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
            user_id, org_id, user_perm, org_perm = tokenizer.authorize_request(request)
            if permissions.has_user_permission(user_perm, permissions.USER_INCIDENTS_CREATE_PERMISSION):
                tasks = db_tasks.get_user_tasks(conn, times.get_current_timestamp(), user_id)
                return Response(tasks)
            else:
                return Response(_lt.get_label(errors.err_user_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_task(request, conn=None):
    '''
    Gets a single task given it's reference ID.
    :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.task_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_user_permission(user_perm, permissions.USER_INCIDENTS_CREATE_PERMISSION):
                tasks = db_tasks.get_user_tasks(conn, times.get_current_timestamp(),
                                                task_ref_id=request.data[var_names.task_ref_id])
                if len(tasks) > 0:
                    return Response(tasks[0])
                else:
                    return Response(_lt.get_label(errors.err_unknown_resource, lang), status=400)
            else:
                return Response(_lt.get_label(errors.err_user_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 create(request, conn=None, cache=None):
    '''
    Creates a new task.
    :param request: Http Request
    :param conn: db connection
    :param cache: cache client
    :return: Http Response -> (str)
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.task_title, var_names.text_msg, var_names.service_ref_id,
                           var_names.assignee_policies, var_names.urgency_level]
        optional_fields = [var_names.start_date, var_names.task_time, var_names.repeat, var_names.timezone,
                           var_names.access_method]
        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, optional_fields)
            user_id, org_id, user_perm, org_perm = tokenizer.authorize_request(request)

            if permissions.has_user_permission(user_perm, permissions.USER_INCIDENTS_CREATE_PERMISSION):
                current_time = times.get_current_timestamp()

                title = request.data[var_names.task_title]
                text_msg = request.data[var_names.text_msg]
                service_ref_id = request.data[var_names.service_ref_id]
                assignee_ref_ids = request.data[var_names.assignee_policies]
                urgency_level = request.data[var_names.urgency_level]

                # optional fields
                start_date = request.data[var_names.start_date] if var_names.start_date in request.data else None
                task_time = request.data[var_names.task_time] if var_names.task_time in request.data else None
                repeat_on = request.data[var_names.repeat] if var_names.repeat in request.data else None
                timezone = request.data[var_names.timezone] if var_names.timezone in request.data\
                    else configs.standard_timezone
                access_method = request.data[var_names.access_method] if var_names.access_method in request.data\
                    else constants.app

                # convert reference IDs to internal IDs
                service_id, service_policy_id, assignee_ids = None, None, None
                if service_ref_id is not None:
                    service_id, service_policy_id = db_services.get_service_policy_id(
                        conn, current_time, org_id, service_ref_id
                    )
                if assignee_ref_ids is not None:
                    assignee_ids = db_policies.list_policy_ids_from_ref_ids(conn, current_time, org_id,
                                                                            assignee_ref_ids, unmasked=False)

                if start_date is None:
                    payload = TaskPayload(current_time, org_id, current_time.date(), title, configs.standard_timezone,
                                          current_time.time(), text_msg=text_msg, urgency_level=urgency_level,
                                          trigger_method=access_method, service_id=service_id,
                                          service_policy_id=service_policy_id, assignees=assignee_ids,
                                          created_by=user_id)
                    Router(conn, cache, payload).start()
                else:
                    syncer_task_instances.create_task(
                        conn, cache, current_time, org_id, start_date, title, timezone, task_time,
                        service_id=service_id, service_policy_id=service_policy_id, assignees=assignee_ids,
                        created_by=user_id, repeat_on=repeat_on, text_msg=text_msg, urgency_level=urgency_level,
                        trigger_method=access_method, pre_scheduled=True
                    )

                return Response(_lt.get_label(info.msg_success, lang))
            else:
                return Response(_lt.get_label(errors.err_user_rights, lang), status=403)
        except (InvalidRequest, ValueError) as e:
            logging.exception(str(e))
            return Response(_lt.get_label(str(e), lang), status=400)
        except MaliciousSource as e:
            logging.error(str(e))
            return Response(_lt.get_label(errors.err_authorization, lang), status=401)
        except ServiceUnavailable as e:
            logging.warning(str(e))
            return Response(_lt.get_label(errors.err_service_unavailable, lang), status=401)
        except (UnauthorizedRequest, jwt.ExpiredSignatureError, jwt.InvalidSignatureError) as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_authorization, lang), status=401)
        except NoOneIsOnCall as e:
            logging.exception(str(e))
            return Response(_lt.get_label(str(e), lang), status=412)
        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(request, conn=None):
    '''
    Edits an existing task.
    :param request: Http Request
    :param conn: db connection
    :return: Http Response -> (str)
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.task_ref_id, var_names.task_title, var_names.text_msg,
                           var_names.service_ref_id, var_names.assignee_policies, var_names.urgency_level]
        optional_fields = [var_names.start_date, var_names.task_time, var_names.repeat, var_names.timezone]
        try:
            conn = CONN_POOL.get_db_conn() if conn is None else conn
            request_validator.validate_fields(request, expected_fields, optional_fields)
            user_id, org_id, user_perm, org_perm = tokenizer.authorize_request(request)

            if permissions.has_user_permission(user_perm, permissions.USER_INCIDENTS_CREATE_PERMISSION):
                current_time = times.get_current_timestamp()
                task_ref_id = request.data[var_names.task_ref_id]

                if db_tasks.get_task_owner(conn, current_time, task_ref_id) == user_id:
                    title = request.data[var_names.task_title]
                    text_msg = request.data[var_names.text_msg]
                    service_ref_id = request.data[var_names.service_ref_id]
                    assignee_ref_ids = request.data[var_names.assignee_policies]
                    urgency_level = request.data[var_names.urgency_level]

                    # optional fields
                    start_date = request.data[var_names.start_date] if var_names.start_date in request.data else None
                    task_time = request.data[var_names.task_time] if var_names.task_time in request.data else None
                    repeat_on = request.data[var_names.repeat] if var_names.repeat in request.data else None
                    timezone = request.data[var_names.timezone] if var_names.timezone in request.data\
                        else configs.standard_timezone

                    service_id, service_policy_id, assignee_ids = None, None, None
                    if service_ref_id is not None:
                        service_id, service_policy_id = db_services.get_service_policy_id(
                            conn, current_time, org_id, service_ref_id
                        )
                    if assignee_ref_ids is not None:
                        assignee_ids = db_policies.list_policy_ids_from_ref_ids(conn, current_time, org_id,
                                                                                assignee_ref_ids, unmasked=False)

                    if start_date is None:
                        start_date = current_time.date()
                        task_time = current_time.time()

                    # If a task is edited to trigger immediately; then just edit the entry in the database
                    # and let the task monitor take care of it.

                    db_tasks.edit_task(conn, user_id, current_time, task_ref_id, start_date,
                                       title, timezone, task_time, service_id, assignee_ids, repeat_on,
                                       text_msg, urgency_level)

                    return Response(_lt.get_label(info.msg_success, lang))
                else:
                    return Response(_lt.get_label(errors.err_unknown_resource, lang), status=403)
            else:
                return Response(_lt.get_label(errors.err_user_rights, lang), status=403)
        except (InvalidRequest, 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(request, conn=None):
    '''
    Deletes a task.
    :param request: Http Request
    :param conn: db connection
    :return: Http Response -> str
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.task_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_user_permission(user_perm, permissions.USER_INCIDENTS_CREATE_PERMISSION):
                current_time = times.get_current_timestamp()
                task_ref_id = request.data[var_names.task_ref_id]

                if db_tasks.get_task_owner(conn, current_time, task_ref_id) == user_id:
                    db_tasks.delete_task(conn, current_time, task_ref_id)
                    return Response(_lt.get_label(info.msg_success, lang))
                else:
                    return Response(_lt.get_label(errors.err_unknown_resource, lang), status=403)
            else:
                return Response(_lt.get_label(errors.err_user_rights, lang), status=403)
        except (InvalidRequest, 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)
