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

"""
import base64
import json

import pika
import xmltodict
from django.db.models import Sum
from django.utils import formats

from intervention.settings import EGON_RABBITMQ_HOST, EGON_RABBITMQ_USER, EGON_RABBITMQ_PW, EGON_RABBITMQ_PORT
from konova.sub_settings.django_settings import DEFAULT_DATE_FORMAT

from konova.sub_settings.lanis_settings import DEFAULT_SRID_RLP


class EgonExporter:
    """
    EGON is the payment management system of SNU RLP. Due to compatibility reasons we need to provide the old style
    of data transmission between KSP and EGON:
        1. Create GML from intervention object
        2. Send created GML to the appropriate RabbitMQ channel
    """
    intervention = None
    gml_builder = None

    def __init__(self, intervention):
        self.intervention = intervention
        self.gml_builder = EgonGmlBuilder(intervention)

    def export_to_rabbitmq(self):
        """ Sends the exporter gml to message broker rabbitmq to be fetched by EGON application from there

        Returns:

        """
        msg = {
            "nachricht": self.gml_builder.gml,
        }
        msg = json.dumps(msg)
        print(msg)
        credentials = pika.PlainCredentials(EGON_RABBITMQ_USER, EGON_RABBITMQ_PW)
        params = pika.ConnectionParameters(
            EGON_RABBITMQ_HOST,
            EGON_RABBITMQ_PORT,
            "/",
            credentials
        )
        conn = pika.BlockingConnection(params)
        channel = conn.channel()
        channel.basic_publish(
            exchange="",
            routing_key="KSP_EGON",
            body=msg.encode("utf-8"),
        )
        conn.close()


class EgonGmlBuilder:
    """
    Creates the GML for EGON export
    """
    intervention = None
    gml = None
    _PAYMENT_FALLBACK_DATE = ""

    def __init__(self, intervention):
        self.intervention = intervention
        self.gml = self.build_gml()

    def _gen_flurstuecksKennzeichen(self, parcel):
        """ Generates oneo:flurstuecksKennzeichen to provide backwards compatibility

        Args:
            parcel (Parcel): The requested parcel

        Returns:
            str
        """
        gmrkng_code = "{0:06d}".format(int(parcel.parcel_group.key) or 0)
        flr_code = "{0:03d}".format(int(parcel.flr or 0))
        flrstckzhlr_code = "{0:05d}".format(int(parcel.flrstck_zhlr or 0))
        flrstcknnr_code = "{0:06d}".format(int(parcel.flrstck_nnr or 0))
        return gmrkng_code + flr_code + flrstckzhlr_code + flrstcknnr_code

    def _sum_all_payments(self):
        all_payments = self.intervention.payments.aggregate(
            summed=Sum("amount")
        )["summed"]
        return all_payments

    def _float_to_localized_string(self, value: float):
        return formats.number_format(value, use_l10n=True, decimal_pos=2)

    def _gen_kompensationsArt(self) -> (str, int):
        comp_type = "Ersatzzahlung"
        comp_type_code = 774898901
        if self.intervention.compensations.exists():
            comp_type += " und Kompensation"
            comp_type_code = 771655351
        return comp_type, comp_type_code

    def _gen_geometry_list(self):
        geom = self.intervention.geometry.geom
        geom.transform(DEFAULT_SRID_RLP)
        geoms_list = [
            {
                "gml:Polygon": {
                    "gml:exterior": {
                        "gml:LinearRing": {
                            "gml:posList": " ".join([f"{str(coord[0])} {str(coord[1])}" for coord in coords[0]])
                        }
                    }
                }
            } for coords in geom.coords
        ]
        return geoms_list

    def _gen_raumreferenz(self):
        parcels = self.intervention.get_underlying_parcels()
        spatial_reference_list = [
            {
                "oneo:datumAbgleich": None,
                "oneo:ortsangabe": {
                    "oneo:Ortsangaben": {
                        "oneo:kreisSchluessel": {
                            "xlink:href": f"http://register.naturschutz.rlp.de/repository/services/referenzliste/588/{parcel.district.key}",
                        },
                        "oneo:gemeindeSchluessel": {
                            "xlink:href": f"http://register.naturschutz.rlp.de/repository/services/referenzliste/910/{parcel.municipal.key}",
                        },
                        "oneo:verbandsgemeindeSchluessel": {
                            "xlink:href": f"http://register.naturschutz.rlp.de/repository/services/referenzliste/589/{None}",
                        },
                        "oneo:flurstuecksKennzeichen": self._gen_flurstuecksKennzeichen(parcel),
                    }
                },
            } for parcel in parcels
        ]
        return spatial_reference_list

    def _gen_foto(self):
        revoc_docs, regular_docs = self.intervention.get_documents()
        docs_list = {
            "oneo:Foto": [
                {
                    "oneo:aufnahmezeitpunkt": doc.date_of_creation.strftime(DEFAULT_DATE_FORMAT),
                    "oneo:bemerkung": doc.comment,
                    "oneo:fotoverweis": base64.b64encode(doc.file.read()).decode("utf-8"),
                    "oneo:dateiname": doc.file.url.split("/")[-1],
                    "oneo:hauptfoto": False,
                } for doc in regular_docs
            ]
        }
        return docs_list

    def build_gml(self):
        comp_type, comp_type_code = self._gen_kompensationsArt()
        payment = self.intervention.payments.first()
        payment_date = None
        if payment is not None:
            payment_date = payment.due_on
            if payment_date is not None:
                payment_date = payment_date.strftime(DEFAULT_DATE_FORMAT)
            else:
                payment_date = self._PAYMENT_FALLBACK_DATE

        cons_office = self.intervention.responsible.conservation_office
        reg_office = self.intervention.responsible.registration_office
        law = self.intervention.legal.laws.first()
        process_type = self.intervention.legal.process_type
        handler = self.intervention.responsible.handler
        reg_date = self.intervention.legal.registration_date
        bind_date = self.intervention.legal.binding_date

        # EGON needs special treatment in case of missing data
        if reg_office is None:
            reg_office_element = {}
        else:
            reg_office_element = {
                "@xlink:href": f"http://register.naturschutz.rlp.de/repository/services/referenzliste/1053/{reg_office.atom_id}",
                "#text": reg_office.long_name
            }

        if cons_office is None:
            cons_office_element = {}
        else:
            cons_office_element = {
                "@xlink:href": f"http://register.naturschutz.rlp.de/repository/services/referenzliste/907/{cons_office.atom_id}",
                "#text": cons_office.long_name
            }

        xml_dict = {
            "wfs:FeatureCollection": {
                "@xmlns:wfs": "http://www.opengis.net/wfs",
                "@xmlns:xlink": "http://www.w3.org/1999/xlink",
                "@xmlns:oneo": "http://www.osiris-projekt.rlp.de/oneo",
                "@xmlns:gmlexr": "http://www.opengis.net/gml/3.3/exr",
                "@xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
                "@xmlns:gml": "http://www.opengis.net/gml/3.2",
                "oneo:Eingriffsverfahren": {
                    "@gml:id": self.intervention.identifier,
                    "oneo:azEintragungsstelle": self.intervention.responsible.conservation_file_number,
                    "oneo:azZulassungsstelle": self.intervention.responsible.registration_file_number,
                    "oneo:bemerkungZulassungsstelle": "",
                    "oneo:eintragungsstelle": cons_office_element,
                    "oneo:zulassungsstelle": reg_office_element,
                    "oneo:ersatzzahlung": self._float_to_localized_string(self._sum_all_payments()),
                    "oneo:kompensationsart": {
                        "@xlink:href": f"http://register.naturschutz.rlp.de/repository/services/referenzliste/88140/{comp_type_code}",
                        "#text": comp_type
                    },
                    "oneo:verfahrensrecht": {
                        "@xlink:href": f"http://register.naturschutz.rlp.de/repository/services/referenzliste/1048/{law.atom_id if law else ''}",
                        "#text": law.short_name if law else ""
                    },
                    "oneo:verfahrenstyp": {
                        "@xlink:href": f"http://register.naturschutz.rlp.de/repository/services/referenzliste/44382/{process_type.atom_id if process_type else ''}",
                        "#text": process_type.long_name if process_type else "",
                    },
                    "oneo:eingreifer": {
                        "oneo:Eingreifer": {
                            "oneo:art": {
                                "@xlink:href": f"http://register.naturschutz.rlp.de/repository/services/referenzliste/1053/{handler.type.atom_id if handler.type else ''}",
                                "#text": handler.type.long_name if handler.type else "",
                            },
                            "oneo:bemerkung": handler.detail if handler else "",
                        }
                    },
                    "oneo:erfasser": {
                        "oneo:Erfasser": {
                            "oneo:name": "",
                            "oneo:bemerkung": "",
                        }
                    },
                    "oneo:zulassung": {
                        "oneo:Zulassungstermin": {
                            "oneo:bauBeginn": payment_date,
                            "oneo:erlass": reg_date.strftime(DEFAULT_DATE_FORMAT) if reg_date else "",
                            "oneo:rechtsKraft": bind_date.strftime(DEFAULT_DATE_FORMAT) if bind_date else "",
                        }
                    },
                    "oneo:geometrie": {
                        "gml:multiSurfaceProperty": {
                            "gml:MultiPolygon": {
                                "@srsName": f"http://www.opengis.net/gml/srs/epsg.xml#{DEFAULT_SRID_RLP}",
                                "gml:polygonMember": self._gen_geometry_list(),
                            }
                        },
                    },
                    "oneo:kennung": self.intervention.identifier,
                    "oneo:bezeichnung": self.intervention.title,
                    "oneo:bemerkung": self.intervention.comment,
                    "oneo:verantwortlicheStelle": "",
                    "oneo:veroffentlichtAm": "",
                    "oneo:raumreferenz": {
                        "oneo:Raumreferenz": self._gen_raumreferenz(),
                    },
                    "oneo:foto": self._gen_foto(),
                }
            },
        }
        gml = xmltodict.unparse(xml_dict)
        return gml
