import json
import logging

from urllib.parse import urlparse
from datetime import datetime
from ..utils import is_uri

log = logging.getLogger(__name__)


class NdffObject:

    NON_ZERO_FIELDS = ()
    URI_FIELDS = ()
    OTHER_FIElDS = ()
    OPTIONAL_FIELDS = ()

    # location is here to make code checker happy, AND because we want to have is_valid at this level
    # a location should always be a valid location dict, never something else!
    # NOTE: the creation of a valid location dict is the responsibility of the NdffConnector object!
    location: dict = None

    def fields(self):
        return self.__dict__

    def get(self, field_name):
        if field_name in self.__dict__:
            if field_name == 'location':
                if self.location:
                    # TODO: create better string representation (eg for Polygon)
                    return str(self.location)
                else:
                    return 'Location ??'
            else:
                return self.__dict__[field_name]
        else:
            return None

    def set(self, field_name: str, field_value):
        # TODO more checks ?
        # ONLY set available properties (NOT adding new ones)
        if field_name in self.__dict__:
            if field_name == 'location' and not isinstance(field_value, dict):
                raise ValueError(f'Not possible to set location for an NdffObject: value is NOT a (location) dict!')
            self.__dict__[field_name] = field_value
        else:
            raise ValueError(f'Not possible to set "{field_name}" for this NdffObject: NOT an available property!')

    def set_extra_info(self, key_uri: str, value: str):
        # the key of an extra info field should ALWAYS be an uri! SHOULD BE CHECKED BEFORE SETTING !!!
        # the value can be both a data value or a value mapped to a NDFF uri
        # BOTH should be valid strings, NOT none
        if key_uri and value:
            self.extra_info.append({'key': key_uri, 'value': value})
        else:
            raise ValueError(f'Setting an Extra Info Key/Value, one of them is None: "{key_uri}" - "{value}"')

    def is_valid_datetime_string(self, datetime_field) -> (datetime, list):
        valid_datetime = None
        datetime_string = str(self.get(datetime_field)).strip()  # strip to be sure to remove leading/trailing spaces
        errors = []
        wrong_datetime_format = 'Veld "{}" met waarde "{}" zou een NDFF-geldig formaat moeten hebben: YYYY-MM-DDThh:mm:ss, YYYY-MM-DD hh:mm:ss, YYYY-MM-DDThh:mm, YYYY-MM-DD hh:mm, YYYY-MM-DDThh, YYYY-MM-DD hh, YYYY-MM-DD, YYYY-MM, YYYY'

        try:
            # just try to create a datetime from iso format
            valid_datetime = datetime.fromisoformat(datetime_string)
            # ok, that worked, done:
            return valid_datetime, []
        except Exception as e:
            pass

        # apparently one of those other possible formats
        datetime_string = datetime_string.replace('T', ' ')
        date_and_time = datetime_string.split(' ')
        try:
            if len(date_and_time) == 1:
                # apparently ONLY a Date
                # possible values: YYYY-MM-DD, YYYY-MM, YYYY
                # go from shortest to longest
                if len(date_and_time[0]) == 4:
                    valid_datetime = datetime.strptime(date_and_time[0], '%Y')
                elif len(date_and_time[0]) == 7:
                    valid_datetime = datetime.strptime(date_and_time[0], '%Y-%m')
                elif len(date_and_time[0]) == 10:
                    valid_datetime = datetime.strptime(date_and_time[0], '%Y-%m-%d')
                else:
                    raise
            elif len(date_and_time) == 2:
                # apparently a Date AND a Time
                # we already removed the T so possible values: YYYY-MM-DD hh:mm:ss, YYYY-MM-DD hh:mm, YYYY-MM-DD hh
                date_and_time = datetime_string
                if len(date_and_time) == 13:
                    valid_datetime = datetime.strptime(date_and_time, '%Y-%m-%d %H')
                elif len(date_and_time) == 16:
                    valid_datetime = datetime.strptime(date_and_time, '%Y-%m-%d %H:%M')
                elif len(date_and_time) == 19:
                    valid_datetime = datetime.strptime(date_and_time, '%Y-%m-%d %H:%M:%S')
                else:
                    raise
            else:
                raise
        except Exception as e:
            errors.append(wrong_datetime_format.format(datetime_field, datetime_string))
        #errors.append(wrong_datetime_format.format(datetime_field, datetime_string))

        return valid_datetime, errors

    def is_valid(self) -> (bool, list):
        errors = []
        none_message = 'Verplicht veld "{}" is nog leeg of onbekend'
        not_uri_message = 'Veld "{}" zou een geldige URI moeten zijn: "{}"'
        not_dict_message = 'Veld "{}" zou een "dict" moeten zijn, maar is "{}"'
        missing_location_field = '"Lokatie" zou een veld "{}" (waarde) moeten hebben'
        wrong_location_buffer_type = 'De buffer/nauwkeurigheid moet een numerieke waarde zijn (meters), maar heeft de waarde "{}"'
        wrong_geometry = 'Er is iets misgegaan bij het maken van de NDFF geometry, check data'
        wrong_buffer = 'De bufferwaarde/nauwkeurigheid moet een getal zijn en groter dan 0 zijn voor (multi) points/linestrings, maar heeft waarde "{}"'
        wrong_polygon_buffer = 'De bufferwaarde/nauwkeurigheid moet een getal zijn, en voor (multi)polygonen altijd 0 zijn, maar heeft waarde "{}"'
        periode_wrong_order = 'De start van de periode: "{}", ligt niet VOOR of OP het einde van de periode: "{}"'
        period_in_future = 'De periode "{}" - "{}" ligt in de toekomst'
        value_not_zero = 'De waarde voor {} moet groter 0 zijn dan, maar heeft waarde "{}"'

        # URI fields:
        for name in self.URI_FIELDS:
            if self.__dict__[name] in (None, '', ' ', '-', '?'):
                if name not in self.OPTIONAL_FIELDS:  # 20220421 dwelling is now an optional URI value
                    errors.append(none_message.format(name))
            elif not is_uri(self.__dict__[name]):
                # prrr... except when it is a abundance_value, which can be both uri and literal...
                if name != 'abundance_value':
                    errors.append(not_uri_message.format(name, self.__dict__[name]))

        # NON uri fields, we can only check for None/Null
        for name in self.OTHER_FIElDS:
            if self.__dict__[name] in (None, '', ' ', '-', '?'):
                errors.append(none_message.format(name))

        # NON zero fields:
        for name in self.NON_ZERO_FIELDS:
            if self.__dict__[name] in (None, '', '0', 0):
                errors.append(value_not_zero.format(name, self.__dict__[name]))

        # ONLY if this ndff object has a 'location' field
        if 'location' in self.__dict__:
            # Location related fields:
            # "location": {
            #     "buffer": 5,
            #     "geometry": {
            #         "type": "Point",
            #         "coordinates": [
            #             408241,
            #             78648
            #             ]
            #         }
            #     },
            if self.location in (None, '', ' ', '-', '?'):
                errors.append(none_message.format('location'))
            elif not isinstance(self.location, dict):
                errors.append(not_dict_message.format('location', type(self.location)))
            else:
                # ok, we have a dict, check keys
                #log.debug(f'self.location in is_valid: {self.location}')
                # check for buffer and geometry
                if 'buffer' not in self.location:
                    errors.append(missing_location_field.format('buffer/Lokatie-nauwkeurigheid'))
                if 'geometry' not in self.location:
                    errors.append(missing_location_field.format('geometry'))
                # check for valid buffer value
                if 'buffer' in self.location and self.location['buffer'] in ('', None, '-', '?'):
                    if not none_message.format('location_buffer') in errors:
                        errors.append(none_message.format('location_buffer'))
                elif 'buffer' in self.location and not str(self.location['buffer']).isnumeric():
                    errors.append(wrong_location_buffer_type.format(self.location['buffer']))
                # further geometry checking...
                if 'geometry' in self.location and 'coordinates' in self.location['geometry'] \
                        and 'type' in self.location['geometry'] and \
                        self.location['geometry']['type'].upper() in ('POINT', 'POLYGON', 'MULTIPOLYGON', 'LINESTRING', 'MULTILINESTRING'):
                    # going to check the value of the buffer:
                    if 'buffer' in self.location and self.location['geometry']['type'].upper() in ('POINT', 'LINESTRING', 'MULTILINESTRING'):
                        # a (MULTI)POINT  and (MULTI)LINESTRINGS should ALWAYS have a NON-ZERO buffer
                        if not str(self.location['buffer']).isnumeric() or float(self.location['buffer']) <= 0:
                            errors.append(wrong_buffer.format(self.location['buffer']))
                    elif 'buffer' in self.location and self.location['geometry']['type'].upper() in ('POLYGON', 'MULTIPOLYGON'):
                        # while a (MULTI)POLYGON should always have a ZERO buffer
                        if not str(self.location['buffer']).isnumeric() or float(self.location['buffer']) > 0:
                            errors.append(wrong_polygon_buffer.format(self.location['buffer']))
                    else:
                        pass
                else:
                    errors.append(wrong_geometry)

        # only checking for period_start... if it is set, there should also be a period_end
        if 'period_start' in self.__dict__:
            # period (period_start and period_stop)
            # NDFF 400 message: "Datetime heeft een ongeldig formaat, gebruik 1 van de volgende formaten:
            # YYYY-MM-DDThh:mm:ss, YYYY-MM-DD hh:mm:ss, YYYY-MM-DDThh:mm, YYYY-MM-DD hh:mm, YYYY-MM-DDThh, YYYY-MM-DD hh, YYYY-MM-DD, YYYY-MM, YYYY

            # append errors (IF any) after validation of the periods
            start, period_start_errors = self.is_valid_datetime_string('period_start')
            if start is None:
                errors += period_start_errors
            stop, period_stop_errors = self.is_valid_datetime_string('period_stop')
            if stop is None:
                errors += period_stop_errors

            if self.period_start is None:
                errors.append(none_message.format('period_start'))
            if self.period_stop is None:
                errors.append(none_message.format('period_stop'))

            # NOT going to check if stop > start, as NDFF also accepts start == stop :-(
            if start and stop:
                if stop >= datetime.now():
                    errors.append(period_in_future.format(self.period_start, self.period_stop))
                if start > stop:
                    errors.append(periode_wrong_order.format(self.period_start, self.period_stop))

        # other fields
        if len(errors) == 0:
            return True, []
        else:
            # insert the 'identity' of this observation
            errors.insert(0, f'* Fouten voor NdffObject {self.identity} *')
            return False, errors


