# MIT License
#
# Copyright (c) 2020 PANGAEA (https://www.pangaea.de/)
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import json

import idutils
import rdflib
from rdflib import Namespace
from rdflib.namespace import RDF
from rdflib.namespace import DCTERMS
from rdflib.namespace import DC
from rdflib.namespace import FOAF

from fuji_server.helper.metadata_collector import MetaDataCollector
from fuji_server.helper.request_helper import RequestHelper, AcceptTypes
from fuji_server.helper.metadata_mapper import Mapper

class MetaDataCollectorRdf (MetaDataCollector):
    target_url=None
    def __init__(self,  loggerinst, target_url, source, rdf_graph=None):
        self.target_url = target_url
        self.content_type = None
        self.source_name = source
        self.rdf_graph=rdf_graph
        super().__init__(logger=loggerinst)


    def parse_metadata(self):
        #self.source_name = self.getEnumSourceNames().LINKED_DATA.value
        self.logger.info('FsF-F2-01M : Extract metadata from {}'.format(self.source_name))
        rdf_metadata=dict()
        if self.rdf_graph is None:
            #print(self.target_url)
            requestHelper: RequestHelper = RequestHelper(self.target_url, self.logger)
            requestHelper.setAcceptType(AcceptTypes.rdf)
            neg_source,rdf_response = requestHelper.content_negotiate('FsF-F2-01M')
            #required for metric knowledge representation

            if requestHelper.getHTTPResponse() is not None:
                self.content_type = requestHelper.getHTTPResponse().headers.get('content-type')
                if self.content_type is not None:
                    self.content_type = self.content_type.split(";", 1)[0]
                    #handle JSON-LD
                    DCAT = Namespace("http://www.w3.org/ns/dcat#")
                    if self.content_type == 'application/ld+json':
                        try:
                            jsonldgraph= rdflib.ConjunctiveGraph()
                            rdf_response = jsonldgraph.parse(data=json.dumps(rdf_response), format='json-ld')
                            rdf_response = jsonldgraph
                        except Exception as e:
                            self.logger.info('FsF-F2-01M : Parsing error, failed to extract JSON-LD - {}'.format(e))
        else:
            neg_source, rdf_response = 'html' , self.rdf_graph

        ontology_indicator=[rdflib.term.URIRef('http://www.w3.org/2004/02/skos/core#'),rdflib.term.URIRef('http://www.w3.org/2002/07/owl#')]
        if isinstance(rdf_response,rdflib.graph.Graph):
            self.logger.info('FsF-F2-01M : Found RDF Graph')
            # TODO: set credit score for being valid RDF
            # TODO: since its valid RDF aka semantic representation, make sure FsF-I1-01M is passed and scored
            if rdflib.term.URIRef('http://www.w3.org/ns/dcat#') in dict(list(rdf_response.namespaces())).values():
                self.logger.info('FsF-F2-01M : RDF Graph seems to contain DCAT metadata elements')
                rdf_metadata = self.get_dcat_metadata(rdf_response)
            elif bool(set(ontology_indicator) & set(dict(list(rdf_response.namespaces())).values())):
                rdf_metadata = self.get_ontology_metadata(rdf_response)
            else:
                rdf_metadata = self.get_default_metadata(rdf_response)
            #add found namespaces URIs to namespace
            for ns in rdf_response.namespaces():
                self.namespaces.append(str(ns[1]))
        else:
            self.logger.info('FsF-F2-01M : Expected RDF Graph but received - {0}'.format(self.content_type))
        return self.source_name, rdf_metadata

    def get_default_metadata(self,g):
        meta = dict()
        try:
            self.logger.info('FsF-F2-01M : Trying to query generic SPARQL on RDF')
            r = g.query(Mapper.GENERIC_SPARQL.value)
            for row in sorted(r):
                for l, v in row.asdict().items():
                    if l is not None:
                        meta[l] = str(v)
                break
        except Exception as e:
            self.logger.info('FsF-F2-01M : SPARQLing error - {}'.format(e))
        if len(meta)<=0:
            meta['object_type'] = 'Other'
            self.logger.info('FsF-F2-01M : Could not find metadata elements through generic SPARQL query on RDF')
        else:
            self.logger.info('FsF-F2-01M : Found some metadata elements through generic SPARQL query on RDF: '+str(meta.keys()))
        return meta

    #TODO rename to: get_core_metadata
    def get_metadata(self,g, item, type='Dataset'):
        DCAT = Namespace("http://www.w3.org/ns/dcat#")
        meta = dict()
        meta['object_identifier'] = (g.value(item, DC.identifier) or g.value(item, DCTERMS.identifier))
        '''
        if self.source_name != self.getEnumSourceNames().RDFA.value:
            meta['object_identifier'] = str(item)
            meta['object_content_identifier'] = [{'url': str(item), 'type': 'application/rdf+xml'}]
        '''
        meta['object_type'] = type
        meta['title'] = (g.value(item, DC.title) or g.value(item, DCTERMS.title))
        meta['summary'] = (g.value(item, DC.description) or g.value(item, DCTERMS.description))
        meta['publication_date'] = (g.value(item, DC.date) or g.value(item, DCTERMS.date)  or g.value(item, DCTERMS.issued))
        meta['publisher'] = (g.value(item, DC.publisher) or g.value(item, DCTERMS.publisher))
        meta['keywords']=[]
        for keyword in (list(g.objects(item, DCAT.keyword)) + list(g.objects(item, DCTERMS.keyword)) + list(g.objects(item, DC.keyword))):
            meta['keywords'].append(str(keyword))
        #TODO creators, contributors
        meta['creator'] = g.value(item, DC.creator)
        meta['license'] = g.value(item, DCTERMS.license)
        # quick fix (avoid rdflib literal type exception)
        for v in [meta['title'],meta['summary'], meta['publisher']]:
            if v:
                v = v.toPython()
        return meta

    def get_ontology_metadata(self, graph):
        ont_metadata=dict()
        OWL=Namespace("http://www.w3.org/2002/07/owl#")
        SKOS = Namespace("http://www.w3.org/2004/02/skos/core#")
        ontologies=list(graph[: RDF.type: OWL.Ontology])
        if len(ontologies)>0:
            self.logger.info('FsF-F2-01M : RDF Graph seems to represent a OWL Ontology')
            ont_metadata=self.get_metadata(graph,ontologies[0],type='Ontology')
        else:
            ontologies = list(graph[: RDF.type: SKOS.Collection])
            if len(ontologies)>0:
                self.logger.info('FsF-F2-01M : RDF Graph seems to represent a SKOS Ontology')
                ont_metadata = self.get_metadata(graph, ontologies[0], type='Ontology')
            else:
                self.logger.info('FsF-F2-01M : Could not parse Ontology RDF')
        return ont_metadata

    def get_dcat_metadata(self, graph):
        dcat_metadata=dict()
        DCAT = Namespace("http://www.w3.org/ns/dcat#")

        datasets = list(graph[: RDF.type: DCAT.Dataset])
        if len(datasets)>0:
            dcat_metadata = self.get_metadata(graph, datasets[0],type='Dataset')
            # publisher
            if idutils.is_url(dcat_metadata.get('publisher')) or dcat_metadata.get('publisher') is None:
                publisher = graph.value(datasets[0], DCTERMS.publisher)
                # FOAF preferred DCAT compliant
                publisher_name = graph.value(publisher, FOAF.name)
                dcat_metadata['publisher'] = publisher_name
                # in some cases a dc title is used (not exactly DCAT compliant)
                if dcat_metadata.get('publisher') is None:
                    publisher_title = graph.value(publisher, DCTERMS.title)
                    dcat_metadata['publisher'] = publisher_title

            # creator
            if idutils.is_url(dcat_metadata.get('creator')) or dcat_metadata.get('creator') is None:
                creators = graph.objects(datasets[0], DCTERMS.creator)
                creator_name = []
                for creator in creators:
                    creator_name.append(graph.value(creator, FOAF.name))
                if len(creator_name) > 0:
                    dcat_metadata['creator'] = creator_name

            # distribution
            distribution = graph.objects(datasets[0], DCAT.distribution)
            dcat_metadata['object_content_identifier']=[]
            for dist in distribution:
                durl=graph.value(dist, DCAT.accessURL)
                #taking only one just to check if licence is available
                dcat_metadata['license']=graph.value(dist, DCTERMS.license)
                # TODO: check if this really works..
                dcat_metadata['access_rights']=(graph.value(dist, DCTERMS.accessRights) or graph.value(dist, DCTERMS.rights))
                dtype=graph.value(dist, DCAT.mediaType)
                dsize=graph.value(dist, DCAT.bytesSize)
                dcat_metadata['object_content_identifier'].append({'url':str(durl),'type':str(dtype), 'size':dsize})

            if dcat_metadata['object_content_identifier']:
                self.logger.info('FsF-F3-01M : Found data links in DCAT.org metadata : ' + str(dcat_metadata['object_content_identifier']))
                #TODO: add provenance metadata retrieval
        else:
            self.logger.info('FsF-F2-01M : Found DCAT content but could not correctly parse metadata')
            #in order to keep DCAT in the found metadata list, we need to pass at least one metadata value..
            dcat_metadata['object_type'] = 'Dataset'
        return dcat_metadata
            #rdf_meta.query(self.metadata_mapping.value)
            #print(rdf_meta)
        #return None

    def get_content_type(self):
        return self.content_type