import logging

import redis
import requests
from django.conf import settings
from django.contrib.auth.decorators import login_required, permission_required
from django.http import JsonResponse
from django.template.context_processors import csrf
from django.utils.translation import gettext_lazy
from rest_framework.decorators import api_view
from rest_framework.mixins import CreateModelMixin, UpdateModelMixin, RetrieveModelMixin, ListModelMixin
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.viewsets import GenericViewSet

from assets.models.assets import Asset
from core.decorators import log_url
from core.mixins import ApiPermissionCheckMixin
from correlation.models import Rule
from dashboard.models import DashboardLayout
from dashboard.serializers import (AssetsByIncidentsSerializer, IncidentsByCategorySerializer, TopIdsEventsSerializer,
                                   SystemInfoSerializer, SerivcesWidgetSerializer, OpenedIncidentsSerializer,
                                   IncidentsByImportanceSerializer)
from dashboard.serializers import DashboardSerializer
from dashboard.utils import RedisInstances
from dashboard.widgets.opened_incs_widget import get_opened_incidents_count
from dashboard.widgets.services_widget import get_services_list
from dashboard.widgets.sys_info_widget import (get_machine_name, get_operating_system, get_cpu_model, get_uptime,
                                               get_cpu_total,
                                               get_console_version, get_disk_usage, get_mem, get_server_time,
                                               get_description)
from dashboard.widgets.top_ids_widget import get_top_ids_events
from dashboard.widgets.widgets import get_widget_list_from_user
from incident.models import Incident, IncidentCategory
from perms.models import Perm

CORRELATOR_URL = getattr(settings, 'CORRELATOR_URL', 'http://localhost:9200')
redis_instance = redis.StrictRedis(host=getattr(settings, 'REDIS_HOST', 'redis'),
                                   port=getattr(settings, 'REDIS_PORT', 6379), db=0)

_log = logging.getLogger(__name__)


@login_required
@log_url
def add_widget_form(request):
    """ API for rendering the add widget form """
    user = request.user
    available_widgets_for_user_count = len(get_widget_list_from_user(request.user))
    form = AddWidgetForm(user)
    ctx = {}
    ctx.update(csrf(request))
    form = render_crispy_form(form, context=ctx)
    return JsonResponse({'status': 'ok',
                         'form_html': form,
                         'available_widgets_count': available_widgets_for_user_count})


class DashboardLayoutViewSet(ApiPermissionCheckMixin,
                             ListModelMixin,
                             RetrieveModelMixin,
                             UpdateModelMixin,
                             CreateModelMixin,
                             GenericViewSet):
    """ ViewSet for handling all operations with dashboard layout """
    pagination_class = None
    serializer_class = DashboardSerializer
    queryset = DashboardLayout.objects.all()
    console_permissions = {'default': [Perm.can_add_widgets]}


# Incidendts by time widget api
@api_view(['GET'])
@permission_required(Perm.perm_req(Perm.can_add_widgets), raise_exception=True)
def get_current_incidents_by_time_widget_data(request):
    response = {
        'day_stats': redis_instance.get(RedisInstances.WIDGET_INCS_BY_TIME_DAY),
        'week_stats': redis_instance.get(RedisInstances.WIDGET_INCS_BY_TIME_WEEK),
        'month_stats': redis_instance.get(RedisInstances.WIDGET_INCS_BY_TIME_MONTH),
        'year_stats': redis_instance.get(RedisInstances.WIDGET_INCS_BY_TIME_YEAR),
    }
    return Response(response, status=200)


# Events by time widget api
@api_view(['GET'])
@permission_required(Perm.perm_req(Perm.can_add_widgets), raise_exception=True)
def get_current_events_by_time_widget_data(request):
    response = {
        'day_stats': redis_instance.get(RedisInstances.WIDGET_EVENTS_BY_TIME_DAY),
        'week_stats': redis_instance.get(RedisInstances.WIDGET_EVENTS_BY_TIME_WEEK),
        'month_stats': redis_instance.get(RedisInstances.WIDGET_EVENTS_BY_TIME_MONTH),
        'year_stats': redis_instance.get(RedisInstances.WIDGET_EVENTS_BY_TIME_YEAR),
    }
    return Response(response, status=200)


class AssetsByIncidentsViewSet(ApiPermissionCheckMixin, ListModelMixin, GenericViewSet):
    pagination_class = None
    serializer_class = AssetsByIncidentsSerializer
    queryset = Asset.objects.filter(incidents__isnull=False).distinct()
    console_permissions = [Perm.can_add_widgets]


class IncidentsByCategoryViewSet(ApiPermissionCheckMixin, ListModelMixin, GenericViewSet):
    pagination_class = None
    serializer_class = IncidentsByCategorySerializer
    queryset = IncidentCategory.objects.filter(incident__isnull=False).distinct()
    console_permissions = [Perm.can_view_incidents]