class NdffDatasetType(NdffObject):
    def __init__(self, identity: str = None, category: str = None, description: str = None, ndff_uri: str = None):
        self.identity = identity  # URI
        self.category = category  # any
        self.description = description  # any
        self.ndff_uri = ndff_uri  # URI

    def to_ndff_datasettype_json(self, datasettype_identity=None) -> str:
        # https://accapi.ndff.nl/api/v2/domains/708/datasettypes/21534/
        # {
        #     "_links": {
        #         "self": {
        #             "href": "https://accapi.ndff.nl/api/v2/domains/708/datasettypes/21534/"
        #         }
        #     },
        #     "category": "gewone map",
        #     "description": "map Test NGB",
        #     "identity": "http://ndff.nl/foldertypes/test_ngb/map"
        # }
        data = {}

        if datasettype_identity:
            data['identity'] = datasettype_identity
        else:
            data['identity'] = self.identity
        data['category']                = self.category
        data['description']             = self.description

        return json.dumps(data, indent=2)


class NdffProtocol(NdffObject):

    # all URI_FIELDS that should have a URI as value
    URI_FIELDS = (
        'identity',
    )

    OTHER_FIElDS = (
        'description',
    )

    def __init__(self, identity: str = None, category: str = None, description: str = None, ndff_uri: str = None):
        self.identity = identity  # URI
        self.description = description  # any
        self.ndff_uri = ndff_uri  # URI

    def to_ndff_protocol_json(self, protocol_identity=None) -> str:
        # https://accapi.ndff.nl/api/v2/domains/708/datasettypes/21534/
        # {
        #     "_links": {
        #         "self": {
        #             "href": "https://accapi.ndff.nl/api/v2/domains/708/datasettypes/21534/"
        #         }
        #     },
        #     "category": "gewone map",
        #     "description": "map Test NGB",
        #     "identity": "http://ndff.nl/foldertypes/test_ngb/map"
        # }
        data = {}

        if protocol_identity:
            data['identity'] = protocol_identity
        else:
            data['identity'] = self.identity
        data['description'] = self.description

        return json.dumps(data, indent=2)


