# By: Riasat Ullah
# This file contains all report views.

from dbqueries import db_postmortem_reports, db_users
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 CONN_POOL
from validations import request_validator
import jwt


@api_view(['POST'])
def list_postmortem_reports(request, conn=None):
    '''
    Gets the list of postmortem reports a user is allowed to view with some basic information.
    Full details of the reports are not provided from this view.
    :param request: Http request
    :param conn: db connection
    :return: Http response -> (list) of dicts
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        optional_fields = [var_names.row_limit, var_names.row_offset]
        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)

            if permissions.has_org_permission(org_perm, permissions.ORG_POSTMORTEM_PERMISSION):
                if permissions.has_user_permission(user_perm, permissions.USER_POSTMORTEMS_VIEW_PERMISSION):
                    reports = db_postmortem_reports.get_postmortem_reports_list(
                        conn, times.get_current_timestamp(), with_user_id=user_id,
                        row_limit=request.data[var_names.row_limit] if var_names.row_limit in request.data else None,
                        row_offset=request.data[var_names.row_offset] if var_names.row_offset in request.data else None
                    )
                    return Response(reports)
                else:
                    return Response(_lt.get_label(errors.err_user_rights, lang), status=403)
            return Response(_lt.get_label(errors.err_subscription_rights, lang), status=401)
        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_postmortem_report_details(request, conn=None):
    '''
    Gets all the details of a postmortem report including that of the incident it is for.
    :param request: Http request
    :param conn: db connection
    :return: Http response -> (dict) -> { report: {...}, incident: {...} }
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.report_number]
        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_POSTMORTEM_PERMISSION):
                if permissions.has_user_permission(user_perm, permissions.USER_POSTMORTEMS_VIEW_PERMISSION):
                    full_report = db_postmortem_reports.get_postmortem_report_details(
                        conn, times.get_current_timestamp(), org_id, request.data[var_names.report_number]
                    )
                    return Response(full_report)
                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=401)
        except (InvalidRequest, LookupError) 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_postmortem_report(request, conn=None):
    '''
    Create a new postmortem report.
    :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.instance_id, var_names.status, var_names.report_name, var_names.complete_by,
                           var_names.overview, var_names.description, var_names.impact, var_names.resolution,
                           var_names.positives, var_names.negatives, var_names.preventions,
                           var_names.reviewers, var_names.review_sessions]
        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_POSTMORTEM_PERMISSION):
                if permissions.has_user_permission(user_perm, permissions.USER_POSTMORTEMS_EDIT_PERMISSION):
                    report_number = db_postmortem_reports.create_postmortem_report(
                        conn, times.get_current_timestamp(), org_id, user_id,
                        request.data[var_names.instance_id], request.data[var_names.status],
                        request.data[var_names.report_name], request.data[var_names.complete_by],
                        request.data[var_names.overview], request.data[var_names.description],
                        request.data[var_names.impact], request.data[var_names.resolution],
                        request.data[var_names.positives], request.data[var_names.negatives],
                        request.data[var_names.preventions], request.data[var_names.reviewers],
                        request.data[var_names.review_sessions]
                    )
                    return Response(report_number)
                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=401)
        except (InvalidRequest, LookupError) 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_postmortem_report(request, conn=None):
    '''
    Edit a new postmortem report.
    :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.report_number, var_names.status, var_names.report_name, var_names.complete_by,
                           var_names.overview, var_names.description, var_names.impact, var_names.resolution,
                           var_names.positives, var_names.negatives, var_names.preventions,
                           var_names.reviewers, var_names.review_sessions]
        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)

            report_num = request.data[var_names.report_number]
            current_time = times.get_current_timestamp()

            if permissions.has_org_permission(org_perm, permissions.ORG_POSTMORTEM_PERMISSION):
                if permissions.has_user_permission(user_perm, permissions.USER_POSTMORTEMS_EDIT_PERMISSION):

                    report_org_id = db_postmortem_reports.get_organization_id_of_report(conn, current_time, report_num)
                    if org_id == report_org_id:
                        db_postmortem_reports.edit_postmortem_report(
                            conn, times.get_current_timestamp(), org_id, user_id,
                            report_num, request.data[var_names.status],
                            request.data[var_names.report_name], request.data[var_names.complete_by],
                            request.data[var_names.overview], request.data[var_names.description],
                            request.data[var_names.impact], request.data[var_names.resolution],
                            request.data[var_names.positives], request.data[var_names.negatives],
                            request.data[var_names.preventions], request.data[var_names.reviewers],
                            request.data[var_names.review_sessions]
                        )
                        return Response(_lt.get_label(info.msg_postmortem_edited, lang))
                    else:
                        return Response(_lt.get_label(errors.err_unknown_resource, lang), status=401)
                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=401)
        except (InvalidRequest, LookupError) 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 save_postmortem_comment(request, conn=None):
    '''
    Save a comment made on a postmortem report.
    :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.report_number, var_names.comments]
        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_POSTMORTEM_PERMISSION):
                if permissions.has_user_permission(user_perm, permissions.USER_POSTMORTEMS_EDIT_PERMISSION):
                    current_time = times.get_current_timestamp()
                    display_name = db_users.get_user_display_name(conn, current_time, org_id, user_id=user_id)
                    db_postmortem_reports.save_comment(
                        conn, current_time, user_id,
                        request.data[var_names.report_number], request.data[var_names.comments]
                    )
                    return Response([current_time, display_name])
                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=401)
        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 delete_postmortem_report(request, conn=None):
    '''
    Delete a postmortem report.
    :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.report_number]
        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_POSTMORTEM_PERMISSION):
                if permissions.has_user_permission(user_perm, permissions.USER_POSTMORTEMS_EDIT_PERMISSION):
                    db_postmortem_reports.delete_postmortem_report(
                        conn, times.get_current_timestamp(), org_id, user_id, request.data[var_names.report_number]
                    )
                    return Response(_lt.get_label(info.msg_postmortem_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=401)
        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)
