# By: Riasat Ullah
# This file contains analytics api views (it is all part of the incidents-api).

from analytics.business_impact_analytics import BusinessImpactAnalytics
from analytics.incident_analytics import IncidentAnalytics
from analytics.service_analytics import ServiceAnalytics
from analytics.team_analytics import TeamAnalytics
from analytics.user_analytics import UserAnalytics
from data_syncers import syncer_api_keys, syncer_organizations
from dbqueries import db_users
from exceptions.user_exceptions import InvalidRequest, UnauthorizedRequest
from rest_framework.decorators import api_view, throttle_classes
from rest_framework.response import Response
from rest_framework.throttling import UserRateThrottle
from translators import label_translator as _lt
from utils import errors, key_manager, 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'])
@throttle_classes([UserRateThrottle])
def incident_analytics(request, conn=None, cache=None):
    '''
    Gets the data for incident analytics. Although this type of analytics is available for all subscriptions,
    filtering by escalation policies, services and teams are only available for plans with "secondary" analytics
    permission.
    :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.start_date, var_names.end_date, var_names.timezone]
        optional_fields = [var_names.urgency_level, var_names.policies, var_names.services, var_names.teams,
                           var_names.tags]
        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)

            current_time = times.get_current_timestamp()
            api_key = tokenizer.extract_api_key(request)
            details = syncer_api_keys.get_api_key_details(conn, cache, current_time, api_key)
            if details is None:
                raise UnauthorizedRequest(errors.err_api_key_request_denied)
            key_id, org_id, perm, for_user, ip_restrictions = details
            request_validator.validate_api_ip_allowed(request, ip_restrictions)

            start_date = times.get_date_from_string(request.data[var_names.start_date])
            end_date = times.get_date_from_string(request.data[var_names.end_date])
            req_tz = request.data[var_names.timezone]
            urg_lvls = request.data[var_names.urgency_level] if var_names.urgency_level in request.data else None
            pol_refs = [key_manager.unmask_reference_key(x) for x in request.data[var_names.policies]]\
                if var_names.policies in request.data else None
            serv_refs = [key_manager.unmask_reference_key(x) for x in request.data[var_names.services]]\
                if var_names.services in request.data else None
            team_refs = [key_manager.unmask_reference_key(x) for x in request.data[var_names.teams]]\
                if var_names.teams in request.data else None
            tags = request.data[var_names.tags] if var_names.tags in request.data else None

            # make sure the rights permissions are present
            org_perm = syncer_organizations.get_single_organization_permission(conn, cache, current_time, org_id)
            if not permissions.has_org_permission(org_perm, permissions.ORG_ANALYTICS_BASIC_PERMISSION):
                return Response(_lt.get_label(errors.err_subscription_rights, lang), status=403)

            if ((pol_refs is not None or serv_refs is not None or team_refs is not None)
                and not permissions.has_org_permission(org_perm, permissions.ORG_ANALYTICS_SECONDARY_PERMISSION))\
                    or (tags is not None
                        and not permissions.has_org_permission(org_perm, permissions.ORG_CONTEXTUAL_SEARCH_PERMISSION)):
                return Response(_lt.get_label(errors.err_subscription_rights, lang), status=403)

            if not permissions.can_user_view_analytics(perm):
                return Response(_lt.get_label(errors.err_api_key_permission, lang), status=403)

            analytics_maker = IncidentAnalytics(conn, current_time, org_id, start_date, end_date, req_tz,
                                                urgency_levels=urg_lvls, policy_refs=pol_refs, service_refs=serv_refs,
                                                team_refs=team_refs, tags=tags)
            return Response(analytics_maker.get_metrics())
        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 PermissionError as e:
            logging.exception(str(e))
            return Response(_lt.get_label(str(e), lang), status=403)
        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'])
@throttle_classes([UserRateThrottle])
def business_impact_analytics(request, conn=None, cache=None):
    '''
    Get business impact analytics. It is only available for plans with advanced analytics permission (highest tier).
    :param request: Http request
    :param conn: database connection
    :param cache: cache client
    :return: JSON response
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.start_date, var_names.end_date, var_names.timezone]
        optional_fields = [var_names.teams, var_names.business_cost, var_names.labor_cost, var_names.tags]
        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)

            current_time = times.get_current_timestamp()
            api_key = tokenizer.extract_api_key(request)
            details = syncer_api_keys.get_api_key_details(conn, cache, current_time, api_key)
            if details is None:
                raise UnauthorizedRequest(errors.err_api_key_request_denied)
            key_id, org_id, perm, for_user, ip_restrictions = details
            request_validator.validate_api_ip_allowed(request, ip_restrictions)

            org_perm = syncer_organizations.get_single_organization_permission(conn, cache, current_time, org_id)
            if not permissions.has_org_permission(org_perm, permissions.ORG_ANALYTICS_SECONDARY_PERMISSION):
                return Response(_lt.get_label(errors.err_subscription_rights, lang), status=403)

            if not permissions.can_user_view_analytics(perm):
                return Response(_lt.get_label(errors.err_api_key_permission, lang), status=403)

            start_date = times.get_date_from_string(request.data[var_names.start_date])
            end_date = times.get_date_from_string(request.data[var_names.end_date])
            req_tz = request.data[var_names.timezone]
            team_refs = [key_manager.unmask_reference_key(x) for x in request.data[var_names.teams]]\
                if var_names.teams in request.data else None
            bus_cost = request.data[var_names.business_cost]\
                if var_names.business_cost in request.data else None
            labor_cost = request.data[var_names.labor_cost] if var_names.labor_cost in request.data else None
            tags = request.data[var_names.tags] if var_names.tags in request.data else None

            analytics_maker = BusinessImpactAnalytics(conn, current_time, org_id, start_date, end_date, req_tz,
                                                      team_refs=team_refs, hourly_business_loss=bus_cost,
                                                      hourly_labor_cost=labor_cost, tags=tags)
            return Response(analytics_maker.get_metrics())
        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 KeyError as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_unknown_resource, lang), status=400)
        except PermissionError as e:
            logging.exception(str(e))
            return Response(_lt.get_label(str(e), lang), status=403)
        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'])