class NdffDataset(NdffObject):

    # all URI_FIELDS that should have a URI as value
    URI_FIELDS = (
        'dataset_type',
        'parent',
        'identity',
        'protocol',
    )

    OTHER_FIElDS = (
        'period_start',
        'period_stop',
        'description',
    )

    OPTIONAL_FIELDS = (
        'duration',
        'location_coverage',
        'protocol',
    )

    # TODO should we have a constructor without arguments?
    def __init__(self, description: str = None, dataset_type_uri: str = None, parent_uri: str = None):
        self.ndff_uri = None  # URI optional
        self.identity = None  # URI mandatory
        self.parent = parent_uri  # URI mandatory
        self.dataset_type = dataset_type_uri  # URI mandatory
        self.description = description  # any mandatory
        self.duration = 0  # any
        # location: mandatory for now always fixed starting coordinates ??
        self.location = {'buffer': 10, 'geometry': {'type': 'Point', 'coordinates': [5.38759722, 52.1556583]}}
        self.location_coverage = 0  # ?? don't know what this is for?

        self.protocol = None  # URI optional

        self.period_start = None  # mandatory NDFF Date(Time) str: YYYY-MM-DDThh:mm:ss, YYYY-MM-DD hh:mm:ss, YYYY-MM-DDThh:mm, YYYY-MM-DD hh:mm, YYYY-MM-DDThh, YYYY-MM-DD hh, YYYY-MM-DD, YYYY-MM, YYYY
        self.period_stop = None  # mandatory NDFF Date(Time) str: YYYY-MM-DDThh:mm:ss, YYYY-MM-DD hh:mm:ss, YYYY-MM-DDThh:mm, YYYY-MM-DD hh:mm, YYYY-MM-DDThh, YYYY-MM-DD hh, YYYY-MM-DD, YYYY-MM, YYYY

        self.extra_info = []
        self.involved = []

    @staticmethod
    def from_dataset_json_to_dataset(dataset_json_dict: dict):
        """
        {
             '_links': {
                 'self': {
                     'href': 'https://accapi.ndff.nl/api/v2/domains/708/datasets/2922579/'}
             },
             'datasetType': 'http://ndff.nl/foldertypes/test_ngb/map',
             'description': 'test_richard',
             'duration': None,
             'extrainfo': [],
             'identity': 'http://ndff.nl/testngb/folders/2922579',
             'involved': [],
             'location': None,
             'locationCoverage': None,
             'parent': 'http://ndff.nl/folders/2920720',
             'periodStart': None,
             'periodStop': None,
             'protocol': None
         }
        """
        d = NdffDataset()
        d.dataset_type = dataset_json_dict['datasetType']
        d.description = dataset_json_dict['description']
        d.duration = dataset_json_dict['duration']
        d.extra_info = dataset_json_dict['extrainfo']
        d.identity = dataset_json_dict['identity']
        d.involved = dataset_json_dict['involved']
        d.location = dataset_json_dict['location']
        d.location_coverage = dataset_json_dict['locationCoverage']
        d.parent = dataset_json_dict['parent']
        d.period_start = dataset_json_dict['periodStart']
        d.period_stop = dataset_json_dict['periodStop']
        d.protocol = dataset_json_dict['protocol']
        if dataset_json_dict['_links'] and dataset_json_dict['_links']['self'] and dataset_json_dict['_links']['self']['href']:
            d.ndff_uri = dataset_json_dict['_links']['self']['href']
        return d

    def to_ndff_dataset_json(self, dataset_identity=None) -> str:
        # https://accapi.ndff.nl/api/v2/domains/708/datasets/2929738/

        # {
        #     "_links": {
        #         "self": {
        #             "href": "https://accapi.ndff.nl/api/v2/domains/708/datasets/2929738/"
        #         }
        #     },
        #     "datasetType": "http://ndff.nl/foldertypes/test_ngb/map",
        #     "description": "test_dataset",
        #     "duration": 0,
        #     "extrainfo": [],
        #     "identity": "http://ndff.nl/testngb/folders/2929738",
        #     "involved": [],
        #     "location": {
        #         "buffer": 10,
        #         "geometry": {
        #             "type": "Point",
        #             "coordinates": [
        #                 5.38759722,
        #                 52.1556583
        #             ]
        #         }
        #     },
        #     "locationCoverage": 0,
        #     "parent": "http://ndff.nl/folders/2920720",
        #     "periodStart": null,
        #     "periodStop": null,
        #     "protocol": null
        # }
        data = {}

        if dataset_identity:
            data['identity'] = dataset_identity
        else:
            data['identity'] = self.identity
        data['parent']                  = self.parent
        data['datasetType']             = self.dataset_type
        data['description']             = self.description
        data['duration']                = self.duration
        data['protocol']                = self.protocol
        # periods could be datetime objects (in case of Postgis Datasource)
        data['periodStart']             = str(self.period_start)
        data['periodStop']              = str(self.period_stop)
        data['location']                = self.location
        data['locationCoverage']        = self.location_coverage
        # note: this is the data holder for the lists in the json (NOT fields from record or observation)
        data['extrainfo']               = self.extra_info
        data['involved']                = self.involved

        return json.dumps(data, indent=2)


