# coding=utf8
# -*- coding: utf8 -*-
# vim: set fileencoding=utf8 :

from __future__ import unicode_literals

from django.conf import settings
from django.utils.timezone import now
from rest_framework import mixins, permissions, status, viewsets
from rest_framework.decorators import detail_route, list_route
from rest_framework.response import Response
from rest_messaging.compat import compat_get_paginated_response, compat_get_request_data, compat_pagination_messages, compat_serializer_check_is_valid, compat_thread_serializer_set, compat_perform_update
from rest_messaging.models import Message, NotificationCheck, Participant, Participation, Thread
from rest_messaging.permissions import IsInThread
from rest_messaging.serializers import MessageNotificationCheckSerializer, ComplexMessageSerializer, SimpleMessageSerializer, ThreadSerializer
import json


class ThreadView(mixins.RetrieveModelMixin,
                 mixins.CreateModelMixin,
                 mixins.UpdateModelMixin,
                 viewsets.GenericViewSet):
    """
    The ThreadView allow us to create threads, and add/remove people to/from them.
    It does not list the messages belonging to the thread.
    """
    queryset = Thread.objects.all().prefetch_related('participants')
    serializer_class = ThreadSerializer
    permission_classes = (IsInThread,)

    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = ThreadSerializer(instance, callback=getattr(settings, 'REST_MESSAGING_SERIALIZE_PARTICIPANTS_CALLBACK', None), context={'request': request})  # self.get_serializer will raise an error in DRF 2.4
        return Response(serializer.data)

    def create(self, request, *args, **kwargs):
        """ We ensure the Thread only involves eligible participants. """
        serializer = self.get_serializer(data=compat_get_request_data(request))
        compat_serializer_check_is_valid(serializer)
        self.perform_create(request, serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def perform_create(self, request, serializer):
        participants_ids = json.loads(compat_get_request_data(self.request).get('participants'))
        thread = Thread.managers.get_or_create_thread(self.request, compat_get_request_data(self.request).get('name'), *participants_ids)
        setattr(serializer, compat_thread_serializer_set(), thread)

    def update(self, request, *args, **kwargs):
        participants_ids = compat_get_request_data(self.request).getlist('participants', [])
        if len(participants_ids) > 0:
            # we warn the user he cannot update the participants here
            return Response("Participant updates not allowed by this method.", status=status.HTTP_400_BAD_REQUEST)
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=compat_get_request_data(request), partial=partial)
        compat_serializer_check_is_valid(serializer)
        try:
            self.perform_update(serializer)
        except:
            compat_perform_update(self, serializer)
        return Response(serializer.data)

    @detail_route(methods=['post'])
    def add_participants(self, request, pk=None):
        # we get the thread and check for permission
        thread = Thread.objects.get(id=pk)
        self.check_object_permissions(request, thread)
        # we get the participants and add them
        participants_ids = json.loads(compat_get_request_data(self.request).get('participants'))
        thread.add_participants(request, *participants_ids)
        # we return the thread
        serializer = self.get_serializer(thread)
        return Response(serializer.data)

    @detail_route(methods=['post'])
    def remove_participant(self, request, pk=None):
        # we get the thread and check for permission
        thread = Thread.objects.get(id=pk)
        self.check_object_permissions(request, thread)
        # we get the participant
        participant_id = compat_get_request_data(self.request).get('participant')
        participant = Participant.objects.get(id=participant_id)
        # we remove him if thread.remove_participant allows us to
        try:
            thread.remove_participant(request, participant)
            # we return the thread
            serializer = self.get_serializer(thread)
            return Response(serializer.data)
        except Exception:
            return Response(status=status.HTTP_400_BAD_REQUEST)

    @detail_route(methods=['get'])
    def get_removable_participants_ids(self, request, pk=None):
        # we get the thread and check for permission
        thread = Thread.objects.get(id=pk)
        self.check_object_permissions(request, thread)
        # we get the removable participants
        removable_participants_ids = thread.get_removable_participants_ids(request)
        # we remove him if thread.remove_participant allows us to
        try:
            return Response({'participants': removable_participants_ids})
        except Exception:
            return Response(status=status.HTTP_400_BAD_REQUEST)

    @detail_route(methods=['post'])
    def mark_thread_as_read(self, request, pk=None):
        """ Pk is the pk of the Thread to which the messages belong. """
        # we get the thread and check for permission
        thread = Thread.objects.get(id=pk)
        self.check_object_permissions(request, thread)
        # we save the date
        try:
            participation = Participation.objects.get(thread=thread, participant=request.rest_messaging_participant)
            participation.date_last_check = now()
            participation.save()
            # we return the thread
            serializer = self.get_serializer(thread)
            return Response(serializer.data)
        except Exception:
            return Response(status=status.HTTP_400_BAD_REQUEST)


