# SPDX-FileCopyrightText: 2023 Karlsruher Institut für Technologie
#
# SPDX-License-Identifier: CC0-1.0
import sys
import traceback
from datetime import datetime
from typing import Tuple, Union
from urllib.parse import quote_plus
from urllib.request import urlopen

import pytz
import requests
from dateutil.parser import parse
from lxml import etree

from . import logger, utils
from .analysers.recognizer import Recognizer
from .dimensions.spatial import Spatial
from .dimensions.temporal import Temporal
from .statics import constants
from .webservices import core


class CollectionHarvester(object):
    """
    This class harvests data pertaining to Collections
    from TDS catalogs. Depending on the sort of dataset
    scenario, it returns one of the five variables below.
    `collection_id` , `collection_title` , `collection_description`
    , `collection_url` , and `collection_subdirs`.

    Args:
        url (str): TDS catalog URL address
        recognizer (str): `status` senario number of :class:`~tds2stac.Recognizer`
        subdirs (list): subdirs is a list of url, id, title, and subdirs of a nested dataset
        collection_tuples (list):  a tuple of STAC collection's auto-generated ID, user-ID, user-Title and user-Description defined by user.
        auth (tuple): Authentication tuple for TDS catalog e.g.('user', 'password') (optional)
        logger_properties (dict): dictionary of logger properties


    """

    url: str
    """
        TDS catalog URL address. Initial point of harvesting e.g.
        https://thredds.atmohub.kit.edu/thredds/catalog/caribic/IAGOS-CARIBIC_MS_files_collection_20231017/catalog.html (*)
    """
    recognizer: Union[str, None]
    """
        `status` senario output of Recognizer class
    """
    subdirs: Union[list, None]
    """
        subdirs is a list of url, id, title, and subdirs of a nested dataset
    """
    collection_tuples: Union[list[tuple], None]
    """
        a tuple of STAC collection's auto-generated ID, user-ID, user-Title and user-Description defined by user.
    """
    auth: Union[Tuple[str, str], None]
    """
        Authentication tuple for TDS catalog e.g.('user', 'password') (optional)
    """
    logger_properties: dict
    """
        dictionary of logger properties, more information in :class:`~tds2stac.logger.Logger`
    """

    def __init__(
        self,
        url: str,
        recognizer: Union[str, None],
        subdirs: Union[list, None] = [],
        collection_tuples: Union[list[tuple], None] = None,
        auth: Union[Tuple[str, str], None] = None,
        logger_properties: dict = dict(),
    ):
        ###############################################
        # Defining output and input variables as an empty parameter
        ###############################################
        self.auth = auth
        self.collection_tuples = collection_tuples
        self.Recognizer_output = recognizer
        self.collection_id: Union[str, None] = None
        self.collection_title: Union[str, None] = None
        self.colleciton_description: Union[str, None] = None
        self.collection_url: Union[str, None] = None
        self.collection_subdirs: Union[list, None] = []
        if logger_properties is not None:
            self.logger_properties = logger_properties

        ###############################################
        # Getting the Collection ID and Descriptionin
        # in none nested scenarios
        ###############################################
        if any(
            scenario in str(self.Recognizer_output)
            for scenario in constants.none_nested_scenarios
        ):
            (
                self.collection_id,
                self.collection_title,
                self.collection_description,
            ) = self.collection_id_desc_maker(
                url, self.collection_tuples, self.Recognizer_output
            )
            self.collection_url = url
        ###############################################
        # Getting the Collection ID and Descriptionin
        # in nested scenarios
        ###############################################
        if any(
            scenario in str(self.Recognizer_output)
            for scenario in constants.nested_scenarios
        ):
            #####################################
            # Collection cases with subdirs
            #####################################
            if subdirs is not None:
                if subdirs[3] != []:
                    self.collection_url = subdirs[0]
                    self.collection_id = subdirs[1]
                    self.collection_title = subdirs[2]
                    self.collection_subdirs = subdirs[3]
                    self.collection_description = (
                        "[Link to TDS](" + url.replace("xml", "html") + ")"
                    )
                    if self.collection_tuples is not None:
                        for i in self.collection_tuples:
                            if i[0] == self.collection_id:
                                if i[1] != "" and not i[1].isspace():
                                    self.collection_id = i[1]
                                else:
                                    self.collection_id = self.collection_id
                                if i[2] != "" and not i[2].isspace():
                                    self.collection_title = i[2]
                                else:
                                    self.collection_title = (
                                        self.collection_title
                                    )
                                if i[3] != "" and not i[3].isspace():
                                    self.collection_description = (
                                        i[3]
                                        .strip()
                                        .replace("\n", "\n\n")
                                        .replace('"', "")
                                        + "\n\n [Link to TDS]("
                                        + url.replace("xml", "html")
                                        + ")"
                                    )
                #####################################
                # Collection cases without subdirs
                #####################################
                else:
                    xml_url_catalog, id_catalog, xml = utils.xml_processing(
                        subdirs[0], self.auth
                    )
                    recognizer_output_temp = Recognizer(subdirs[0])
                    self.collection_url = subdirs[0]
                    (
                        self.collection_id,
                        self.collection_title,
                        self.collection_description,
                    ) = self.collection_id_desc_maker(
                        xml_url_catalog,
                        self.collection_tuples,
                        recognizer_output_temp.status,
                    )

    def collection_id_desc_maker(
        self,
        url: str,
        collection_tuples: Union[list[tuple], None] = None,
        recognizer_output: Union[str, None] = None,
    ):
        """
        A function for getting collection id and description
        from the TDS catalog urls and pre-defined `collection_tuples`
        for scenarios number 4, 5, 6, 7 ,and 9

        Args:
            url (str): TDS catalog URL address
            collection_tuples (list):  a tuple of STAC collection's auto-generated ID, user-ID, user-Title and user-Description defined by user.
            recognizer_output (str): `status` senario output of Recognizer class
        """

        if "Fourth Scenario" in str(recognizer_output):
            collection_id = utils.replacement_func_collection_item_id(
                url + "-empty".replace("html", "xml")
            )
            collection_title = utils.replacement_func(
                url + " Empty".replace("html", "xml")
            )
            collection_description = (
                "[Link to TDS](" + url.replace("xml", "html") + ")"
            )
            if collection_tuples is not None:
                for i in collection_tuples:
                    if i[0] == collection_id:
                        if i[1] != "" and not i[1].isspace():
                            collection_id = i[1]
                        else:
                            collection_id = collection_id
                        if i[2] != "" and not i[2].isspace():
                            collection_title = i[2]
                        else:
                            collection_title = collection_title
                        if i[3] != "" and not i[3].isspace():
                            collection_description = (
                                i[3]
                                .strip()
                                .replace("\n", "\n\n")
                                .replace('"', "")
                                + "\n\n [Link to TDS]("
                                + url.replace("xml", "html")
                                + ")"
                            )
            return collection_id, collection_title, collection_description
        fifth_and_ninth_scenarios = ["Fifth Scenario", "Ninth Scenario"]
        if any(x in str(recognizer_output) for x in fifth_and_ninth_scenarios):
            collection_id = utils.replacement_func_collection_item_id(
                url + "-collection".replace("html", "xml")
            )
            collection_title = utils.replacement_func(
                url + " Collection".replace("html", "xml")
            )
            collection_description = (
                "[Link to TDS](" + url.replace("xml", "html") + ")"
            )
            if collection_tuples is not None:
                for i in collection_tuples:
                    if i[0] == collection_id:
                        if i[1] != "" and not i[1].isspace():
                            collection_id = i[1]
                        else:
                            collection_id = collection_id
                        if i[2] != "" and not i[2].isspace():
                            collection_title = i[2]
                        else:
                            collection_title = collection_title
                        if i[3] != "" and not i[3].isspace():
                            collection_description = (
                                i[3]
                                .strip()
                                .replace("\n", "\n\n")
                                .replace('"', "")
                                + "\n\n [Link to TDS]("
                                + url.replace("xml", "html")
                                + ")"
                            )
            return collection_id, collection_title, collection_description

        if "Sixth Scenario" in str(recognizer_output):
            collection_id = utils.replacement_func_collection_item_id(
                url + "-single".replace("html", "xml")
            )
            collection_title = utils.replacement_func(
                url + " Single".replace("html", "xml")
            )
            collection_description = (
                "[Link to TDS](" + url.replace("xml", "html") + ")"
            )
            if collection_tuples is not None:
                for i in collection_tuples:
                    if i[0] == collection_id:
                        if i[1] != "" and not i[1].isspace():
                            collection_id = i[1]
                        else:
                            collection_id = collection_id
                        if i[2] != "" and not i[2].isspace():
                            collection_title = i[2]
                        else:
                            collection_title = collection_title
                        if i[3] != "" and not i[3].isspace():
                            collection_description = (
                                i[3]
                                .strip()
                                .replace("\n", "\n\n")
                                .replace('"', "")
                                + "\n\n [Link to TDS]("
                                + url.replace("xml", "html")
                                + ")"
                            )

            return collection_id, collection_title, collection_description

        if "Seventh Scenario" in str(recognizer_output):
            collection_id = utils.replacement_func_collection_item_id(
                url + "-aggregated".replace("html", "xml")
            )
            collection_title = utils.replacement_func(
                url + " Aggregated".replace("html", "xml")
            )
            collection_description = (
                "[Link to TDS](" + url.replace("xml", "html") + ")"
            )
            if collection_tuples is not None:
                for i in collection_tuples:
                    if i[0] == collection_id:
                        if i[1] != "" and not i[1].isspace():
                            collection_id = i[1]
                        else:
                            collection_id = collection_id
                        if i[2] != "" and not i[2].isspace():
                            collection_title = i[2]
                        else:
                            collection_title = collection_title
                        if i[3] != "" and not i[3].isspace():
                            collection_description = (
                                i[3]
                                .strip()
                                .replace("\n", "\n\n")
                                .replace('"', "")
                                + "\n\n [Link to TDS]("
                                + url.replace("xml", "html")
                                + ")"
                            )

            return collection_id, collection_title, collection_description

    def __iter__(self):
        yield "collection_id", self.collection_id
        yield "collection_title", self.collection_title
        yield "collection_description", self.collection_description
        yield "collection_url", self.collection_url
        yield "collection_subdirs", self.collection_subdirs


