#!/home/xgarnier/Workspace/git_repositories/abstractor/venv/bin/python

import argparse
import textwrap
from libabstractor.SparqlQuery import SparqlQuery


class Abstractor(object):
    """Abstractor main class"""

    def __init__(self):
        """Init

        Parse args and get prefixex
        """
        parser = argparse.ArgumentParser(description="Generate AskOmics abstraction from a SPARQL endpoint")

        parser.add_argument("-e", "--endpoint", type=str, help="SPARQL enpoint url", required=True)
        parser.add_argument("-p", "--endpoint-prefix", type=str, help="Endpoint prefix", required=True)
        parser.add_argument("--askomics-prefix", type=str, help="AskOmics prefix", default="http://www.semanticweb.org/user/ontologies/2018/1#")
        parser.add_argument("-o", "--output", type=str, help="Output ttl file", default="abstraction.ttl")

        self.args = parser.parse_args()

    def get_entities_and_relations(self):
        """Get all entities and relations

        Returns
        -------
        list, list
            header and results
        """
        sparql = SparqlQuery(self.args.endpoint, self.args.askomics_prefix)

        query = textwrap.dedent('''
        SELECT DISTINCT ?source_entity ?relation ?target_entity
        WHERE {
            # Get entities
            ?instance_of_source a ?source_entity .
            ?instance_of_target a ?target_entity .
            # Relations
            ?instance_of_source ?relation ?instance_of_target .
        }
        ''')

        return sparql.process_query(query)

    def get_entities_and_numeric_attributes(self):
        """Get all entities and numeric attributes

        Returns
        -------
        list, list
            header and results
        """
        sparql = SparqlQuery(self.args.endpoint, self.args.askomics_prefix)

        query = textwrap.dedent('''
        SELECT DISTINCT ?entity ?attribute
        WHERE {
            # Get entities
            ?instance_of_entity a ?entity .
            # Attributes
            ?instance_of_entity ?attribute ?value .
            FILTER (isNumeric(?value))
        }
        ''')

        return sparql.process_query(query)

    def get_entities_and_text_attributes(self):
        """Get all entities and text attributes

        Returns
        -------
        list, list
            header and results
        """
        sparql = SparqlQuery(self.args.endpoint, self.args.askomics_prefix)

        query = textwrap.dedent('''
        SELECT DISTINCT ?entity ?attribute
        WHERE {
            # Get entities
            ?instance_of_entity a ?entity .
            # Attributes
            ?instance_of_entity ?attribute ?value .
            FILTER (isLiteral(?value))
            FILTER (!isNumeric(?value))
        }
        ''')

        return sparql.process_query(query)

    def main(self):
        """main"""
        sparql = SparqlQuery(self.args.endpoint, self.args.askomics_prefix)

        with open(self.args.output, "w") as file:

            # Insert prefix
            file.write(sparql.get_ttl_prefix())

            # launch query
            try:
                result_entities = self.get_entities_and_relations()
            except Exception as e:
                raise e

            entities = []

            # Entities and relations
            for result in result_entities:
                source_entity = result["source_entity"]
                target_entity = result["target_entity"]
                relation = result["relation"]

                if not source_entity.startswith(self.args.endpoint_prefix) or not target_entity.startswith(self.args.endpoint_prefix):
                    continue

                # Write source entity
                if source_entity not in entities:
                    entities.append(source_entity)
                    ttl = textwrap.dedent('''
                    <{}> a :entity ,
                            :startPoint ,
                            owl:Class ;
                            :instancesHaveNoLabels true ;
                        rdfs:label "{}" .
                    '''.format(
                        source_entity,
                        sparql.get_label(source_entity)
                    ))
                    file.write(ttl)

                # Write target entity
                if target_entity not in entities:
                    entities.append(target_entity)
                    ttl = textwrap.dedent('''
                    <{}> a :entity ,
                            :startPoint ,
                            owl:Class ;
                            :instancesHaveNoLabels true ;
                        rdfs:label "{}" .
                    '''.format(
                        target_entity,
                        sparql.get_label(target_entity)
                    ))
                    file.write(ttl)

                # Write relation
                ttl = textwrap.dedent('''
                <{}> a owl:ObjectProperty ,
                        :AskomicsRelation ;
                    rdfs:label "{}" ;
                    rdfs:domain <{}> ;
                    rdfs:range <{}> .
                '''.format(
                    relation,
                    sparql.get_label(relation),
                    source_entity,
                    target_entity
                ))
                file.write(ttl)

            # launch query
            try:
                result_numeric_attr = self.get_entities_and_numeric_attributes()
            except Exception as e:
                raise e

            # Numeric attributes
            for result in result_numeric_attr:
                entity = result["entity"]
                attribute = result["attribute"]

                if not entity.startswith(self.args.endpoint_prefix) and attribute.startswith(self.args.endpoint_prefix):
                    continue

                if attribute:
                    ttl = textwrap.dedent('''
                    <{}> a owl:DatatypeProperty ;
                        rdfs:label "{}" ;
                        rdfs:domain <{}> ;
                        rdfs:range xsd:decimal .
                    '''.format(
                        attribute,
                        sparql.get_label(attribute),
                        entity
                    ))

                    file.write(ttl)

            # launch query
            try:
                result_text_attr = self.get_entities_and_text_attributes()
            except Exception as e:
                raise e

            for result in result_text_attr:
                entity = result["entity"]
                attribute = result["attribute"]

                if not entity.startswith(self.args.endpoint_prefix) and attribute.startswith(self.args.endpoint_prefix):
                    continue

                if attribute:
                    ttl = '''
                    <{}> a owl:DatatypeProperty ;
                        rdfs:label "{}" ;
                        rdfs:domain <{}> ;
                        rdfs:range xsd:string .
                    '''.format(
                        attribute,
                        sparql.get_label(attribute),
                        entity
                    )

                    file.write(ttl)


if __name__ == '__main__':
    """main"""
    Abstractor().main()
