import xml.etree.ElementTree as ET
from abc import ABC, abstractmethod
from enum import Enum


class DeclarationType(Enum):
    CLASS: str = 'Class',
    NAMED_INDIVIDUAL: str = 'NamedIndividual',
    ANNOTATION_PROPERTY: str = 'AnnotationProperty',
    OBJECT_PROPERTY: str = 'ObjectProperty'


class XmlWriter(ABC):
    def __init__(self, ontology, tree):
        self.ontology = ontology
        self.tree = tree

    @staticmethod
    def add_declaration(parent_node: ET.Element, iri: str, declaration_type: DeclarationType) -> ET.Element:
        declaration = ET.SubElement(parent_node, 'Declaration')
        if declaration_type == DeclarationType.CLASS:
            ET.SubElement(declaration, 'Class', {'IRI': iri})
        elif declaration_type == DeclarationType.NAMED_INDIVIDUAL:
            ET.SubElement(declaration, 'NamedIndividual', {'IRI': iri})
        elif declaration_type == DeclarationType.ANNOTATION_PROPERTY:
            ET.SubElement(declaration, 'AnnotationProperty', {'IRI': iri})
        elif declaration_type == DeclarationType.OBJECT_PROPERTY:
            ET.SubElement(declaration, 'ObjectProperty', {'IRI': iri})
        return declaration

    @staticmethod
    def add_literal_property(parent_node: ET.Element, iri: str,
                             property_attributes: dict,
                             property_type: dict, property_value: str):
        annotation_assertion = ET.SubElement(parent_node, 'AnnotationAssertion')
        ET.SubElement(annotation_assertion, 'AnnotationProperty',
                      property_attributes)
        iri_element = ET.SubElement(annotation_assertion, 'IRI')
        iri_element.text = iri
        if property_type.items() is None:
            literal = ET.SubElement(annotation_assertion, 'Literal')
        else:
            literal = ET.SubElement(annotation_assertion, 'Literal', property_type)
        literal.text = property_value
        return annotation_assertion

    @staticmethod
    def add_object_property(parent_node: ET.Element, iri: str,
                            property_attributes: dict,
                            property_value: str):
        annotation_assertion = ET.SubElement(parent_node, 'AnnotationAssertion')
        ET.SubElement(annotation_assertion, 'AnnotationProperty',
                      property_attributes)
        iri_element = ET.SubElement(annotation_assertion, 'IRI')
        iri_element.text = iri
        iri_element = ET.SubElement(annotation_assertion, 'IRI')
        iri_element.text = property_value
        return annotation_assertion

    @staticmethod
    def add_instance(parent_node: ET.Element, parent_iri: str,
                     named_individual_iri: str):
        subclass = ET.SubElement(parent_node, 'ClassAssertion')
        ET.SubElement(subclass, 'Class', {'IRI': parent_iri})
        ET.SubElement(subclass, 'NamedIndividual', {'IRI': named_individual_iri})

    @abstractmethod
    def write(self, entities: dict, linked_entities: dict):
        raise NotImplementedError()


class QuestionInstanceXmlWriter(XmlWriter):
    def write(self, entities, linked_entities):
        for key in entities:
            entity = entities[key]

            for individual in entity.individuals:
                declaration = self.add_declaration(self.ontology, individual.iri,
                                                   declaration_type=DeclarationType.NAMED_INDIVIDUAL)
                self.add_literal_property(
                    parent_node=self.ontology,
                    iri=individual.iri,
                    property_attributes={'abbreviatedIRI': "rdfs:label"},
                    property_type={'xml:lang': "en"},
                    property_value=individual.label)
                self.add_instance(self.ontology, individual.parent_iri, individual.iri)

                ET.indent(declaration, '    ')

            ET.indent(self.ontology, '    ')


class RespondentInstanceXmlWriter(XmlWriter):
    def write(self, entities, linked_entities):
        for key in entities:
            individual = entities[key]

            declaration = self.add_declaration(self.ontology, individual.iri,
                                               declaration_type=DeclarationType.NAMED_INDIVIDUAL)
            self.add_literal_property(
                parent_node=self.ontology,
                iri=individual.iri,
                property_attributes={'abbreviatedIRI': "rdfs:label"},
                property_type={'xml:lang': "en"},
                property_value=individual.label)
            self.add_literal_property(
                parent_node=self.ontology,
                iri=individual.iri,
                property_attributes={'abbreviatedIRI': "dc:date"},
                property_type={'datatypeIRI': "http://www.w3.org/2001/XMLSchema#dateTime"},
                property_value=individual.date)
            self.add_object_property(
                parent_node=self.ontology, iri=individual.iri,
                property_attributes={'IRI': "#OWLObjectProperty_9061e019_7ee9_46f7_9e79_5533e912bbad"},
                property_value=individual.questionnaire_individual.iri)
            self.add_instance(self.ontology, individual.parent_iri, individual.iri)

            ET.indent(self.ontology, '    ')
            ET.indent(declaration, '    ')