class ItemHarvester(object):
    """
    This class harvests information about an Item
    from TDS data catalogs. It ultimately returns
    a dictionary of harvesting variables, based on
    the type of dataset scenario and activated
    extensions.

    Args:
        url (str): TDS catalog URL address
        elem (str): xml element of the data in dataset
        harvesting_vars (dict): dictionary of harvesting variables that is going to be filled
        web_service_dict (dict): web service that the user wants to harvest from
        datetime_after (str): datetime that the user wants to harvest data after that
        datetime_before (str): datetime that the user wants to harvest data before that
        spatial_information (list): Spatial information of 2D datasets e.g. [minx, maxx, miny, maxy] or 1D dataset e.g. [x,y]
        temporal_format_by_dataname (str): datetime format for datasets that have datetime in their name e.g `e%y%m%d%H.%M%S%f`(optional),
        extension_properties (dict): dictionary of extension properties (optional)
        linestring (bool): using this attribute, user activate making LineString instead of Polygon (True and False)  (optional)
        logger_properties (dict): dictionary of logger properties
    """

    usl: str
    """
        TDS catalog URL address. Initial point of harvesting e.g.
        https://thredds.atmohub.kit.edu/thredds/catalog/caribic/IAGOS-CARIBIC_MS_files_collection_20231017/catalog.html (*)
    """
    elem: etree.Element
    """
        xml element of the data in dataset. It's an element of the
        xml file that is going to be harvested
    """
    harvesting_vars: dict
    """
        dictionary of harvesting variables that is going to be filled
    """
    web_service_dict: Union[dict, None]
    """
        web service that the user wants to harvest from
    """
    datetime_after: Union[str, None]
    """
        datetime that the user wants to harvest data after that
    """
    datetime_before: Union[str, None]
    """
        datetime that the user wants to harvest data before that
    """
    spatial_information: Union[list, None]
    """
        Spatial information of 2D datasets e.g. [minx, maxx, miny, maxy] or 1D dataset e.g. [x,y] (optional)
    """
    temporal_format_by_dataname: Union[str, None]
    """
        datetime format for datasets that have datetime in their name e.g `e%y%m%d%H.%M%S%f`(optional)
    """
    extension_properties: Union[dict, None]
    """
        dictionary of extension properties (optional)
    """
    linestring: bool
    """
        using this attribute, user activate making LineString instead of Polygon (True and False)  (optional)
    """
    logger_properties: dict
    """
        dictionary of logger properties, more information in :class:`~tds2stac.logger.Logger`
    """

    def __init__(
        self,
        url: str,
        elem: etree.Element,
        harvesting_vars: dict,
        web_service_dict: Union[dict, None],
        datetime_after: Union[datetime, None] = None,
        datetime_before: Union[datetime, None] = None,
        spatial_information: Union[list, None] = None,
        temporal_format_by_dataname: Union[str, None] = None,
        extension_properties: Union[dict, None] = None,
        linestring: bool = False,
        logger_properties: dict = dict(),
    ):
        # Defining variables in TDS catalog and webservices as an empty parameter
        # Getting the item_id
        self.harvesting_vars = harvesting_vars
        self.harvesting_vars["item_id"] = utils.replacement_func(
            elem.get("ID")
        )
        if logger_properties is not None:
            self.logger_properties = logger_properties

        #################################
        # Opening the xml file
        #################################
        if "?dataset=" in url:
            data_get = requests.get(
                str(url),
                None,
                verify=False,
            )
        else:
            data_get = requests.get(
                str(url) + "?dataset=" + str(elem.get("ID")),
                None,
                verify=False,
            )
        #################################
        # Getting the tree of the xml file
        #################################
        try:
            tree_data = etree.XML(data_get.text.encode("utf-8"))
        except etree.XMLSyntaxError:
            ex_type, ex_value, ex_traceback = sys.exc_info()
            if ex_type is not None and ex_value is not None:
                self.logger_properties[
                    "logger_msg"
                ] = "`datetime_filter` warning: %s : %s" % (
                    ex_type.__name__,
                    ex_value,
                )
                self.logger_properties["logger_level"] = "WARNING"
            logger.Logger(self.logger_properties)
            return
        else:
            try:
                ###############################################
                # It serves as a function to skip
                # data based on `datetime_before` and `datetime_after`
                ###############################################
                extracted_date = elem.find(
                    './/{%s}date[@type="modified"]' % constants.unidata
                )
                if extracted_date is not None:
                    try:
                        extracted_date = extracted_date.text
                        dt = parse(extracted_date)
                        self.harvesting_vars[
                            "modified_date_time"
                        ] = dt.replace(tzinfo=pytz.utc)
                    except ValueError:
                        ex_type, ex_value, ex_traceback = sys.exc_info()
                        if ex_type is not None and ex_value is not None:
                            self.logger_properties[
                                "logger_msg"
                            ] = "`datetime_filter` error: %s : %s" % (
                                ex_type.__name__,
                                ex_value,
                            )
                            self.logger_properties["logger_level"] = "ERROR"
                            logger.Logger(self.logger_properties)
                        return
                    else:
                        dt = dt.replace(tzinfo=pytz.utc)
                        if datetime_after and dt < datetime_after:
                            return
                        if datetime_before and dt > datetime_before:
                            return
                # bleedfixing datatime in STAC-Colleciton and STAC-Items for aggregated datasets. Should be fixed in the future
                else:
                    self.harvesting_vars["modified_date_time"] = parse(
                        datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%fz")
                    ).replace(tzinfo=pytz.utc)
                    self.logger_properties[
                        "logger_msg"
                    ] = "The dataset does not have a modified datetime attribute so it is set to the current datetime"
                    self.logger_properties["logger_level"] = "INFO"
                    logger.Logger(self.logger_properties)
                ###############################################
                # Getting `dataset`, `catalog_url` and `catalog_id` of the data
                ###############################################
                self.harvesting_vars["dataset"] = tree_data.find(
                    "{%s}dataset" % constants.unidata
                )
                self.harvesting_vars["catalog_url"] = url.split("?")[0]
                self.harvesting_vars["main_dataset_url"] = url
                harvesting_vars["catalog_id"] = self.harvesting_vars[
                    "dataset"
                ].get("ID")
                metadata = self.harvesting_vars["dataset"].find(
                    "{%s}metadata" % constants.unidata
                )
                ########################################
                # Finding sevice tag in data tree element
                ########################################
                service_tag = self.harvesting_vars["dataset"].find(
                    "{%s}serviceName" % constants.unidata
                )
                if service_tag is None and metadata is not None:
                    service_tag = metadata.find(
                        "{%s}serviceName" % constants.unidata
                    )
                if service_tag is None:
                    # Use services found in the file depends on the version of TDS 4 or 5
                    try:
                        self.harvesting_vars["services"] = tree_data.findall(
                            ".//{%s}service[@serviceType='Compound']"
                            % constants.unidata
                        )
                    except Exception:
                        self.harvesting_vars["services"] = tree_data.findall(
                            ".//{%s}service[@serviceType='compound']"
                            % constants.unidata
                        )
                else:
                    # Use specific named services
                    self.harvesting_vars["services"] = tree_data.findall(
                        ".//{%s}service[@name='%s']"
                        % (constants.unidata, service_tag.text)
                    )
                #############################################
                # Loop over all existing services in the data
                #############################################
                if "?dataset=" in url:
                    harvesting_vars["service_url_html"] = utils.xml2html(
                        str(url)
                    )
                else:
                    harvesting_vars["service_url_html"] = utils.xml2html(
                        str(url) + "?dataset=" + str(elem.get("ID"))
                    )
                if web_service_dict is not None:
                    if (
                        web_service_dict.get("web_service_config_file")
                        == "default"
                        or web_service_dict.get("web_service_config_file")
                        is None
                    ):
                        json_file = constants.default_tag_config_file
                    else:
                        json_file = web_service_dict["web_service_config_file"]
                extension_list = ["main_metadata"]
                # TODO: we need to think sabout this part to make it more specific for an extension
                if extension_properties is not None and isinstance(
                    extension_properties.get("item_extensions"), list
                ):
                    for item_extension in extension_properties[
                        "item_extensions"
                    ]:
                        if isinstance(item_extension, str):
                            extension_list.append(item_extension)
                        elif isinstance(item_extension, tuple):
                            extension_list.append(item_extension[0])
                if extension_properties is not None and isinstance(
                    extension_properties.get("collection_extensions"), list
                ):
                    for collection_extension in extension_properties[
                        "collection_extensions"
                    ]:
                        if isinstance(collection_extension, str):
                            extension_list.append(collection_extension)
                        elif isinstance(collection_extension, tuple):
                            extension_list.append(collection_extension[0])

                # if extension_properties is not None and extension_properties["item_common_metadata"] is True:
                #     extension_list.extend("common_metadata")
                # if extension_properties is not None and (extension_properties["item_scientific"] is True or extension_properties["collection_scientific"] is True):
                #     extension_list.extend("scientific_extension")
                for i, service in enumerate(self.harvesting_vars["services"]):
                    if (
                        service.get("serviceType") == "Compound"
                        or service.get("serviceType") == "compound"
                    ):
                        services_list = service.findall(
                            "{%s}service" % constants.unidata
                        )
                        services_list.append(url)
                        for s in services_list:
                            if s == url:
                                s_get_name = "catalog"
                                service_url = url
                            else:
                                service_url = utils.references_urls(
                                    url, s.get("base")
                                ) + self.harvesting_vars["dataset"].get(
                                    "urlPath"
                                )

                                s_get_name = s.get("name")
                                if s.get("suffix") is not None:
                                    service_url += s.get("suffix")
                                if s_get_name in ["iso", "ncml", "uddc"]:
                                    service_url += (
                                        "?dataset=%s&&catalog=%s"
                                        % (
                                            self.harvesting_vars["catalog_id"],
                                            quote_plus(
                                                self.harvesting_vars[
                                                    "catalog_url"
                                                ]
                                            ),
                                        )
                                    )
                                if s_get_name in ["wms"]:
                                    service_url += "?service=WMS&version=1.3.0&request=GetCapabilities"
                                if s_get_name in ["dap4"]:
                                    service_url += ".dmr.xml"

                            if web_service_dict is not None and (
                                web_service_dict.get("webservices") is not None
                                and all(
                                    a.lower()
                                    in constants.supported_webservices
                                    for a in web_service_dict["webservices"]
                                )
                                and any(
                                    "/thredds/" + b.lower() + "/"
                                    in service_url
                                    for b in web_service_dict["webservices"]
                                )
                            ):
                                # To prevent 400 HTTP error
                                try:
                                    root = etree.XML(
                                        urlopen(service_url).read()
                                    )
                                    # root_nested = etree.parse(urlopen(service_url)).getroot()
                                except Exception:
                                    (
                                        ex_type,
                                        ex_value,
                                        ex_traceback,
                                    ) = sys.exc_info()
                                    if (
                                        ex_type is not None
                                        and ex_value is not None
                                    ):
                                        self.logger_properties[
                                            "logger_msg"
                                        ] = (
                                            "400 HTTP verifier error code `urlopen`: %s: %s . It appears that the %s webservice you are utilizing in your configuration is deactivated or not working. Please check the `tag_config.json` file or ensure the web service is activated."
                                            % (
                                                ex_type.__name__,
                                                ex_value,
                                                s_get_name,
                                            )
                                        )
                                        self.logger_properties[
                                            "logger_level"
                                        ] = "ERROR"
                                        logger.Logger(self.logger_properties)
                                    print(traceback.format_exc())
                                    continue

                                core.WebServiceContentScraper(
                                    root,
                                    json_file,
                                    extension_list,
                                    harvesting_vars,
                                )
                            # elif web_service_dict is not None and web_service_dict["webservice"] == "all":
                            #     if s_get_name in constants.supported_webservices:
                            #         # To prevent 400 HTTP error
                            #         try:
                            #             root = etree.XML(urlopen(service_url).read())
                            #         except Exception:
                            #             (
                            #                 ex_type,
                            #                 ex_value,
                            #                 ex_traceback,
                            #             ) = sys.exc_info()
                            #             if ex_type is not None and ex_value is not None:
                            #                 self.logger_properties["logger_msg"] = "400 HTTP verifier error code `urlopen`: %s: %s . It appears that the %s webservice you are utilizing in your configuration is deactivated or not working. Please check the `tag_config.json` file or ensure the web service is activated." % (
                            #                     ex_type.__name__,
                            #                     ex_value,
                            #                     s_get_name,
                            #                 )
                            #                 self.logger_properties["logger_level"] = "ERROR"
                            #                 logger.Logger(self.logger_properties)
                            #             # print(traceback.format_exc())
                            #             continue
                            #         core.WebServiceContentScraper(
                            #             root,
                            #             json_file,
                            #             extension_list,
                            #             harvesting_vars,
                            #         )
                            # elif web_service_dict is not None and web_service_dict["webservice"] == "catalog":
                            #     try:
                            #         root = etree.XML(urlopen(url).read())
                            #     except Exception:
                            #         (
                            #             ex_type,
                            #             ex_value,
                            #             ex_traceback,
                            #         ) = sys.exc_info()
                            #         if ex_type is not None and ex_value is not None:
                            #             self.logger_properties["logger_msg"] = "400 HTTP verifier error code `urlopen`: %s: %s . It appears that the %s webservice you are utilizing in your configuration is deactivated or not working. Please check the `tag_config.json` file or ensure the web service is activated." % (
                            #                 ex_type.__name__,
                            #                 ex_value,
                            #                 s_get_name,
                            #             )
                            #             self.logger_properties["logger_level"] = "ERROR"
                            #             logger.Logger(self.logger_properties)
                            #         print(traceback.format_exc())
                            #         continue
                            #     core.WebServiceContentScraper(
                            #         root,
                            #         json_file,
                            #         extension_list,
                            #         harvesting_vars,
                            #     )
                            elif web_service_dict is not None and (
                                web_service_dict.get("webservices") is not None
                                and not all(
                                    a.lower()
                                    in constants.supported_webservices
                                    for a in web_service_dict["webservices"]
                                )
                                and not any(
                                    "/thredds/" + b.lower() + "/"
                                    in service_url
                                    for b in web_service_dict["webservices"]
                                )
                            ):
                                self.logger_properties[
                                    "logger_msg"
                                ] = "It couldn't find any activated web service according to your `tag_config.json` configuration. Please check your web service configuration file, `tag_config.json`"
                                self.logger_properties[
                                    "logger_level"
                                ] = "ERROR"
                                logger.Logger(self.logger_properties)
                                continue
                    spatial = Spatial()
                    spatial_output = spatial.regulator(
                        self.harvesting_vars, spatial_information
                    )
                    if isinstance(spatial_output, list):
                        spatial_output = list(dict.fromkeys(spatial_output))
                        for error in spatial_output:
                            self.logger_properties["logger_msg"] = error
                            self.logger_properties["logger_level"] = "ERROR"
                            logger.Logger(self.logger_properties)
                        # important break, because spatial infos is one of mandatory elements of STAC
                        continue
                    else:
                        (
                            self.harvesting_vars["horizontal_extent_lon_min"],
                            self.harvesting_vars["horizontal_extent_lon_max"],
                            self.harvesting_vars["horizontal_extent_lat_min"],
                            self.harvesting_vars["horizontal_extent_lat_max"],
                        ) = spatial_output

                        spatial.harvester(self.harvesting_vars, linestring)
                    #########################################
                    # Harvesting and then regulating temporal
                    # information from the data
                    #########################################
                    temporal_class = Temporal()
                    temporal_output = temporal_class.regulator(
                        self.harvesting_vars,
                        temporal_format_by_dataname,
                        elem.get("name"),
                    )
                    if isinstance(temporal_output, list):
                        temporal_output = list(dict.fromkeys(temporal_output))
                        for error in temporal_output:
                            self.logger_properties["logger_msg"] = error
                            self.logger_properties["logger_level"] = "ERROR"
                            logger.Logger(self.logger_properties)
                        # important break, because temporal infos is one of mandatory elements of STAC
                        continue
                    self.harvesting_vars["collection_interval_time_final"] = [
                        self.harvesting_vars["collection_interval_time"][0],
                        self.harvesting_vars["collection_interval_time"][-1],
                    ]

            except Exception:
                ex_type, ex_value, ex_traceback = sys.exc_info()
                if ex_type is not None and ex_value is not None:
                    self.logger_properties[
                        "logger_msg"
                    ] = "`ItemHarvester` error: %s : %s" % (
                        ex_type.__name__,
                        ex_value,
                    )
                    self.logger_properties["logger_level"] = "CRITICAL"
                    logger.Logger(self.logger_properties)
                print(traceback.format_exc())
                return

    def __iter__(self):
        for key in self.harvesting_vars.keys():
            yield key, self.harvesting_vars[key]
