import json

from django.http import HttpResponse
from django.db.models import Prefetch
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django.views import View
from drf_yasg.utils import swagger_auto_schema
from rest_framework.decorators import action
from rest_framework import viewsets
from drf_yasg import openapi
from rest_framework.response import Response
from six import text_type

import elections.api.next.serializers
from api.next.views import ResultsSetPagination
from candidates import models as extra_models
from candidates.api.next.serializers import LoggedActionSerializer
from elections.models import Election
from elections.uk.geo_helpers import (
    get_ballots_from_coords,
    get_ballots_from_postcode,
)
from elections.filters import BallotFilter
from official_documents.models import OfficialDocument
from popolo.models import Membership
from utils.db import LastWord


class UpcomingElectionsView(View):

    http_method_names = ["get"]

    def get(self, request, *args, **kwargs):
        postcode = request.GET.get("postcode", None)
        coords = request.GET.get("coords", None)

        # TODO: postcode may not make sense everywhere
        errors = None
        if not postcode and not coords:
            errors = {"error": "Postcode or Co-ordinates required"}

        try:
            if coords:
                ballots = get_ballots_from_coords(coords)
            else:
                ballots = get_ballots_from_postcode(postcode)
        except Exception as e:
            errors = {"error": e.message}

        if errors:
            return HttpResponse(
                json.dumps(errors), status=400, content_type="application/json"
            )

        results = []
        ballots = ballots.select_related("post", "election")
        for ballot in ballots:
            results.append(
                {
                    "post_name": ballot.post.label,
                    "post_slug": ballot.post.slug,
                    "organization": ballot.post.organization.name,
                    "election_date": text_type(ballot.election.election_date),
                    "election_name": ballot.election.name,
                    "election_id": ballot.election.slug,
                }
            )

        return HttpResponse(
            json.dumps(results), content_type="application/json"
        )


class ElectionViewSet(viewsets.ReadOnlyModelViewSet):
    lookup_value_regex = r"(?!\.json$)[^/]+"
    queryset = Election.objects.order_by("id")
    lookup_field = "slug"
    serializer_class = elections.api.next.serializers.ElectionSerializer
    filterset_fields = ("current",)
    pagination_class = ResultsSetPagination


class BallotViewSet(viewsets.ReadOnlyModelViewSet):
    """
    A paginated list of all ballots

    """

    lookup_field = "ballot_paper_id"
    lookup_value_regex = "[^/]+"
    queryset = (
        extra_models.Ballot.objects.select_related("election", "post")
        .prefetch_related(
            Prefetch(
                "membership_set",
                queryset=Membership.objects.all()
                .select_related("result", "person", "party")
                .annotate(last_name=LastWord("person__name"))
                .order_by(
                    "-elected",
                    "-result__is_winner",
                    "-result__num_ballots",
                    "person__sort_name",
                    "last_name",
                ),
            ),
            Prefetch(
                "officialdocument_set",
                queryset=OfficialDocument.objects.filter(
                    document_type=OfficialDocument.NOMINATION_PAPER
                ).order_by("modified"),
            ),
        )
        .order_by("-election__election_date", "ballot_paper_id")
    )
    serializer_class = elections.api.next.serializers.BallotSerializer
    pagination_class = ResultsSetPagination

    filterset_class = BallotFilter

    def retrieve(self, request, *args, **kwargs):
        """
        A single `Ballot` object
        """
        return super().retrieve(request, *args, **kwargs)

    history_response = openapi.Response("", LoggedActionSerializer)

    @swagger_auto_schema(method="get", responses={200: history_response})
    @action(detail=True, methods=["get"], name="Ballot History")
    def history(self, request, pk=None, **kwargs):
        """
        A list of `LoggedAction` objects for this Ballot

        Returns a list of `LoggedAction` objects filtered by this ballot and
        ordered by the date it was created.

        Typically, this includes locking, unlocking events and entering results

        """
        qs = (
            extra_models.LoggedAction.objects.filter(
                ballot__ballot_paper_id=kwargs["ballot_paper_id"]
            )
            .select_related("ballot", "user")
            .order_by("-created")
        )

        page = self.paginate_queryset(qs)
        if page is not None:
            serializer = LoggedActionSerializer(
                page, many=True, context={"request": request}
            )
            return self.get_paginated_response(serializer.data)

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


