import warnings
from io import BytesIO

import pandas as pd
from django.db.models import Q
from django.http import HttpResponse
from rest_framework import status
from rest_framework import status as http_status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.viewsets import ReadOnlyModelViewSet

from service import serializers, models, pagination
from service import utils
from service.utils import load_data


class RayonViewSet(ReadOnlyModelViewSet):
    serializer_class = serializers.RayonSerializer
    queryset = models.Rayon.objects


class AOViewSet(ReadOnlyModelViewSet):
    serializer_class = serializers.AOSerializer
    queryset = models.AO.objects


class PlacementPointViewSet(ReadOnlyModelViewSet):
    serializer_class = serializers.PlacementPointSerializer
    queryset = models.PlacementPoint.objects
    pagination_class = pagination.MyPagination

    def get_queryset(self):
        qs = self.queryset.all()
        location_ids = self.request.GET.get('location_ids[]')
        prediction_first = self.request.GET.get('prediction_first[]')
        prediction_current = self.request.GET.get('prediction_current[]')
        categories = self.request.GET.get('categories[]')
        status = self.request.GET.get('status[]')
        delta = self.request.GET.get('delta[]')
        fact = self.request.GET.get('fact[]')
        age = self.request.GET.get('age[]')
        included = self.request.GET.get('included[]')
        excluded = self.request.GET.get('excluded[]')
        plan_first = self.request.GET.get('plan_first[]')
        plan_current = self.request.GET.get('plan_current[]')
        rayons = self.request.GET.get('rayon[]')
        aos = self.request.GET.get('ao[]')
        if location_ids:
            location_ids = list(location_ids.split(','))
            qs = qs.filter(location_id__in=location_ids)
        if prediction_first:
            prediction_first = list(prediction_first.split(','))
            qs = qs.filter(prediction_first__range=prediction_first)
        if prediction_current:
            prediction_current = list(prediction_current.split(','))
            qs = qs.filter(prediction_current__range=prediction_current)
        if categories:
            categories = list(categories.split(','))
            qs = qs.filter(category__in=categories)
        if status:
            status = list(status.split(','))
            qs = qs.filter(status__in=status)
        if delta:
            delta = list(delta.split(','))
            qs = qs.filter(delta__range=delta)
        if fact:
            fact = list(fact.split(','))
            qs = qs.filter(fact__range=fact)
        if age:
            age = list(age.split(','))
            qs = qs.filter(age__range=age)
        if plan_first:
            plan_first = list(plan_first.split(','))
            qs = qs.filter(plan_first__range=plan_first)
        if plan_current:
            plan_current = list(plan_current.split(','))
            qs = qs.filter(plan_current__range=plan_current)
        if rayons:
            rayons = list(rayons.split(','))
            qs = qs.filter(rayon_id__in=rayons)
        if aos:
            aos = list(aos.split(','))
            qs = qs.filter(okrug_id__in=aos)
        if excluded:
            excluded = list(excluded.split(','))
            qs = qs.filter(~Q(location_id__in=excluded))
        if included:
            inclded = list(included.split(','))
            qs2 = models.PlacementPoint.objects.filter(location_id__in=inclded).all()
            qs = (qs | qs2).distinct()
        return qs

    @action(detail=False, methods=['get'])
    def filters(self, request):
        qs = self.get_queryset()
        age_day = [x for x in list(set(qs.values_list('age_day', flat=True))) if x is not None]
        prediction_first = [x for x in list(set(qs.values_list('prediction_first', flat=True))) if x is not None]
        prediction_current = [x for x in list(set(qs.values_list('prediction_current', flat=True))) if x is not None]
        plan_first = [x for x in list(set(qs.values_list('plan_first', flat=True))) if x is not None]
        plan_current = [x for x in list(set(qs.values_list('plan_current', flat=True))) if x is not None]
        fact = [x for x in list(set(qs.values_list('fact', flat=True))) if x is not None]
        delta_first = [x for x in list(set(qs.values_list('delta_first', flat=True))) if x is not None]
        delta_current = [x for x in list(set(qs.values_list('delta_current', flat=True))) if x is not None]
        flat_cnt = [x for x in list(set(qs.values_list('flat_cnt', flat=True))) if x is not None]
        year_bld = [x for x in list(set(qs.values_list('year_bld', flat=True))) if x is not None]
        levels = [x for x in list(set(qs.values_list('levels', flat=True))) if x is not None]
        doors = [x for x in list(set(qs.values_list('doors', flat=True))) if x is not None]
        flats_cnt = [x for x in list(set(qs.values_list('flats_cnt', flat=True))) if x is not None]
        popul_home = [x for x in list(set(qs.values_list('popul_home', flat=True))) if x is not None]
        popul_job = [x for x in list(set(qs.values_list('popul_job', flat=True))) if x is not None]
        other_post_cnt = [x for x in list(set(qs.values_list('other_post_cnt', flat=True))) if x is not None]
        target_post_cnt = [x for x in list(set(qs.values_list('target_post_cnt', flat=True))) if x is not None]
        yndxfood_cnt = [x for x in list(set(qs.values_list('yndxfood_cnt', flat=True))) if x is not None]
        yndxfood_sum = [x for x in list(set(qs.values_list('yndxfood_sum', flat=True))) if x is not None]
        yndxfood_cnt_cst = [x for x in list(set(qs.values_list('yndxfood_cnt_cst', flat=True))) if x is not None]
        data = {
            "age_day": [min(age_day), max(age_day)] if age_day else [0, 2030],
            "prediction_first": [min(prediction_first), max(prediction_first)] if prediction_first else [-22, 22],
            "prediction_current": [min(prediction_current), max(prediction_current)] if prediction_current else [-22,
                                                                                                                 22],
            "plan_first": [min(plan_first), max(plan_first)] if plan_first else [0, 100],
            "plan_current": [min(plan_current), max(plan_current)] if plan_current else [0, 100],
            "fact": [min(fact), max(fact)] if fact else [0, 100],
            "delta_first": [min(delta_first), max(delta_first)] if delta_first else [0, 100],
            "delta_current": [min(delta_current), max(delta_current)] if delta_current else [0, 100],
            "flat_cnt": [min(flat_cnt), max(flat_cnt)] if flat_cnt else [0, 100],
            "year_bld": [min(year_bld), max(year_bld)] if year_bld else [0, 2030],
            "levels": [min(levels), max(levels)] if levels else [0, 100],
            "doors ": [min(doors), max(doors)] if doors else [0, 100],
            "flats_cnt": [min(flats_cnt), max(flats_cnt)] if flats_cnt else [0, 100],
            "popul_home": [min(popul_home), max(popul_home)] if popul_home else [0, 100],
            "popul_job": [min(popul_job), max(popul_job)] if popul_job else [0, 100],
            "other_post_cnt": [min(other_post_cnt), max(other_post_cnt)] if other_post_cnt else [0, 100],
            "target_post_cnt": [min(target_post_cnt), max(target_post_cnt)] if target_post_cnt else [0, 100],
            "yndxfood_cnt": [min(yndxfood_cnt), max(yndxfood_cnt)] if yndxfood_cnt else [0, 100],
            "yndxfood_sum": [min(yndxfood_sum), max(yndxfood_sum)] if yndxfood_sum else [0, 100],
            "yndxfood_cnt_cst": [min(yndxfood_cnt_cst), max(yndxfood_cnt_cst)] if yndxfood_cnt_cst else [0, 100],
        }
        return Response(data, status=status.HTTP_200_OK)

    @action(detail=False, methods=['get'])
    def search_address(self, request):
        address = self.request.GET.get('address')
        qs = self.get_queryset()
        qs = qs.filter(address__icontains=address)

        page = self.paginate_queryset(qs)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(qs, many=True)
        return Response(serializer.data)

    @action(detail=False, methods=['put'])
    def update_status(self, request):
        qs = self.get_queryset()
        new_status = self.request.GET.get('status')
        if not new_status:
            return Response({'message': 'No status'}, 400)
        qs.update(**{'status': new_status})
        return Response({'message': 'status updated'}, status=http_status.HTTP_200_OK)

    @action(detail=False, methods=['put'])
    def update_fact(self, request):
        point_id = request.GET.get('location_id')
        fact = request.GET.get('fact')
        if not point_id or not fact or not fact.isdigit():
            return Response(status=http_status.HTTP_400_BAD_REQUEST)
        qs = models.PlacementPoint.objects.filter(location_id=point_id)
        if not qs:
            return Response(status=http_status.HTTP_404_NOT_FOUND)
        qs.update(**{'fact': fact})
        return Response({'message': 'fact updated'}, status=http_status.HTTP_200_OK)

    @action(detail=False, methods=['get'])
    def to_excel(self, request):
        qs = self.get_queryset()
        data = pd.DataFrame(list(qs.values()))
        data['start_date'] = data['start_date'].dt.tz_localize(None)
        data['sample_trn'] = data['sample_trn'].astype(int)
        with BytesIO() as b:
            with pd.ExcelWriter(b) as writer:
                data.to_excel(writer, sheet_name="Placement Points", index=False)

            filename = "placement_points.xlsx"
            res = HttpResponse(
                b.getvalue(),
                content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
            )
            res['Content-Disposition'] = f'attachment; filename={filename}'
            return res

    @action(detail=False, methods=['get'])
    def get_10k(self, request):
        if models.PlacementPoint.objects.count() > 10000:
            qs = models.PlacementPoint.objects.order_by('-prediction_current').all()[10000]
            return Response({'prediction_current': qs.prediction_current}, 200)
        return Response(
            {'prediction_current': models.PlacementPoint.objects.order_by(
                'prediction_current').first().prediction_current},
            200)


class refresh_placement_points(APIView):
    @staticmethod
    def post(request):
        warnings.filterwarnings('ignore')
        file = request.FILES['file']
        load_data(file)
        return Response(status=http_status.HTTP_200_OK)


class load_ao_and_rayons(APIView):
    @staticmethod
    def post(request):
        warnings.filterwarnings('ignore')
        file_ao = request.FILES['file_ao']
        file_rayon = request.FILES['file_rayon']
        utils.load_ao_and_rayons(file_ao, file_rayon)
        return Response(status=http_status.HTTP_200_OK)