@compat_pagination_messages
class MessageView(mixins.ListModelMixin,
                  viewsets.GenericViewSet):
    """ The view only lists and creates. """

    queryset = Message.objects.none()
    serializer_class = ComplexMessageSerializer
    permission_classes = (IsInThread,)

    def get_queryset(self):
        """ We list all the threads involving the user """
        check_notifications = self.request.GET.get("check_notifications", True)
        messages = Message.managers.get_lasts_messages_of_threads(self.request.rest_messaging_participant.id, check_who_read=True, check_is_notification=check_notifications)
        return messages

    @detail_route(methods=['post'], permission_classes=[IsInThread], serializer_class=SimpleMessageSerializer)
    def post_message(self, request, pk=None):
        """ Pk is the pk of the Thread to which the message belongs. """
        # we get the thread and check for permission
        thread = Thread.objects.get(id=pk)
        self.check_object_permissions(request, thread)
        # we get the body
        body = compat_get_request_data(self.request).get('body')
        # we create the message
        # Message.objects.save() could return an Exception
        try:
            message = Message(sender=request.rest_messaging_participant, thread=thread, body=body)
            message.save()
            serializer = SimpleMessageSerializer(message)
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        except Exception:
            return Response(status=status.HTTP_412_PRECONDITION_FAILED)

    @detail_route(methods=['get'], permission_classes=[IsInThread], serializer_class=ComplexMessageSerializer)
    def list_messages_in_thread(self, request, pk=None):
        """ Pk is the pk of the Thread to which the messages belong. """
        # we get the thread and check for permission
        thread = Thread.objects.get(id=pk)
        self.check_object_permissions(request, thread)
        messages = Message.managers.get_all_messages_in_thread(participant_id=request.rest_messaging_participant.id, thread_id=thread.id, check_who_read=True)
        page = self.paginate_queryset(messages)
        if page is not None:
            return compat_get_paginated_response(self, page)
        serializer = ComplexMessageSerializer(messages, many=True)
        return Response(serializer.data)


class NotificationCheckView(mixins.ListModelMixin, viewsets.GenericViewSet):

    serializer_class = MessageNotificationCheckSerializer
    permission_classes = (permissions.IsAuthenticated,)

    @list_route(methods=['post'])
    def check(self, request, *args, **kwargs):
        # we get the NotificationCheck instance corresponding to the user or we create it

        try:
            nc = NotificationCheck.objects.get(participant=request.rest_messaging_participant)
            if nc:
                nc.date_check = now()
                nc.save()
            status_code = status.HTTP_200_OK

        except Exception:
            nc = NotificationCheck.objects.create(participant=request.rest_messaging_participant, date_check=now())
            status_code = status.HTTP_201_CREATED

        serializer = self.get_serializer(nc)
        return Response(serializer.data, status=status_code)


class ParticipantAuthenticationView(mixins.ListModelMixin, viewsets.GenericViewSet):
    """
    View that simply return the participant id of the user as set by the middleware, if it exists.
    """

    permission_classes = (permissions.IsAuthenticated,)

    def list(self, request, *args, **kwargs):
        participant = Participant.objects.get(id=self.request.rest_messaging_participant.id)
        return Response({'id': participant.id})