# coding: utf-8

from collections import defaultdict

from django.core import mail
from django.db.models import Count, F, Prefetch, Q
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import OrderingFilter
from rest_framework.response import Response

from procu.api import models
from procu.api.enums import RISK
from procu.api.push.external import notify_new_user
from procu.api.spark.tasks import update_supplier_risks
from procu.api.utils import is_readonly
from procu.rest import generics, mixins, permissions
from procu.rest.filters import IncludeFilter, SearchFilter
from procu.rest.pagination import PageNumberPagination
from . import serializers
from .filters import SupplierFilter, SuppliersByTagsFilter


class SupplierList(generics.ListCreateAPIView):
    pagination_class = PageNumberPagination
    permission_classes = (permissions.ListPermission,)
    filter_backends = (
        DjangoFilterBackend,
        SearchFilter,
        OrderingFilter,
        IncludeFilter,
        SuppliersByTagsFilter,
    )
    search_fields = (
        'title',
        'legal_name',
        'terms',
        'vat_id',
        'comment',
        'agents__first_name',
        'agents__last_name',
        'agents__email',
        'tags__label',
        'info',
    )
    queryset = models.Supplier.objects.annotate(
        has_warnings=Count(F('warnings'), filter=Q(warnings__is_deleted=False))
    ).filter(is_deleted=False)
    filter_class = SupplierFilter

    ordering_fields = ('id', 'title', 'legal_name', 'has_contract', 'vat_id')
    ordering = ('title',)

    def perform_create(self, serializer):
        return serializer.save(author=self.request.user)

    def get_serializer_class(self):
        if self.request.method == 'GET':
            return serializers.List
        elif self.request.user.has_perm('api.progress_enquiry'):
            return serializers.SupplierSecrets
        else:
            return serializers.SupplierNoSecrets

    def get_serializer_context(self):
        """
        For GET requests we prefetch all non-staff users,
        create a supplier->user map, and add it into the serialier context.
        It is further rendered by a custom read-only `agents' field.

        This seems cheaper than using prefetch_related('agents')
        as the latter leads to WHERE id IN (...) that usually contains
        as many ids as the number of suppliers.
        """

        context = super().get_serializer_context()

        if self.request.method != 'GET':
            return context

        # --------------------------------------------------------

        agents = defaultdict(list)

        qs = models.User.objects.filter(
            is_staff=False, supplier_id__isnull=False
        )

        for user in qs:
            agents[user.supplier_id].append(
                {'id': user.id, 'full_name': user.full_name}
            )

        # --------------------------------------------------------

        tags = defaultdict(list)

        qs = models.SupplierTag.objects.filter(
            suppliers__id__isnull=False
        ).values_list('suppliers__id', 'id', 'label')

        for supplier, id, label in qs:
            tags[supplier].append({'id': id, 'label': label})

        context['agents'] = agents
        context['tags'] = tags

        # --------------------------------------------------------

        return context


class SupplierCreateCold(generics.CreateAPIView):
    serializer_class = serializers.CreateCold
    output_serializer_class = serializers.SupplierNoSecrets
    permission_classes = (permissions.ListPermission,)
    queryset = models.Supplier.objects.filter(is_deleted=False)

    def perform_create(self, serializer):

        data = serializer.validated_data

        contact = models.User.objects.create(email=data['email'], is_cold=True)

        supplier = models.Supplier.objects.create(
            title=data['title'], author=self.request.user, is_cold=True
        )

        contact.supplier = supplier
        contact.set_unusable_password()
        contact.save(update_fields=['supplier', 'password'])

        return supplier


class SupplierEntry(
    mixins.MakeDeletedImmutable, generics.RetrieveUpdateDestroyAPIView
):
    def get_serializer_context(self):
        context = super().get_serializer_context()
        context['is_readonly'] = is_readonly()
        return context

    def get_serializer_class(self):
        if self.request.user.has_perm('api.progress_enquiry'):
            return serializers.SupplierSecrets
        else:
            return serializers.SupplierNoSecrets

    permission_classes = (permissions.EntryPermission,)
    lookup_url_kwarg = 'supplier_id'
    queryset = models.Supplier.objects.prefetch_related(
        'agents',
        'tags',
        Prefetch(
            'warnings',
            (
                models.SupplierWarning.objects.filter(is_deleted=False)
                .order_by('-id')
                .select_related('author')
            ),
        ),
    )


class SupplierWarmUp(generics.GenericAPIView):

    serializer_class = serializers.SupplierNoSecrets
    permission_classes = (permissions.EntryPermission,)
    lookup_url_kwarg = 'supplier_id'
    queryset = models.Supplier.objects.filter(is_cold=True)

    def post(self, request, *args, **kwargs):

        supplier = self.object

        supplier.is_cold = False
        supplier.save(update_fields=['is_cold'])

        messages = []

        for user in supplier.agents.all():
            if user.is_cold:
                user.is_cold = False
                user.save(update_fields=['is_cold'])

                messages.extend(notify_new_user(user))

        with mail.get_connection(fail_silently=True) as conn:
            conn.send_messages(messages)

        return Response({'result': 'ok'})


class SupplierRetrieveUpdateRisks(generics.RetrieveAPIView):
    permission_classes = (permissions.EntryPermission,)
    lookup_url_kwarg = 'supplier_id'
    queryset = models.Supplier.objects.all()
    serializer_class = serializers.SupplierRisks

    def patch(self, request, *args, **kwargs):
        instance = self.object

        instance.risk = RISK.FETCHING
        instance.risk_data = {}
        instance.save(update_fields=('risk', 'risk_data'))

        update_supplier_risks.apply_async(
            kwargs={'supplier_id': instance.id, 'update': True}
        )

        return Response({'result': 'ok'})
