from datetime import datetime, timedelta

from django.conf import settings
from django.db import models
from django.contrib.gis.geos import Point
from elections.query_helpers import get_point_from_postcode


class ElectionQuerySet(models.QuerySet):
    def for_point(self, point):
        return self.filter(
            models.Q(division_geography__geography__contains=point)
            | models.Q(organisation_geography__geography__contains=point)
        )

    def for_lat_lng(self, lat, lng):
        point = Point(lng, lat)
        return self.for_point(point)

    def for_postcode(self, postcode):
        point = get_point_from_postcode(postcode)
        return self.for_point(point)

    def current(self):
        recent_past = datetime.today() - timedelta(days=settings.CURRENT_PAST_DAYS)
        near_future = datetime.today() + timedelta(days=settings.CURRENT_FUTURE_DAYS)
        return self.filter(
            (
                models.Q(poll_open_date__gte=recent_past)
                & models.Q(poll_open_date__lte=near_future)
            )
            | models.Q(current=True)
        ).exclude(current=False)

    def future(self):
        return self.filter(poll_open_date__gte=datetime.today())

    def filter_by_status(self, status):
        if isinstance(status, list):
            query = models.Q(moderationhistory__status__short_label__in=status)
        elif isinstance(status, str):
            query = models.Q(moderationhistory__status__short_label=status)
        else:
            raise TypeError("Expected list or str found {}".format(type(status)))

        return (
            self.annotate(latest_status=models.Max("moderationhistory__modified"))
            .filter(
                query & models.Q(moderationhistory__modified=models.F("latest_status"))
            )
            .order_by("election_id")
        )


class PublicElectionsManager(models.Manager.from_queryset(ElectionQuerySet)):

    """
    In most cases, we want to expose elections which are approved
    and hide any which are suggested/rejected/deleted
    Instead of remembering to filter on (latest status == approved)
    in every front-end query we can use this manager.
    """

    def get_queryset(self):
        return super().get_queryset().filter_by_status("Approved")


class PrivateElectionsManager(models.Manager.from_queryset(ElectionQuerySet)):

    """
    In some contexts
    (some API outputs, moderation queue code, /admin, unit tests, etc)
    we do also need to reference suggested/rejected/deleted elections.
    In these situations we can explicitly use this manager to
    query all election objects.
    """

    use_in_migrations = True

@@ -68,16 +68,82 @@
 ettings%0A
+from django.contrib.gis.db.models.functions import PointOnSurface%0A
 from dja
@@ -204,16 +204,30 @@
 rt Point
+, GEOSGeometry
 %0Afrom el
@@ -730,16 +730,16 @@
 stcode)%0A
-
         
@@ -760,32 +760,818 @@
 r_point(point)%0A%0A
+    def ballots_with_point_in_area(self, area: GEOSGeometry):%0A        %22%22%22%0A        Returns all election objects whose 'group_type' is 'None' and where the%0A        *centroid* of either the division or organisation (where there is no%0A        DivisionGeography) is inside the area%0A        %22%22%22%0A        return (%0A            self.filter(group_type=None)%0A            .annotate(%0A                division_centroid=PointOnSurface(%22division_geography__geography%22),%0A                organisation_centroid=PointOnSurface(%0A                    %22organisation_geography__geography%22%0A                ),%0A            )%0A            .filter(%0A                models.Q(division_centroid__within=area)%0A                %7C models.Q(organisation_centroid__within=area, division_centroid=None)%0A            )%0A        )%0A%0A
     def current(

Add centroid-in-area filter to Election Manager