@throttle_classes([UserRateThrottle])
def service_analytics(request, conn=None, cache=None):
    '''
    Get service analytics. It is only available for plans with advanced analytics permission (highest tier).
    :param request: Http request
    :param conn: database connection
    :param cache: cache client
    :return: JSON response
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.start_date, var_names.end_date, var_names.timezone]
        optional_fields = [var_names.services, var_names.teams, var_names.tags]
        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)

            current_time = times.get_current_timestamp()
            api_key = tokenizer.extract_api_key(request)
            details = syncer_api_keys.get_api_key_details(conn, cache, current_time, api_key)
            if details is None:
                raise UnauthorizedRequest(errors.err_api_key_request_denied)
            key_id, org_id, perm, for_user, ip_restrictions = details
            request_validator.validate_api_ip_allowed(request, ip_restrictions)

            # make sure the rights permissions are present
            org_perm = syncer_organizations.get_single_organization_permission(conn, cache, current_time, org_id)
            if not permissions.has_org_permission(org_perm, permissions.ORG_ANALYTICS_SECONDARY_PERMISSION):
                return Response(_lt.get_label(errors.err_subscription_rights, lang), status=403)

            if not permissions.can_user_view_analytics(perm):
                return Response(_lt.get_label(errors.err_api_key_permission, lang), status=403)

            start_date = times.get_date_from_string(request.data[var_names.start_date])
            end_date = times.get_date_from_string(request.data[var_names.end_date])
            req_tz = request.data[var_names.timezone]
            serv_refs = [key_manager.unmask_reference_key(x) for x in request.data[var_names.services]]\
                if var_names.services in request.data else None
            team_refs = [key_manager.unmask_reference_key(x) for x in request.data[var_names.teams]]\
                if var_names.teams in request.data else None
            tags = request.data[var_names.tags] if var_names.tags in request.data else None

            analytics_maker = ServiceAnalytics(conn, current_time, org_id, start_date, end_date, req_tz,
                                               service_refs=serv_refs, team_refs=team_refs, tags=tags)
            return Response(analytics_maker.get_metrics())
        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 KeyError as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_unknown_resource, lang), status=400)
        except PermissionError as e:
            logging.exception(str(e))
            return Response(_lt.get_label(str(e), lang), status=403)
        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'])
@throttle_classes([UserRateThrottle])
def team_analytics(request, conn=None, cache=None):
    '''
    Get team analytics. It is only available for plans with secondary analytics permission.
    :param request: Http request
    :param conn: database connection
    :param cache: cache client
    :return: JSON response
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.start_date, var_names.end_date, var_names.timezone]
        optional_fields = [var_names.teams, var_names.urgency_level, var_names.tags]
        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)

            current_time = times.get_current_timestamp()
            api_key = tokenizer.extract_api_key(request)
            details = syncer_api_keys.get_api_key_details(conn, cache, current_time, api_key)
            if details is None:
                raise UnauthorizedRequest(errors.err_api_key_request_denied)
            key_id, org_id, perm, for_user, ip_restrictions = details
            request_validator.validate_api_ip_allowed(request, ip_restrictions)

            # make sure the rights permissions are present
            org_perm = syncer_organizations.get_single_organization_permission(conn, cache, current_time, org_id)
            if not permissions.has_org_permission(org_perm, permissions.ORG_ANALYTICS_SECONDARY_PERMISSION):
                return Response(_lt.get_label(errors.err_subscription_rights, lang), status=403)

            if not permissions.can_user_view_analytics(perm):
                return Response(_lt.get_label(errors.err_api_key_permission, lang), status=403)

            start_date = times.get_date_from_string(request.data[var_names.start_date])
            end_date = times.get_date_from_string(request.data[var_names.end_date])
            req_tz = request.data[var_names.timezone]
            team_refs = [key_manager.unmask_reference_key(x) for x in request.data[var_names.teams]]\
                if var_names.teams in request.data else None
            urg_lvls = request.data[var_names.urgency_level]\
                if var_names.urgency_level in request.data else None
            tags = request.data[var_names.tags] if var_names.tags in request.data else None

            analytics_maker = TeamAnalytics(conn, org_id, start_date, end_date, req_tz,
                                            urgency_levels=urg_lvls, team_refs=team_refs, tags=tags)
            return Response(analytics_maker.get_metrics())
        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 KeyError as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_unknown_resource, lang), status=400)
        except PermissionError as e:
            logging.exception(str(e))
            return Response(_lt.get_label(str(e), lang), status=403)
        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'])
