from collections import defaultdict
from django.db.models import Q
from rest_framework.generics import CreateAPIView, UpdateAPIView, RetrieveAPIView, ListAPIView, ListCreateAPIView, DestroyAPIView
from django_filters.rest_framework import DjangoFilterBackend
from user import permission as userPermissions
from django.shortcuts import get_object_or_404
from rest_framework.response import Response
from seller import models as sellerModel
from rest_framework import permissions
from core import models as coreModel
from rest_framework import filters
from . import filters as custom_filters
from user import exceptions as ex
from django.utils import timezone
from rest_framework import status
from . import models as saleModel
from setting import models as settingModel
from core import paginations
from drf_yasg.utils import swagger_auto_schema
from drf_yasg import openapi
from . import serializers
from . import sms
from .task import calculate_lowest_price
import math


class CreateInquiry(CreateAPIView):
    serializer_class = serializers.InquiryBoxesSerializer

    @swagger_auto_schema(manual_parameters=[
        openapi.Parameter(
            'from',
            openapi.IN_QUERY,
            description="Optional search term",
            type=openapi.TYPE_STRING,
            required=False
        ),
    ])
    def post(self, request, *args, **kwargs):
        return super().post(request, *args, **kwargs)

    def perform_create(self, serializer):
        from_value = self.request.query_params.get('from')
        if from_value and from_value == 'bot':
            serializer.save(customer_id=self.request.user.id, temp=False)
        else:
            serializer.save(customer_id=self.request.user.id, temp=True)

        for box in serializer.data.get('boxes'):
            data = self._check_24_hours(box)
            self._send_to_sellers(data)
            calculate_lowest_price(box_id=data['id'], schedule=settingModel.SiteSetting.get_solo().inquiry_time * 60)


    @staticmethod
    def _check_24_hours(data):
        for inquiry in data['inquiries']:
            obj = saleModel.Inquiry.objects.filter(product_id=inquiry['product'],
                                                   originality=inquiry['originality'],
                                                   inquiry_box__car_id=data['car'],
                                                   inquiry_box__type=data['type'],
                                                   inquiry_box__production_year=data['production_year'],
                                                   inquiry_box__engine_volume=data['engine_volume'])
            if obj:
                inquiries = [item.id for item in obj]
                suggested = saleModel.SuggestedPrices.objects.filter(inquiry_id__in=inquiries, accepted=True,
                                                                     created__gte=
                                                                     timezone.now() - timezone.timedelta(hours=24))
                if suggested is not None:
                    suggested_array = [item.id for item in suggested]
                    base = saleModel.PriceBasis.objects.filter(suggested_id__in=suggested_array, stock__gte=1).first()
                    if base:
                        suggested = [a for a in suggested if a.id == base.suggested.id]
                        inquiry['suggested'] = serializers.SuggestedPricesSerializer(suggested[0]).data
            if 'suggested' not in inquiry:
                inquiry['suggested'] = None
        return data

    def _send_to_sellers(self, data):
        groups = {}
        send_to_winners = []
        for inquiry in data['inquiries']:
            if inquiry['suggested']:
                send_to_winners.append(inquiry)
            else:
                product = coreModel.Product.objects.get(id=inquiry['product'])
                inquiry['group'] = product.group or ''
                if inquiry['group'] not in groups:
                    groups[inquiry['group']] = []
                groups[inquiry['group']].append(inquiry)
        for key in groups:
            if key == '' or len(groups[key]) == 1:
                self._send_normally(groups[key], data)
            else:
                self._send_specially(groups[key], data)
        if send_to_winners:
            self._send_to_winner(send_to_winners)

    @staticmethod
    def _send_normally(inquiries, data):
        seller_products = defaultdict(list)
        for inquiry in inquiries:
            related = sellerModel.Seller.objects.filter(Q(car__id=data['car']) & Q(product__id=inquiry['product']))
            # sellers = []
            for obj in related:
                saleModel.SuggestedPrices(seller_id=obj.id, inquiry_id=inquiry['id']).save()
                seller_products[obj.mobile].append(inquiry['product_name'])
                # sellers.append(obj.mobile)
            # sms.send_notification_per_product(sellers, data['car_name'], inquiry['product_name'])
        sms.send_notification(seller_products, data['car_name'])

    @staticmethod
    def _send_specially(inquiries, data):
        products = []
        for inquiry in inquiries:
            products.append(inquiry['product'])
        related = sellerModel.Seller.objects.filter(Q(car__id=data['car']) & Q(product__id__in=products)).values('id').distinct()

        list_of_groups = []
        inner_loop = math.ceil(len(related) / len(inquiries))
        outer_loop = len(inquiries)
        if len(related) < len(inquiries):
            inner_loop = 1
            outer_loop = len(related)

        for col in range(0, outer_loop):
            inner = []
            for row in range(col * inner_loop, col * inner_loop + inner_loop):
                if row < len(related):
                    inner.append(related[row]['id'])
            list_of_groups.append(inner)

        seller_products = defaultdict(list)
        for i in range(len(list_of_groups)):
            # sellers = []
            for seller in list_of_groups[i]:
                saleModel.SuggestedPrices(seller_id=seller, inquiry_id=inquiries[i]['id']).save()
                seller_products[sellerModel.Seller.objects.get(user_ptr=seller).mobile].append(inquiries[i]['product_name'])
                # sellers.append(sellerModel.Seller.objects.get(user_ptr=seller).mobile)
            # sms.send_notification_per_product(sellers, data['car_name'], inquiries[i]['product_name'])
        sms.send_notification(seller_products, data['car_name'])

    @staticmethod
    def _send_to_winner(inquiries):
        for inquiry in inquiries:
            suggested = saleModel.SuggestedPrices(seller_id=inquiry['suggested']['seller'], inquiry_id=inquiry['id'],
                                                  price=inquiry['suggested']['price'], status='from-base',
                                                  total_price=inquiry['suggested']['total_price'],
                                                  delivery_time=inquiry['suggested']['delivery_time'],
                                                  created=timezone.now(), accepted=True)
            suggested.save()
            inquiry['suggested'] = serializers.SuggestedPricesSerializer(suggested).data