class NdffObservation(NdffObject):
    """
    A NdffObservation object is more of a simple data holder, able to validate itself and to serialize itself to json

    A NdffObservation object is created using the NdffConnector's 'map_data_to_ndff_observation'.

    It is the responsibility of the NdffConnector to create a more or less valid NdffObservation.
    """

    # all URI_FIELDS that should have an URI as value
    URI_FIELDS = (
        'taxon',
        'abundance_schema',
        'determination_method',
        'dataset',
        'biotope',
        'identity',
        'lifestage',
        'subject_type',
        'survey_method',
        'sex',
        'activity',
        'dwelling',
        'abundance_value',  # NOTE: this can be both an URI and a literal (mostly integer....)
    )

    OTHER_FIElDS = (
        #'abundance_value',
        'period_start',
        'period_stop',
    )

    NON_ZERO_FIELDS = (
        'abundance_value',
    )

    OPTIONAL_FIELDS = (
        'dwelling',
    )

    def __init__(self):
        self.taxon = None  # URI
        self.abundance_schema = None  # URI
        self.abundance_value = None  # any
        self.activity = None  # URI
        self.determination_method = None  # URI
        self.dataset = None  # URI
        self.biotope = None  # URI
        self.identity = None  # URI, actually the INTERNAL identity (NOT the NDFF identity link)
        self.lifestage = None  # URI
        self.period_start = None  # NDFF Date(Time) str: YYYY-MM-DDThh:mm:ss, YYYY-MM-DD hh:mm:ss, YYYY-MM-DDThh:mm, YYYY-MM-DD hh:mm, YYYY-MM-DDThh, YYYY-MM-DD hh, YYYY-MM-DD, YYYY-MM, YYYY
        self.period_stop = None  # NDFF Date(Time) str: YYYY-MM-DDThh:mm:ss, YYYY-MM-DD hh:mm:ss, YYYY-MM-DDThh:mm, YYYY-MM-DD hh:mm, YYYY-MM-DDThh, YYYY-MM-DD hh, YYYY-MM-DD, YYYY-MM, YYYY
        self.sex = None  # URI
        self.subject_type = None  # URI
        self.survey_method = None  # URI
        self.dwelling = None  # URI OPTIONAL

        self.extra_info = []
        self.involved = []

        # a location should always be a valid location dict, never something else!
        # NOTE: the creation of a valid location dict is the responsibility of the NdffConnector object!
        self.location: dict = None

    def to_ndff_observation_json(self, observation_identity=None, observation_dataset=None) -> str:
        """
        The observation_identity and observation_dataset params make it possible
        to override the real identity/dataset content of the observation, thereby
        makeing it easier to create unique observation json for testing...

        Create valid JSON for NDFF based on current values of all fields.
        If the param 'observation_identity' is None, the field 'identity'
        should be a valid identity(-uri).
        If the param 'observation_dataset' is None, the field 'dataset' should
        be a valid dataset(-uri). AND should be available/defined at NDFF

        :param: observation_identity (optional)
        :param: observation_dataset (optional)
        :return: NDFF json as string
        """
        # json = '''{"abundanceSchema": "http://ndff-ecogrid.nl/codes/scales/exact_count",
        #     "abundanceValue": 1,
        #     "activity": "http://ndff-ecogrid.nl/codes/domainvalues/observation/activities/calling",
        #     "determinationMethod": "http://ndff-ecogrid.nl/codes/domainvalues/observation/determinationmethods/550",
        #     "extrainfo": [],
        #     "dataset": "http://notatio.nl/dataset/2",
        #     "biotope": "http://ndff-ecogrid.nl/codes/domainvalues/location/biotopes/unknown",
        #     "identity": "http://notatio.nl/waarneming/7500",
        #     "involved": [
        #         {
        #         "involvementType": "http://ndff-ecogrid.nl/codes/involvementtypes/data_owner",
        #         "person": "http://telmee.nl/contacts/persons/1261085"
        #         }
        #     ],
        #     "lifestage": "http://ndff-ecogrid.nl/codes/domainvalues/observation/lifestages/509",
        #     "location": {
        #         "buffer": 5,
        #         "geometry": {
        #             "type": "Point",
        #             "coordinates": [
        #                 408241,
        #                 78648
        #                 ]
        #             }
        #         },
        #     "periodStart": "2014-08-29 01:22:00",
        #     "periodStop": "2014-08-29 01:22:00",
        #     "sex": "http://ndff-ecogrid.nl/codes/domainvalues/observation/sexes/undefined",
        #     "subjectType": "http://ndff-ecogrid.nl/codes/subjecttypes/live/individual",
        #     "surveyMethod": "http://ndff-ecogrid.nl/codes/domainvalues/survey/surveymethods/na",
        #     "taxon": "http://ndff-ecogrid.nl/taxonomy/taxa/pipistrellusnathusii",
        #     "dwelling": "http://ndff-ecogrid.nl/codes/domainvalues/observation/dwellings/unknown",
        #     "extrainfo": [
        #         {
        #           "key": "http://ndff-ecogrid.nl/codes/keys/external/location_id",
        #           "value": "NL09_GROO6140002"
        #         },
        #         {
        #           "key": "http://ndff-ecogrid.nl/codes/keys/external/original_visit_id",
        #           "value": "NL09_GROO6140002_2014_7_21_VISB1_1"
        #         }
        #     ]
        #
        # }

        if not self.is_valid()[0]:
            raise ValueError(self.is_valid()[1])

        data = {}

        if observation_identity:
            data['identity'] = observation_identity
        else:
            data['identity'] = self.identity
        if observation_dataset:
            data['dataset'] = observation_dataset
        else:
            data['dataset'] = self.dataset
        data['abundanceSchema']         = self.abundance_schema
        data['abundanceValue']          = self.abundance_value
        data['activity']                = self.activity
        data['determinationMethod']     = self.determination_method
        # dataset ^
        data['biotope']                 = self.biotope
        # identity ^
        data['lifestage']               = self.lifestage
        data['sex']                     = self.sex
        data['subjectType']             = self.subject_type
        data['surveyMethod']            = self.survey_method
        data['taxon']                   = self.taxon
        # periods could be datetime objects (in case of Postgis Datasource), that is the reason we cast to str
        #data['periodStart']             = str(self.period_start)
        #data['periodStop']              = str(self.period_stop)
        # some timestrings contain decimal seconds, API does not want that, that is the reason we format as below
        # https://ndff.zendesk.com/hc/nl/requests/41288
        data['periodStart']             = (self.is_valid_datetime_string('period_start')[0]).strftime('%Y-%m-%dT%H:%M:%S')
        data['periodStop']              = (self.is_valid_datetime_string('period_stop')[0]).strftime('%Y-%m-%dT%H:%M:%S')
        data['location']                = self.location
        # dwelling is an OPTIONAL key, not to be sent if unknown or empty
        # a value like: http://ndff-ecogrid.nl/codes/domainvalues/observation/dwellings/unknown is going to be sent
        if self.dwelling not in (None, '', '-'):
            #data['dwelling']                = self.dwelling
            self.extra_info.append({'key': 'http://ndff-ecogrid.nl/codes/keys/observation/dwellings', 'value': self.dwelling})

        # note: this is the data holder for the lists in the json (NOT fields from record or observation)
        data['extrainfo']               = self.extra_info
        data['involved']                = self.involved

        # for key in self.data.keys():
        #     list_field = None
        #     kv1 = None
        #     kv2 = None
        #     if 'extra_info_identity_' in key:
        #         list_field = 'extrainfo'
        #         if self.data[key] is not None:
        #             kv1 = ('identity', self.data[key])
        #             kv2 = ('value', self.data['extra_info_value_'+key.replace('extra_info_identity_', '')])
        #     elif 'involved_type_' in key:
        #         list_field = 'involved'
        #         if self.data[key] is not None:
        #             kv1 = ('involvementType', self.data[key])
        #             kv2 = ('person', self.data['involved_person_'+key.replace('involved_type_', '')])
        #     if list_field and kv1 and kv2:
        #         data[list_field].append({kv1[0]: kv1[1], kv2[0]: kv2[1]})

        return json.dumps(data, indent=2)