class ElectionTypesList(viewsets.ReadOnlyModelViewSet):
    """
    A list of election types as defined in [EveryElection](https://elections.democracyclub.org.uk/election_types/)
    """

    serializer_class = elections.api.next.serializers.ElectionTypeSerializer
    pagination_class = ResultsSetPagination
    queryset = (
        Election.objects.order_by("for_post_role")
        .distinct("for_post_role")
        .values("slug", "for_post_role")
    )

    def retrieve(self, request, *args, **kwargs):
        """
        A single `ElectionType` object
        """
        return super().retrieve(request, *args, **kwargs)

    @method_decorator(cache_page(60 * 60 * 24))
    def list(self, request, version, format=None):
        qs = self.get_queryset()

        page = self.paginate_queryset(qs)
        if page is not None:
            serializer = elections.api.next.serializers.ElectionTypeSerializer(
                qs, many=True
            )
            return self.get_paginated_response(serializer.data)

@@ -1,54 +1,4 @@
-import json%0A%0Afrom django.http import HttpResponse%0A
 from
@@ -147,27 +147,23 @@
 om d
-jango.views
+rf_yasg
  import 
 View
@@ -158,20 +158,23 @@
  import 
-View
+openapi
 %0Afrom dr
@@ -236,91 +236,63 @@
 work
-.decorators import action%0Afrom rest_framework import viewsets%0Afrom drf_yasg
+ import viewsets%0Afrom rest_framework.decorators
  import 
 open
@@ -283,31 +283,30 @@
 tors import 
-openapi
+action
 %0Afrom rest_f
@@ -342,34 +342,8 @@
 onse
-%0Afrom six import text_type
 %0A%0Aim
@@ -543,147 +543,8 @@
 zer%0A
-from elections.models import Election%0Afrom elections.uk.geo_helpers import (%0A    get_ballots_from_coords,%0A    get_ballots_from_postcode,%0A)%0A
 from
@@ -582,16 +582,54 @@
 tFilter%0A
+from elections.models import Election%0A
 from off
@@ -748,1469 +748,8 @@
 d%0A%0A%0A
-class UpcomingElectionsView(View):%0A%0A    http_method_names = %5B%22get%22%5D%0A%0A    def get(self, request, *args, **kwargs):%0A        postcode = request.GET.get(%22postcode%22, None)%0A        coords = request.GET.get(%22coords%22, None)%0A%0A        # TODO: postcode may not make sense everywhere%0A        errors = None%0A        if not postcode and not coords:%0A            errors = %7B%22error%22: %22Postcode or Co-ordinates required%22%7D%0A%0A        try:%0A            if coords:%0A                ballots = get_ballots_from_coords(coords)%0A            else:%0A                ballots = get_ballots_from_postcode(postcode)%0A        except Exception as e:%0A            errors = %7B%22error%22: e.message%7D%0A%0A        if errors:%0A            return HttpResponse(%0A                json.dumps(errors), status=400, content_type=%22application/json%22%0A            )%0A%0A        results = %5B%5D%0A        ballots = ballots.select_related(%22post%22, %22election%22)%0A        for ballot in ballots:%0A            results.append(%0A                %7B%0A                    %22post_name%22: ballot.post.label,%0A                    %22post_slug%22: ballot.post.slug,%0A                    %22organization%22: ballot.post.organization.name,%0A                    %22election_date%22: text_type(ballot.election.election_date),%0A                    %22election_name%22: ballot.election.name,%0A                    %22election_id%22: ballot.election.slug,%0A                %7D%0A            )%0A%0A        return HttpResponse(%0A            json.dumps(results), content_type=%22application/json%22%0A        )%0A%0A%0A
 clas

Remove UpcomingElectionsView