class CreateTempInquiry(CreateAPIView):
    serializer_class = serializers.TempInquiryBoxSerializer

    def perform_create(self, serializer):
        serializer.save(customer_id=self.request.user.id)


class DeleteTempInquiry(DestroyAPIView):
    serializer_class = serializers.TempInquirySerializer

    def get_queryset(self):
        queryset = saleModel.Inquiry.objects.filter(inquiry_box__customer=self.request.user, inquiry_box__temp=True)
        return queryset


class DeleteTempInquiryBox(DestroyAPIView):
    serializer_class = serializers.TempInquiryBoxSerializer

    def get_queryset(self):
        queryset = saleModel.InquiryBox.objects.filter(customer=self.request.user, temp=True)
        return queryset


class RetrieveInquiryBox(RetrieveAPIView):
    serializer_class = serializers.InquiryBoxSerializer

    def get_queryset(self):
        return saleModel.InquiryBox.objects.filter(customer_id=self.request.user.id)


class RetrieveInquiryBoxes(ListAPIView):
    """
        set query string "current" to get current inquiry boxes
    """
    serializer_class = serializers.InquiryBoxSerializer
    pagination_class = paginations.Set15ForPagination
    filter_backends = (custom_filters.InquiryCurrentFilterBackend,)

    def get_queryset(self):
        query = self.request.query_params.get('query', None)
        if query is not None and query == 'current':
            return saleModel.InquiryBox.objects.filter(
                Q(customer_id=self.request.user.id,
                  created__gte=timezone.now() - timezone.timedelta(hours=9)) |
                Q(customer_id=self.request.user.id, temp=True)
            ).order_by('-id')
        return saleModel.InquiryBox.objects.filter(customer_id=self.request.user.id).order_by('-id')


class UpdateSuggestedPrices(UpdateAPIView):
    serializer_class = serializers.SuggestedSerializer
    queryset = saleModel.SuggestedPrices.objects.all()

    def perform_update(self, serializer):
        if self.request.user.id == self.kwargs.get('seller') and \
                self.kwargs.get('seller') == serializer.instance.seller_id:
            serializer.save(created=timezone.now(), status='priced')
        else:
            raise ex.ForbiddenException


