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

"""
from django.db import transaction

from api.utils.serializer.v1.serializer import AbstractModelAPISerializerV1, AbstractCompensationAPISerializerV1Mixin
from compensation.models import Compensation
from intervention.models import Intervention
from konova.models import Geometry
from konova.tasks import celery_update_parcels, celery_check_for_geometry_conflicts
from konova.utils.message_templates import DATA_UNSHARED
from user.models import UserActionLogEntry


class CompensationAPISerializerV1(AbstractModelAPISerializerV1, AbstractCompensationAPISerializerV1Mixin):
    model = Compensation

    def prepare_lookup(self, id, user):
        super().prepare_lookup(id, user)
        del self.lookup["users__in"]
        self.lookup["intervention__users__in"] = [user]

    def intervention_to_json(self, entry):
        return {
            "id": entry.pk,
            "identifier": entry.identifier,
            "title": entry.title,
        }

    def _extend_properties_data(self, entry):
        self.properties_data["is_cef"] = entry.is_cef
        self.properties_data["is_coherence_keeping"] = entry.is_coherence_keeping
        self.properties_data["is_pik"] = entry.is_pik
        self.properties_data["intervention"] = self.intervention_to_json(entry.intervention)
        self.properties_data["before_states"] = self._compensation_state_to_json(entry.before_states.all())
        self.properties_data["after_states"] = self._compensation_state_to_json(entry.after_states.all())
        self.properties_data["actions"] = self._compensation_actions_to_json(entry.actions.all())
        self.properties_data["deadlines"] = self._deadlines_to_json(entry.deadlines.all())

    def _initialize_objects(self, json_model, user):
        """ Initializes all needed objects from the json_model data

        Does not persist data to the DB!

        Args:
            json_model (dict): The json data
            user (User): The API user

        Returns:
            obj (Compensation)
        """
        create_action = UserActionLogEntry.get_created_action(user, comment="API Import")
        # Create geometry
        json_geom = self._create_geometry_from_json(json_model)
        geometry = Geometry()
        geometry.geom = json_geom
        geometry.created = create_action

        # Create linked objects
        obj = Compensation()
        created = create_action
        obj.created = created
        obj.modified = created
        obj.geometry = geometry
        return obj

    def set_intervention(self, obj, intervention_id, user):
        """ Sets the linked compensation according to the given id

        Fails if no such intervention found or user has no shared access

        Args:
            obj (Compensation): The Compensation object
            intervention_id (str): The intervention's id
            user (User): The API user

        Returns:
            obj (Compensation)
        """
        if obj.intervention is not None and obj.intervention.id == intervention_id:
            # Nothing to do here
            return obj

        intervention = Intervention.objects.get(
            id=intervention_id,
        )
        is_shared = intervention.is_shared_with(user)

        if not is_shared:
            raise PermissionError(DATA_UNSHARED)

        obj.intervention = intervention
        return obj

    def create_model_from_json(self, json_model, user):
        """ Creates a new entry for the model based on the contents of json_model

        Args:
            json_model (dict): The json containing data
            user (User): The API user

        Returns:
            created_id (str): The id of the newly created Compensation entry
        """
        with transaction.atomic():
            obj = self._initialize_objects(json_model, user)

            # Fill in data to objects
            properties = json_model["properties"]
            obj.identifier = obj.generate_new_identifier()
            obj.title = properties["title"]
            obj.is_cef = properties["is_cef"]
            obj.is_coherence_keeping = properties["is_coherence_keeping"]
            obj.is_pik = properties.get("is_pik", False)
            obj = self.set_intervention(obj, properties["intervention"], user)

            obj.geometry.save()
            obj.save()

            obj = self._set_compensation_actions(obj, properties["actions"])
            obj = self._set_compensation_states(obj, properties["before_states"], obj.before_states)
            obj = self._set_compensation_states(obj, properties["after_states"], obj.after_states)
            obj = self._set_deadlines(obj, properties["deadlines"])

        obj.log.add(obj.created)

        celery_update_parcels.delay(obj.geometry.id)
        celery_check_for_geometry_conflicts.delay(obj.geometry.id)

        return obj.id

    def update_model_from_json(self, id, json_model, user):
        """ Updates an entry for the model based on the contents of json_model

        Args:
            id (str): The object's id
            json_model (dict): The json containing data
            user (User): The API user

        Returns:
            created_id (str): The id of the newly created Compensation entry
        """
        with transaction.atomic():
            update_action = UserActionLogEntry.get_edited_action(user, "API update")
            obj = self._get_obj_from_db(id, user)

            # Fill in data to objects
            properties = json_model["properties"]
            obj.title = properties["title"]
            obj.is_cef = properties["is_cef"]
            obj.is_coherence_keeping = properties["is_coherence_keeping"]
            obj.is_pik = properties.get("is_pik", False)
            obj.modified = update_action
            obj.geometry.geom = self._create_geometry_from_json(json_model)
            obj.geometry.modified = update_action
            obj = self.set_intervention(obj, properties["intervention"], user)

            obj.geometry.save()
            obj.save()

            obj = self._set_compensation_actions(obj, properties["actions"])
            obj = self._set_compensation_states(obj, properties["before_states"], obj.before_states)
            obj = self._set_compensation_states(obj, properties["after_states"], obj.after_states)
            obj = self._set_deadlines(obj, properties["deadlines"])

        obj.log.add(update_action)

        celery_update_parcels.delay(obj.geometry.id)

        return obj.id
