"""
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, \
    ResponsibilityAPISerializerV1Mixin
from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID, CODELIST_HANDLER_ID
from ema.models import Ema
from intervention.models import Responsibility, Handler
from konova.models import Geometry
from konova.tasks import celery_update_parcels, celery_check_for_geometry_conflicts
from user.models import UserActionLogEntry


class EmaAPISerializerV1(AbstractModelAPISerializerV1, AbstractCompensationAPISerializerV1Mixin, ResponsibilityAPISerializerV1Mixin):
    model = Ema

    def _extend_properties_data(self, entry):
        self.properties_data["is_pik"] = entry.is_pik
        self.properties_data["responsible"] = self._responsible_to_json(entry.responsible)
        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 _responsible_to_json(self, responsible: Responsibility):
        return {
            "conservation_office": self._konova_code_to_json(responsible.conservation_office),
            "conservation_file_number": responsible.conservation_file_number,
            "handler": self._handler_to_json(responsible.handler),
        }

    def _set_responsibility(self, obj, responsibility_data: dict):
        """ Sets the responsible data contents to the provided responsibility_data dict

        Args:
            obj (Intervention): The intervention object
            responsibility_data (dict): The new data

        Returns:
            obj
        """
        if responsibility_data is None:
            return obj
        obj.responsible.conservation_office = self._konova_code_from_json(
            responsibility_data["conservation_office"],
            CODELIST_CONSERVATION_OFFICE_ID,
        )
        obj.responsible.conservation_file_number = responsibility_data["conservation_file_number"]
        obj.responsible.handler.type = self._konova_code_from_json(
            responsibility_data["handler"]["type"],
            CODELIST_HANDLER_ID,
        )
        obj.responsible.handler.detail = responsibility_data["handler"]["detail"]
        return obj

    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 = Ema()
        obj.responsible = Responsibility(
            handler=Handler()
        )
        created = create_action
        obj.created = created
        obj.modified = created
        obj.geometry = geometry
        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 Ema 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_pik = properties.get("is_pik", False)
            obj = self._set_responsibility(obj, properties["responsible"])

            obj.geometry.save()
            obj.responsible.handler.save()
            obj.responsible.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)
        obj.users.add(user)

        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 Ema 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_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_responsibility(obj, properties["responsible"])

            obj.geometry.save()
            obj.responsible.handler.save()
            obj.responsible.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