class RetrieveBestOfSuggestedPrices(ListAPIView):
    serializer_class = serializers.SuggestedPricesSerializer

    def get(self, request, *args, **kwargs):
        if self.request.user.id == self.kwargs.get('customer'):
            inquiry_box = get_object_or_404(saleModel.InquiryBox, customer_id=self.kwargs.get('customer'),
                                            id=self.kwargs.get('inquiry_box'))
            inquiries = saleModel.Inquiry.objects.filter(inquiry_box_id=inquiry_box.id)
            inquiries_result = []
            for inquiry in inquiries:
                query = '''
                    (SELECT id, inquiry_id, total_price, delivery_time, seller_id, created
                    FROM `sale_suggestedprices` WHERE inquiry_id = {} and
                    total_price = (select MIN(total_price) from `sale_suggestedprices` where inquiry_id = {})
                    order by delivery_time limit 1)
                    union
                    (SELECT id, inquiry_id, total_price, delivery_time, seller_id, created
                    FROM `sale_suggestedprices` WHERE inquiry_id = {} and
                    delivery_time = (select MIN(delivery_time) from `sale_suggestedprices` 
                    where inquiry_id = {}) order by total_price limit 1)
                    '''.format(inquiry.id, inquiry.id, inquiry.id, inquiry.id)
                best_suggestions = saleModel.SuggestedPrices.objects.raw(query)
                suggestions = []
                base = False
                for suggestion in best_suggestions:
                    serializer = serializers.SuggestedPricesSerializer(suggestion)
                    instance = saleModel.SuggestedPrices.objects.get(id=serializer.instance.id)
                    if len(suggestions) != 0:  # add most fast if it is different
                        if serializer.data['total_price'] != suggestions[0]['total_price'] or \
                                serializer.data['delivery_time'] != suggestions[0]['delivery_time']:
                            suggestions.append(serializer.data)
                    else:
                        base = True if instance.status == 'from-base' else False
                        suggestions.append(serializer.data)
                if not base:
                    inquiry_result = {
                        'inquiry': inquiry.id,
                        'suggestions': suggestions
                    }
                    inquiries_result.append(inquiry_result)
            return Response(inquiries_result, status=status.HTTP_200_OK)
        raise ex.ForbiddenException


class PriceBasisList(ListAPIView):
    permission_classes = [permissions.AllowAny]
    serializer_class = serializers.PriceBasisSerializer
    pagination_class = paginations.Set15ForPagination
    filter_backends = [filters.SearchFilter, DjangoFilterBackend]
    filterset_fields = ['suggested__inquiry__product__subcategory__category',
                        'suggested__inquiry__product__subcategory', 'suggested__inquiry__inquiry_box__car__brand']
    search_fields = ['suggested__inquiry__product__name', 'suggested__inquiry__inquiry_box__car__name']

    def get_queryset(self):
        offset = self.request.query_params.get('offset')
        limit = self.request.query_params.get('limit')
        if offset is not None and limit is not None:
            limit = int(offset) + int(limit)
            return saleModel.PriceBasis.objects.filter(expired__gte=timezone.now(), stock__gte=1).order_by('-id')[int(offset):limit]
        return saleModel.PriceBasis.objects.filter(expired__gte=timezone.now(), stock__gte=1).order_by('-id')


class UpdatePriceBasis(UpdateAPIView):
    serializer_class = serializers.PriceBasisSerializer
    queryset = saleModel.PriceBasis.objects.all()

    def perform_update(self, serializer):
        if self.request.user.id == self.kwargs.get('seller') and \
                self.kwargs.get('seller') == serializer.instance.suggested.seller_id:
            serializer.save()
        else:
            raise ex.ForbiddenException


class OrdersList(ListAPIView):
    serializer_class = serializers.ListOrderSerializer
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['status', ]

    def get_queryset(self):
        if self.request.user.id == self.kwargs.get('user'):
            if self.request.query_params.get('status') == 'cart':
                return saleModel.Orders.objects.filter(user_id=self.kwargs.get('user'),
                                                       created__gte=timezone.now() - timezone.timedelta(minutes=60)).order_by('-id')
            return saleModel.Orders.objects.filter(user_id=self.kwargs.get('user')).exclude(status='cart').order_by('-id')
        raise ex.ForbiddenException


class CreateOrder(CreateAPIView):
    serializer_class = serializers.OrderSerializer

    def perform_create(self, serializer):
        if self.request.user.id == self.kwargs.get('user'):
            serializer.save(user_id=self.request.user.id)
        else:
            raise ex.ForbiddenException


class RetrieveOrder(RetrieveAPIView):
    serializer_class = serializers.ListOrderSerializer

    def get_queryset(self):
        if self.request.user.id == self.kwargs.get('user'):
            return saleModel.Orders.objects.filter(user_id=self.kwargs.get('user'))
        raise ex.ForbiddenException


class SelectAddress(UpdateAPIView):
    serializer_class = serializers.AddressOrderSerializer
    permission_classes = [userPermissions.IsRelatedUser]
    queryset = saleModel.Orders.objects.all()