class ResponseXmlWriter(XmlWriter):
    def write(self, entities, linked_entities):
        for entity in entities:
            declaration = self.add_declaration(self.ontology, entity.iri, declaration_type=DeclarationType.CLASS)
            declaration_annotation_property = self.add_declaration(
                self.ontology, '#hasSampleName', declaration_type=DeclarationType.ANNOTATION_PROPERTY)

            self.add_literal_property(
                parent_node=self.ontology,
                iri=entity.iri,
                property_attributes={'abbreviatedIRI': "rdfs:label"},
                property_type={'xml:lang': "en"},
                property_value=entity.label)

            self.add_object_property(
                parent_node=self.ontology, iri=entity.iri,
                property_attributes={'abbreviatedIRI': "dc:creator"},
                property_value=entity.creator)

            self.add_literal_property(
                parent_node=self.ontology,
                iri=entity.iri,
                property_attributes={'abbreviatedIRI': "dc:date"},
                property_type={'datatypeIRI': "http://www.w3.org/2001/XMLSchema#dateTime"},
                property_value=entity.date)

            self.add_subclass(
                parent_node=self.ontology,
                parent_iri=entity.parent_iri,
                child_iri=entity.iri)

            ET.indent(self.ontology, '    ')
            ET.indent(declaration, '    ')
            ET.indent(declaration_annotation_property, '    ')


class ResponseInstanceXmlWriter(XmlWriter):
    def write(self, entities, linked_entities):
        for individual in entities:
            self.add_declaration(self.ontology, individual.iri, declaration_type=DeclarationType.NAMED_INDIVIDUAL)
            self.add_literal_property(
                    parent_node=self.ontology,
                    iri=individual.iri,
                    property_attributes={'abbreviatedIRI': "rdfs:label"},
                    property_type={'xml:lang': "en"},
                    property_value=individual.label)
            self.add_literal_property(
                    parent_node=self.ontology,
                    iri=individual.iri,
                    property_attributes={'abbreviatedIRI': "dc:date"},
                    property_type={'datatypeIRI': "http://www.w3.org/2001/XMLSchema#dateTime"},
                    property_value=individual.date)
            self.add_literal_property(
                    parent_node=self.ontology,
                    iri=individual.iri,
                    property_attributes={'IRI': "#hasSampleName"},
                    property_type={'xml:lang': "en"},
                    property_value=individual.sample_name)
            self.add_object_property(
                parent_node=self.ontology, iri=individual.iri,
                property_attributes={'IRI': "#OWLObjectProperty_baf0ee7b_758b_4f47_af0e_332d8990debe"},
                property_value=individual.respondent_individual.iri)
            self.add_object_property(
                    parent_node=self.ontology, iri=individual.iri,
                    property_attributes={'IRI': individual.context_iri + "has_items_from_classification"},
                    property_value=individual.classification_instance.iri)
            self.add_object_property(
                    parent_node=self.ontology, iri=individual.iri,
                    property_attributes={'IRI': individual.context_iri + "COMFOCUS_00000000000000000037"},
                    property_value=individual.question_instance.iri)
            self.add_instance(self.ontology, individual.parent_iri, individual.iri)

        ET.indent(self.ontology, '    ')


class QuestionnaireInstanceXmlWriter(XmlWriter):
    def write(self, entities, linked_entities):
        for individual in entities:
            self.add_declaration(self.ontology, individual.iri, declaration_type=DeclarationType.NAMED_INDIVIDUAL)
            self.add_literal_property(
                    parent_node=self.ontology,
                    iri=individual.iri,
                    property_attributes={'abbreviatedIRI': "rdfs:label"},
                    property_type={'xml:lang': "en"},
                    property_value=individual.label)
            self.add_literal_property(
                    parent_node=self.ontology,
                    iri=individual.iri,
                    property_attributes={'abbreviatedIRI': "dc:date"},
                    property_type={'datatypeIRI': "http://www.w3.org/2001/XMLSchema#dateTime"},
                    property_value=individual.date)
            self.add_instance(self.ontology, individual.parent_iri, individual.iri)

        ET.indent(self.ontology, '    ')