class NdffResult(dict):

    # CREATE TABLE public.ndff_log
    # (
    #   id serial,
    #   object_type character varying,
    #   object_id integer,  -- identity uri
    #   ndff_uri character varying,
    #   http_method character varying,
    #   http_status character varying,
    #   http_response character varying,
    #   tstamp timestamp with time zone
    # )

    def __init__(self,
                 waarneming_id,
                 object_type,
                 object_id,
                 ndff_uri,
                 http_method,
                 http_status,
                 http_response,
                 id=None,
                 tstamp=None,
                 related_uri=None):
        super().__init__()
        #log.debug('NdffResult init')
        # temporary?
        # remove query part from nddf_uri
        # after searching the uri's contained ?format=javascript
        o = urlparse(ndff_uri)
        ndff_uri = o.scheme + "://" + o.netloc + o.path
        self['waarneming_id'] = waarneming_id
        self['id'] = id
        self['object_type'] = object_type
        self['object_id'] = object_id
        self['ndff_uri'] = ndff_uri
        self['http_method'] = http_method
        self['http_status'] = http_status
        #self['http_response'] = json.dumps(http_response)
        # 20220422 NOT returning a string representation of the http response, but the response as we got it after 'handling' it
        # it IS very much possible that this is now a dict or an json object...
        self['http_response'] = http_response
        self['tstamp'] = tstamp
        # 20221211 RD: ALSO add the 'related_uri' uri to the output IF it is there
        self['related_uri'] = related_uri

    @staticmethod
    def quote_if_needed(value):
        ret = value
        if ',' in value:
            # create quoted value
            ret = f'"{value}"'
        return ret

    def as_tuple(self):
        if self['tstamp'] is None:
            self['tstamp'] = datetime.now().isoformat(timespec='seconds')
        return (self['tstamp'],
                self['object_type'],
                self['http_method'],
                self['http_status'],
                self['object_id'],
                self['ndff_uri'],
                self['related_uri'],
                self.quote_if_needed(self['http_response'])
                )