@throttle_classes([UserRateThrottle])
def user_summary_analytics(request, conn=None, cache=None):
    '''
    Gets performance related metrics of all users of the organization.
    :param request: Http request
    :param conn: db connection
    :param cache: cache client
    :return: JSON response -> dict
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        expected_fields = [var_names.start_date, var_names.end_date]
        optional_fields = [var_names.tags]
        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)

            current_time = times.get_current_timestamp()
            api_key = tokenizer.extract_api_key(request)
            details = syncer_api_keys.get_api_key_details(conn, cache, current_time, api_key)
            if details is None:
                raise UnauthorizedRequest(errors.err_api_key_request_denied)
            key_id, org_id, perm, for_user, ip_restrictions = details
            request_validator.validate_api_ip_allowed(request, ip_restrictions)

            # make sure the rights permissions are present
            org_perm = syncer_organizations.get_single_organization_permission(conn, cache, current_time, org_id)
            if not permissions.has_org_permission(org_perm, permissions.ORG_ANALYTICS_SECONDARY_PERMISSION):
                return Response(_lt.get_label(errors.err_subscription_rights, lang), status=403)

            if not permissions.can_user_view_analytics(perm):
                return Response(_lt.get_label(errors.err_api_key_permission, lang), status=403)

            start_date = times.get_date_from_string(request.data[var_names.start_date])
            end_date = times.get_date_from_string(request.data[var_names.end_date])
            tags = request.data[var_names.tags] if var_names.tags in request.data else None

            analytics_maker = UserAnalytics(conn, current_time, org_id, start_date, end_date, tags=tags)

            return Response(analytics_maker.get_org_users_performance_metrics_summary())
        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 PermissionError as e:
            logging.exception(str(e))
            return Response(_lt.get_label(str(e), lang), status=403)
        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'])
@throttle_classes([UserRateThrottle])
def user_detailed_analytics(request, conn=None, cache=None):
    '''
    Get detailed analytics metrics specific to a user.
    :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.preferred_username, var_names.start_date, var_names.end_date]
        optional_fields = [var_names.tags]
        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)

            current_time = times.get_current_timestamp()
            api_key = tokenizer.extract_api_key(request)
            details = syncer_api_keys.get_api_key_details(conn, cache, current_time, api_key)
            if details is None:
                raise UnauthorizedRequest(errors.err_api_key_request_denied)
            key_id, org_id, perm, for_user, ip_restrictions = details
            request_validator.validate_api_ip_allowed(request, ip_restrictions)

            # make sure the rights permissions are present
            org_perm = syncer_organizations.get_single_organization_permission(conn, cache, current_time, org_id)
            if not permissions.has_org_permission(org_perm, permissions.ORG_ANALYTICS_SECONDARY_PERMISSION):
                return Response(_lt.get_label(errors.err_subscription_rights, lang), status=403)

            if not permissions.can_user_view_analytics(perm):
                return Response(_lt.get_label(errors.err_api_key_permission, lang), status=403)

            req_pref_name = request.data[var_names.preferred_username]
            start_date = times.get_date_from_string(request.data[var_names.start_date])
            end_date = times.get_date_from_string(request.data[var_names.end_date])
            tags = request.data[var_names.tags] if var_names.tags in request.data else None
            current_time = times.get_current_timestamp()

            req_user_id = db_users.get_user_ids_from_preferred_usernames(
                conn, current_time, org_id, [req_pref_name])

            if len(req_user_id) == 0:
                return Response(_lt.get_label(errors.err_unknown_resource, lang), status=401)
            else:
                req_user_id = req_user_id[0]

            analytics_maker = UserAnalytics(conn, current_time, org_id, start_date, end_date,
                                            user_id=req_user_id, tags=tags)
            return Response(analytics_maker.get_user_specific_detailed_metrics())
        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 KeyError as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_unknown_resource, lang), status=400)
        except PermissionError as e:
            logging.exception(str(e))
            return Response(_lt.get_label(str(e), lang), status=403)
        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)
