# coding: utf-8

from typing import List, MutableMapping

from django.db.models import Count, Q
from django.utils.translation import gettext_lazy as _

from procu.api import models
from procu.api.enquiry.fields import Fields
from procu.rest import serializers


class Product(serializers.ModelSerializer):
    id = serializers.IntegerField(required=False)

    class Meta:
        model = models.EnquiryProduct
        fields = ('id', 'name', 'qty', 'comment')
        extra_kwargs = {
            'name': {'max_length': 512},
            'qty': {'min_value': 0},
            'comment': {'required': False, 'allow_blank': True},
        }


def update_products(rfx: models.Request, data: List[MutableMapping]):

    current_products = set(
        models.EnquiryProduct.objects.filter(request_id=rfx.id).values_list(
            'id', flat=True
        )
    )

    for product in data:
        product_id = product.pop('id', None)

        if product_id in current_products:
            # Update existing product
            current_products.discard(product_id)
            models.EnquiryProduct.objects.filter(id=product_id).update(
                **product
            )

        else:
            # Create a new product
            product['request_id'] = rfx.id
            models.EnquiryProduct.objects.create(**product)

    # At this point `current_products` only contains products to delete.

    # Delete products (including snapshots):
    models.QuoteProduct.with_deleted.filter(
        enquiry_product_id__in=current_products
    ).update(is_deleted=True)

    models.EnquiryProduct.objects.filter(id__in=current_products).update(
        is_deleted=True
    )

    if current_products:
        # If products has been deleted,
        # recalculate the value of `has_offer` flag for each quote.
        quotes = models.Quote.objects.filter(request_id=rfx.id).annotate(
            n_products=Count('products', Q(is_deleted=False))
        )

        for quote in quotes:
            if bool(quote.n_products) is not quote.has_offer:
                quote.has_offer = bool(quote.n_products)
                quote.save(update_fields=['has_offer'])


class Request(serializers.ModelSerializer):
    check_for_nested_writes = False

    products = Product(many=True, label=_('ENQUIRIES::PRODUCTS'))
    address = Fields.address(allow_null=False)
    legal_entity = Fields.legal_entity(allow_null=False)
    attachments = Fields.attachments()

    class Meta:
        model = models.Request
        fields = (
            'subject',
            'products',
            'description',
            'no_replacement',
            'address',
            'legal_entity',
            'attachments',
            'show_assignee',
        )
        extra_kwargs = {'subject': {'max_length': 512}}

    def update(self, instance, data):

        products = data.pop('products', None)

        if 'attachments' in data:
            data['attachments'] = [
                att
                for att in data['attachments']
                if att.request is None or att.request_id == instance.id
            ]

        instance = super().update(instance, data)

        # Products zone
        if products is not None:
            update_products(instance, products)

        return instance


class RetrieveUpdateDeadline(serializers.ModelSerializer):

    deadline_at = Fields.deadline_at(allow_null=False)

    class Meta:
        model = models.Request
        fields = ('deadline_at',)