class TopIdsEventsWidget(ApiPermissionCheckMixin, APIView):
    console_permissions = [Perm.can_view_events]

    def get(self, request):
        context = {
            'top_ids_events': get_top_ids_events(),
        }

        serializer = TopIdsEventsSerializer(data=context)
        serializer.is_valid()

        return Response(serializer.data)


class IncidentByImportanceApi(ApiPermissionCheckMixin, APIView):
    """ Data source for incidents by importance widget """
    console_permissions = [Perm.can_view_incidents]

    def filter_incidents_by_importance_levels(self, min_importance, max_importance):
        return Incident.objects.filter(importance__range=(min_importance, max_importance)).count()

    def get(self, request):
        _log.info("Call for incident by importance stats")
        context = {
            'importance_none': self.filter_incidents_by_importance_levels(0, 0),
            'importance_info': self.filter_incidents_by_importance_levels(1, 9),
            'importance_low': self.filter_incidents_by_importance_levels(10, 39),
            'importance_medium': self.filter_incidents_by_importance_levels(40, 69),
            'importance_high': self.filter_incidents_by_importance_levels(70, 89),
            'importance_critical': self.filter_incidents_by_importance_levels(90, 101),
        }

        serializer = IncidentsByImportanceSerializer(data=context)
        serializer.is_valid()

        return Response(serializer.data)


class SysTimeWidgetApi(APIView):
    def get(self, request):
        return Response({'server_time': get_server_time()})


class SysInfoWidgetApi(ApiPermissionCheckMixin, APIView):
    """ Data source for system information widget """
    console_permissions = [Perm.can_view_sys_info]

    def get(self, request):
        context = {
            'machine_name': get_machine_name(),
            'operating_system': get_operating_system(),
            'cpu_model': get_cpu_model(),
            'machine_uptime': get_uptime(),
            'total_cpu': get_cpu_total(),
            'console_version': get_console_version(),
            'server_time': get_server_time(),
            'description': get_description(),
        }

        context.update(get_disk_usage())
        context.update(get_mem())

        serializer = SystemInfoSerializer(data=context)
        serializer.is_valid()

        return Response(serializer.data)


class ServicesWidgetApi(ApiPermissionCheckMixin, APIView):
    """ Data source for services widget """
    console_permissions = [Perm.can_view_sys_info]

    def get(self, request):
        serializer = SerivcesWidgetSerializer(data=get_services_list(), many=True)
        serializer.is_valid()

        return Response(serializer.data)


class OpenedIncidentsWidgetApi(ApiPermissionCheckMixin, APIView):
    console_permissions = [Perm.can_view_incidents]

    @staticmethod
    def get(request):
        context = {'inc_count': get_opened_incidents_count()}

        serializer = OpenedIncidentsSerializer(data=context)
        serializer.is_valid()

        return Response(serializer.data)


class CorrelatorStat(ApiPermissionCheckMixin, APIView):
    console_permissions = [Perm.can_view_correlation_rules_list]
    message = 'Cannot access correlator API. Permission denied'

    @staticmethod
    def get(request):
        # Need to call correlator api
        # Then, update it's json
        url = CORRELATOR_URL + '/stats/'

        headers = {'Accept': 'application/json',
                   'Content-Encoding': 'utf-8'}

        _log.info("Call correlator stat")
        try:
            response = requests.get(url, headers=headers)
        except requests.ConnectionError:
            _log.exception("Can't get correlator status")
            return JsonResponse({"status": False, "error": gettext_lazy("Correlator not responding")})

        correlator = response.json()

        # {
        #     "aggregator": {
        #         "events_processed": 0,
        #         "events_aggregated": 0,
        #         "average_iteration": {
        #             "value": "0s"
        #         }
        #     },
        #     "correlator": {
        #         "rule_count": 3,
        #         "average_time": {
        #             "value": "15.487471ms"
        #         },
        #         "average_rule_time": {
        #             "1": {
        #                 "value": "5.338945ms"
        #             },
        #             "2": {
        #                 "value": "4.323614ms"
        #             },
        #             "35": {
        #                 "value": "3.4368ms"
        #             }
        #         },
        #         "incident_count": 0
        #     }
        # }

        correlator["status"] = "true"
        rules = Rule.objects.filter(status=True).all()
        try:
            for key in correlator["correlator"]["average_rule_time"]:
                for cur in rules:
                    if cur.pk == int(key):
                        correlator["correlator"]["average_rule_time"][key]["title"] = cur.name
                        break
        except TypeError:
            correlator["status"] = "false"
            _log.error(f"Got bad json: {correlator}")

        return JsonResponse(correlator)
