# coding: utf-8

from django.db import connection
from django.utils.translation import gettext_lazy as _
from rest_framework.exceptions import ValidationError

from procu.api import models

WRONG_ENQUIRY_PRODUCT = _('QUOTE_PRODUCTS::ERROR_WRONG_ENQUIRY_PRODUCT')
WRONG_CURRENCY = _('QUOTE_PRODUCTS::ERROR_WRONG_CURRENCY')
QTY_EXCEEDED = _('QUOTE_PRODUCTS::ERROR_QTY_EXCEEDED')
REPLACEMENTS_NOT_ALLOWED = _('QUOTE_PRODUCTS::ERROR_REPLACEMENTS_NOT_ALLOWED')
REPLACEMENT_NAME_REQUIRED = _('QUOTE_PRODUCTS::ERROR_REPLACEMENT_NAME_REQUIRED')
ALREADY_OFFERED = _('QUOTE_PRODUCTS::ERROR_ALREADY_OFFERED')


def validate_products(quote_id, products):

    no_replacement = models.Request.objects.values_list(
        'no_replacement', flat=True
    ).get(quotes=quote_id)

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

    ids = [p['enquiry_product_id'] for p in products]

    valid_eps = models.EnquiryProduct.objects.filter(
        request__quotes=quote_id, id__in=ids
    ).values('id', 'qty', 'name')

    valid_eps = {p['id']: p for p in valid_eps}

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

    ids = [p['currency_id'] for p in products]
    valid_currencies = set(
        models.Currency.objects.filter(id__in=ids).values_list('id', flat=True)
    )

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

    offered = set()

    errors = [{} for i in products]

    for product, error in zip(products, errors):

        ep_id = product['enquiry_product_id']

        try:
            ep = valid_eps[ep_id]

        except KeyError:
            error['enquiry_product'] = [WRONG_ENQUIRY_PRODUCT]
            continue

        if product['currency_id'] not in valid_currencies:
            error['currency'] = [WRONG_CURRENCY]

        # Offered quantity cannot exceed the one requested
        if product['qty'] > ep['qty']:
            error['qty'] = [QTY_EXCEEDED]

        if product['is_replacement']:

            # Are replacements allowed?
            if no_replacement:
                error['is_replacement'] = [REPLACEMENTS_NOT_ALLOWED]

            # Does replacement have a name?
            if not product.get('name'):
                error['name'] = [REPLACEMENT_NAME_REQUIRED]

        else:
            if ep_id in offered:
                error['is_replacement'] = [ALREADY_OFFERED]
                continue

            offered.add(ep_id)
            product['name'] = ep['name']

    if any(errors):
        raise ValidationError(errors)


def update_products(quote_id, products):

    current_products = set(
        models.QuoteProduct.objects.filter(quote_id=quote_id).values_list(
            'id', flat=True
        )
    )

    new_products = []

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

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

        else:
            # Create a new product
            product['quote_id'] = quote_id
            new_products.append(models.QuoteProduct(**product))

    models.QuoteProduct.objects.bulk_create(new_products)

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

    # Delete products:
    models.QuoteProduct.objects.filter(id__in=current_products).update(
        is_deleted=True
    )

    # Recalculate `has_offer` flag
    with connection.cursor() as cursor:
        cursor.execute(
            """
            UPDATE api_quote q
            SET
                has_offer = c.has_offer
            FROM (
                SELECT
                    COUNT(*) > 0 AS has_offer
                FROM api_quoteproduct
                WHERE
                  quote_id=%s
                  AND is_deleted='f'
                  AND snapshot_id IS NULL
            ) c
            WHERE q.id=%s;
            """,
            (quote_id, quote_id),
        )
