"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 04.01.22

"""
import datetime

from django.contrib.gis.db.models.functions import Area

from konova.management.commands.setup import BaseKonovaCommand
from konova.models import Geometry, ParcelIntersection


class Command(BaseKonovaCommand):
    help = "Recalculates parcels for entries with geometry but missing parcel information"

    def add_arguments(self, parser):
        parser.add_argument(
            "--force-all",
            action="store_true",
            default=False,
            help="If Attribute set, all entries parcels will be recalculated"
        )

    def handle(self, *args, **options):
        try:
            self.recalculate_parcels(options)
        except KeyboardInterrupt:
            self._break_line()
            exit(-1)

    def recalculate_parcels(self, options: dict):
        force_all = options.get("force_all", False)

        geometry_objects = Geometry.objects.filter(
            geom__isempty=False,
        ).exclude(
            geom=None
        )

        if not force_all:
            # Fetch all intersections
            intersection_objs = ParcelIntersection.objects.filter(
                geometry__in=geometry_objects
            )
            # Just take the geometry ids, which seem to have intersections
            geom_with_intersection_ids = intersection_objs.values_list(
                "geometry__id",
                flat=True
            )
            # ... and resolve into Geometry objects again ...
            intersected_geom_objs = Geometry.objects.filter(
                id__in=geom_with_intersection_ids
            )
            # ... to be able to use the way more efficient difference() function ...
            geometry_objects_ids = geometry_objects.difference(intersected_geom_objs).values_list("id", flat=True)
            # ... so we can resolve these into proper Geometry objects again for further annotation usage
            geometry_objects = Geometry.objects.filter(id__in=geometry_objects_ids)

        self._write_warning("=== Update parcels and districts ===")
        # Order geometries by size to process smaller once at first
        geometries = geometry_objects.annotate(
            area=Area("geom")
        ).order_by(
            'area'
        )
        self._write_warning(f"Process parcels for {geometries.count()} geometry entries now ...")
        i = 0
        num_geoms = geometries.count()
        geoms_with_errors = {}
        for geometry in geometries:
            self._write_warning(f"--- {datetime.datetime.now()} Process {geometry.id} now ...")
            try:
                geometry.update_parcels()
                self._write_warning(f"--- Processed {geometry.get_underlying_parcels().count()} underlying parcels")
            except Exception as e:
                geoms_with_errors[geometry.id] = str(e)
            i += 1
            self._write_warning(f"--- {i}/{num_geoms} processed")

        self._write_success("Updating parcels done!")

        for key, val in geoms_with_errors.items():
            self._write_error(f"    Error on {key}: {val}")
        self._write_success(f"{num_geoms - len(geoms_with_errors)} geometries successfuly recalculated!")
        self._break_line()
