import logging

from django.db.models import Count, Q
from django.http import Http404
from rest_framework.decorators import action
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin, CreateModelMixin, UpdateModelMixin
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet

from core.mixins import ApiPermissionCheckMixin
from ncircc.models.comments import Comment
from ncircc.models.notification import Notification
from ncircc.serializers.comments import CommentCreateSerializer, CommentRetrieveSerializer
from ncircc.serializers.notification import NotificationSerializer
from ncircc.services.comments import CommentSenderService
from ncircc.services.notification import NotificationSenderCreateService, NotificationUpdateSenderService, \
    NotificationUploadFileService, NOCRCCCreateException
from perms.models import Perm

_log = logging.getLogger(__name__)


class NotificationApiViewSet(ApiPermissionCheckMixin,
                             ListModelMixin,
                             RetrieveModelMixin,
                             CreateModelMixin,
                             UpdateModelMixin,
                             GenericViewSet):
    """View Set for getting list for creating/updating notification ncircc events."""

    serializer_class = NotificationSerializer
    queryset = Notification.objects.order_by('updated')
    console_permissions = {
        'list': [Perm.can_view_message_list],
        'retrieve': [Perm.can_view_message_card],
        'update': [Perm.can_edit_message_card],
        'create': [Perm.can_edit_message_card],
        'comments': [Perm.can_view_message_card],
        'comments_create': ['can_view_message_card'],
    }

    def create(self, request, *args, **kwargs) -> Response:
        serializer = self.get_serializer(data=request.data)

        if not serializer.is_valid():
            errors = serializer.errors
            _log.error(f'Error: {errors}')
            return Response(errors, status=400)
        instance = serializer.save()
        try:
            sender = NotificationSenderCreateService(instance.pk)
        except NOCRCCCreateException as err:
            instance.delete()
            return Response({'error': str(err)}, status=400)
        msg, status = sender.send()
        if status == 400:
            instance.delete()
            _log.error(f'Error: {msg}')
            return Response({'error': msg}, status=400)
        return Response(serializer.data, status=201)

    def update(self, request, *args, **kwargs) -> Response:
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        if not serializer.is_valid():
            errors = serializer.errors
            _log.error(f'Error: {errors}')
            return Response(errors, status=400)
        serializer.save()

        if getattr(instance, '_prefetched_objects_cache', None):
            # If 'prefetch_related' has been applied to a queryset, we need to
            # forcibly invalidate the prefetch cache on the instance.
            instance._prefetched_objects_cache = {}

        sender = NotificationUpdateSenderService(instance.pk)
        msg, status = sender.send()
        if status == 400:
            _log.error(f'Error:{msg}')
            return Response({'error': msg}, status=status)

        return Response(serializer.data, status=200)

    @action(detail=True, methods=['Get'], name='Comment list')
    def comments(self, request, pk=None) -> Response:
        """Return comment list."""
        if not Notification.objects.filter(pk=pk).exists():
            raise Http404
        comments = Comment.objects.filter(notification=pk)
        data = CommentRetrieveSerializer(comments, many=True).data
        comments.update(is_read=True)
        return Response(data)

    @comments.mapping.post
    def comments_create(self, request, pk=None) -> Response:
        """Create comments to ncitcc."""
        if not Notification.objects.filter(pk=pk).exists():
            raise Http404
        serializer = CommentCreateSerializer(data=request.data)

        if not serializer.is_valid():
            errors = serializer.errors
            _log.error(f'Error: {errors}')
            return Response(errors, status=400)

        text = serializer.data.get('text', '')
        sender = CommentSenderService(text, int(pk), request.user)
        msg, status = sender.send()
        if status == 400:
            _log.error(f'Error: {msg}')
            return Response({'error': msg}, status=400)
        return Response(msg, status=201)

    @action(detail=True, methods=['POST'], name='Upload File')
    def upload(self, request, pk=None) -> Response:
        """Upload file data."""
        file = request.FILES
        file_name = file['files'].name
        file_data = file['files'].file
        uploader = NotificationUploadFileService(pk, file_data, file_name)
        msg, status = uploader.send()
        if status == 200:
            return Response({}, status=200)
        else:
            return Response({'error': msg}, status=status)

    def get_queryset(self):
        qs = super().get_queryset()
        qs = qs.annotate(new_comment_count=Count('comment', filter=Q(comment__is_read=False)))
        return qs

    class Meta:
        model = Notification
