# standard

# PyQGIS

from qgis.core import (
    QgsProcessingAlgorithm,
    QgsProcessingException,
    QgsProcessingOutputString,
    QgsProcessingParameterBoolean,
    QgsProcessingParameterMatrix,
    QgsProcessingParameterNumber,
    QgsProcessingParameterString,
)
from qgis.PyQt.QtCore import QCoreApplication

from geoplateforme.api.configuration import (
    Configuration,
    ConfigurationRequestManager,
    ConfigurationType,
)

# Plugin
from geoplateforme.api.custom_exceptions import (
    AddTagException,
    ConfigurationCreationException,
    OfferingCreationException,
    UnavailableEndpointException,
)
from geoplateforme.api.datastore import DatastoreRequestManager
from geoplateforme.api.offerings import OfferingsRequestManager
from geoplateforme.api.stored_data import StoredDataRequestManager
from geoplateforme.processing.utils import (
    get_short_string,
    get_user_manual_url,
    tags_from_qgs_parameter_matrix_string,
)
from geoplateforme.toolbelt.preferences import PlgOptionsManager

data_type = "WMTS-TMS"


class WmtsPublicationAlgorithm(QgsProcessingAlgorithm):
    DATASTORE = "DATASTORE"
    STORED_DATA = "STORED_DATA"
    NAME = "NAME"
    LAYER_NAME = "LAYER_NAME"  # Equivalent nom technique

    BOTTOM_LEVEL = "BOTTOM_LEVEL"
    TOP_LEVEL = "TOP_LEVEL"
    STYLES = "STYLES"

    FEATURE_INFO_SERVER_URL = "FEATURE_INFO_SERVER_URL"

    TITLE = "TITLE"
    ABSTRACT = "ABSTRACT"
    KEYWORDS = "KEYWORDS"

    TAGS = "TAGS"

    URL_ATTRIBUTION = "URL_ATTRIBUTION"
    URL_TITLE = "URL_TITLE"

    OPEN = "OPEN"

    # Parameter not yet implemented
    METADATA = "metadata"

    OFFERING_ID = "OFFERING_ID"

    def tr(self, message: str) -> str:
        """Get the translation for a string using Qt translation API.

        :param message: string to be translated.
        :type message: str

        :returns: Translated version of message.
        :rtype: str
        """
        return QCoreApplication.translate(self.__class__.__name__, message)

    def createInstance(self):
        return WmtsPublicationAlgorithm()

    def name(self):
        return "wmts_publish"

    def displayName(self):
        return self.tr("Publication service WMTS")

    def group(self):
        return self.tr("Publication")

    def groupId(self):
        return "publication"

    def helpUrl(self):
        return get_user_manual_url(self.name())

    def shortHelpString(self):
        return get_short_string(self.name(), self.displayName())

    def initAlgorithm(self, config=None):
        self.addParameter(
            QgsProcessingParameterString(
                name=self.DATASTORE,
                description=self.tr("Identifiant de l'entrepôt"),
            )
        )

        self.addParameter(
            QgsProcessingParameterString(
                name=self.STORED_DATA,
                description=self.tr("Identifiant de la base de données vectorielle"),
            )
        )

        self.addParameter(
            QgsProcessingParameterString(
                name=self.NAME,
                description=self.tr("Nom de la publication"),
            )
        )

        self.addParameter(
            QgsProcessingParameterString(
                name=self.LAYER_NAME,
                description=self.tr("Nom technique de la publication"),
            )
        )

        self.addParameter(
            QgsProcessingParameterNumber(
                name=self.BOTTOM_LEVEL,
                description=self.tr("Niveau bas de la pyramide"),
                minValue=1,
                maxValue=21,
            )
        )

        self.addParameter(
            QgsProcessingParameterNumber(
                name=self.TOP_LEVEL,
                description=self.tr("Niveau haut de la pyramide"),
                minValue=1,
                maxValue=21,
            )
        )

        self.addParameter(
            QgsProcessingParameterString(
                name=self.STYLES,
                description=self.tr(
                    "Identifiant des styles ROK4. Valeurs séparées par des virgules (,)"
                ),
                optional=True,
            )
        )

        self.addParameter(
            QgsProcessingParameterString(
                name=self.FEATURE_INFO_SERVER_URL,
                description=self.tr(
                    "Ressource cible du GeFeatureInfo, si non défini utilisation des valeurs de la données stockée."
                ),
                optional=True,
            )
        )

        self.addParameter(
            QgsProcessingParameterString(
                name=self.TITLE,
                description=self.tr("Titre de la publication"),
            )
        )

        self.addParameter(
            QgsProcessingParameterString(
                name=self.ABSTRACT,
                description=self.tr("Résumé de la publication"),
                optional=True,
            )
        )

        self.addParameter(
            QgsProcessingParameterString(
                name=self.KEYWORDS,
                description=self.tr("Mot clé de la publication"),
                optional=True,
            )
        )

        self.addParameter(
            QgsProcessingParameterString(
                name=self.URL_ATTRIBUTION,
                description=self.tr("Url attribution"),
            )
        )
        self.addParameter(
            QgsProcessingParameterString(
                name=self.URL_TITLE,
                description=self.tr("Titre attribution"),
            )
        )

        self.addParameter(
            QgsProcessingParameterMatrix(
                name=self.TAGS,
                description=self.tr("Tags"),
                headers=[self.tr("Tag"), self.tr("Valeur")],
            )
        )

        self.addParameter(
            QgsProcessingParameterBoolean(
                name=self.OPEN,
                description=self.tr("Ouvert à tout public"),
                optional=True,
                defaultValue=True,
            )
        )

        self.addOutput(
            QgsProcessingOutputString(
                name=self.OFFERING_ID,
                description=self.tr("Identifiant de l'offre créée."),
            )
        )

    def processAlgorithm(self, parameters, context, feedback):
        datastore_id = self.parameterAsString(parameters, self.DATASTORE, context)
        stored_data_id = self.parameterAsString(parameters, self.STORED_DATA, context)
        name = self.parameterAsString(parameters, self.NAME, context)
        layer_name = self.parameterAsString(parameters, self.LAYER_NAME, context)
        title = self.parameterAsString(parameters, self.TITLE, context)

        url = self.parameterAsString(parameters, self.URL_ATTRIBUTION, context)
        url_title = self.parameterAsString(parameters, self.URL_TITLE, context)
        abstract = self.parameterAsString(parameters, self.ABSTRACT, context)

        tag_data = self.parameterAsMatrix(parameters, self.TAGS, context)
        tags = tags_from_qgs_parameter_matrix_string(tag_data)

        top_level = self.parameterAsInt(parameters, self.TOP_LEVEL, context)
        bottom_level = self.parameterAsInt(parameters, self.BOTTOM_LEVEL, context)

        style_str = self.parameterAsString(parameters, self.STYLES, context)
        style_ids = []
        if style_str:
            style_ids = style_str.split(",")

        is_open = self.parameterAsBool(parameters, self.OPEN, context)

        sandbox_datastore_ids = (
            PlgOptionsManager.get_plg_settings().sandbox_datastore_ids
        )
        if datastore_id in sandbox_datastore_ids and not layer_name.startswith(
            "SANDBOX"
        ):
            layer_name = f"SANDBOX_{layer_name}"
            feedback.pushInfo(
                self.tr(
                    "L'entrepot utilisé est un bac à sable et le prefix SANDBOX est obligatoire pour le nom de la couche. Nouveau nom du couche : {}"
                ).format(layer_name)
            )

        type_infos: dict = {
            "used_data": [
                {
                    "stored_data": stored_data_id,
                    "top_level": top_level,
                    "bottom_level": bottom_level,
                }
            ]
        }

        if len(style_ids) != 0:
            type_infos["styles"] = style_ids

        server_url = self.parameterAsString(
            parameters, self.FEATURE_INFO_SERVER_URL, context
        )
        if server_url:
            type_infos["getfeatureinfo"] = {
                "server_url": self.parameterAsString(
                    parameters, self.FEATURE_INFO_SERVER_URL, context
                )
            }
        else:
            type_infos["getfeatureinfo"] = {"stored_data": True}

        # TODO : add metadata
        metadata = []

        # create (post) configuration from input data
        try:
            manager_configuration = ConfigurationRequestManager()

            configuration = Configuration(
                _id="",
                datastore_id=datastore_id,
                _type=ConfigurationType.WMTS_TMS,
                _metadata=metadata,
                _name=name,
                _layer_name=layer_name,
                _type_infos=type_infos,
                _attribution={},
                is_detailed=True,
            )
            configuration.title = title
            configuration.abstract = abstract
            configuration.url_title = url_title
            configuration.url = url

            # response = configuration
            res = manager_configuration.create_configuration(
                datastore=datastore_id,
                configuration=configuration,
            )
            configuration_id = res

        except ConfigurationCreationException as exc:
            raise QgsProcessingException(f"exc configuration id : {exc}")

        # get the endpoint for the publication
        try:
            datastore_manager = DatastoreRequestManager()
            datastore = datastore_manager.get_datastore(datastore_id)
            res = datastore.get_endpoint(data_type=data_type, open=is_open)

            publication_endpoint = res
        except UnavailableEndpointException as exc:
            raise QgsProcessingException(f"exc endpoint : {exc}")

        # create publication (offering)
        try:
            manager_offering = OfferingsRequestManager()
            offering = manager_offering.create_offering(
                endpoint=publication_endpoint,
                datastore=datastore_id,
                configuration_id=configuration_id,
                is_open=is_open,
            )
        except OfferingCreationException as exc:
            raise QgsProcessingException(f"exc publication url : {exc}")

        try:
            # Update configuration tags
            manager_configuration = ConfigurationRequestManager()
            manager_configuration.add_tags(
                datastore_id=datastore_id,
                configuration_id=configuration_id,
                tags=tags,
            )
        except AddTagException as exc:
            raise QgsProcessingException(f"exc tag update url : {exc}")

        try:
            # Update stored data tags
            manager = StoredDataRequestManager()
            manager.add_tags(
                datastore_id=datastore_id,
                stored_data_id=stored_data_id,
                tags={"published": "true"},
            )
        except AddTagException as exc:
            raise QgsProcessingException(f"exc tag update url : {exc}")

        return {
            self.OFFERING_ID: offering._id,
        }
