# !/usr/bin/env python
# -*- coding: UTF-8 -*-
""" Find spaCy NER (Named Entity Recognition) Types """


from baseblock import BaseObject


class FindNER(BaseObject):
    """ Find spaCy NER (Named Entity Recognition) Types """

    def __init__(self,
                 d_ner_depth: dict,
                 d_ner_taxo: dict,
                 d_spacy_ner: dict,
                 d_graffl_ner: dict):
        """ Change Log

        Created:
            13-Oct-2021
            craigtrim@gmail.com
            *   https://github.com/grafflr/graffl-core/issues/36
        Updated:
            14-Oct-2021
            craigtrim@gmail.com
            *   defect fixing updates
                https://github.com/grafflr/graffl-core/issues/42
        Updated:
            22-Oct-2021
            craigtrim@gmail.com
            *   replace manual depth dictionary with autogenerated values
                https://github.com/grafflr/graffl-core/issues/89#issuecomment-950061167
        Updated:
            27-Oct-2021
            craigtrim@gmail.com
            *   refactor functions into domain components in pursuit of
                https://github.com/grafflr/graffl-core/issues/94
        Updated:
            1-Feb-2022
            craigtrim@gmail.com
            *   make ontology param consistent; permit multiple values
                https://github.com/grafflr/graffl-core/issues/135#issuecomment-1027468040
        Updated:
            26-May-2022
            craigtrim@gmail.com
            *   treat 'ontologies' param as a list
                https://github.com/grafflr/deepnlu/issues/7
        Updated:
            27-May-2022
            craigtrim@gmail.com
            *   migrated to 'owlblock' in pursuit of
                https://github.com/grafflr/deepnlu/issues/13

        Args:
            ontologies (list): one-or-more Ontology models to use in processing
        """
        BaseObject.__init__(self, __name__)
        self._d_ner_depth = d_ner_depth
        self._d_ner_taxo = d_ner_taxo
        self._d_spacy_ner = d_spacy_ner
        self._d_graffl_ner = d_graffl_ner

    def _depth(self,
               ner: str) -> int or None:
        if ner in self._d_ner_depth:
            return self._d_ner_depth[ner][0]
        return None

    @staticmethod
    def _cleanse(input_text: str) -> str:
        if '_' in input_text:
            input_text = input_text.replace('_', ' ')
        return input_text

    def _find_spacy_ner(self,
                        input_text: str) -> list or None:

        # this is neither wrong nor unusual
        # it just means spaCy NERs were not defined in the OWL
        if not self._d_spacy_ner or not len(self._d_spacy_ner):
            return None

        input_text = self._cleanse(input_text)
        if input_text not in self._d_spacy_ner:
            return None

        return self._d_spacy_ner[input_text]

    def _find_graffl_ner(self,
                         input_text: str) -> list or None:

        # this is neither wrong nor unusual
        # it just means Graffl NERs were not defined in the OWL
        if not self._d_graffl_ner or not len(self._d_graffl_ner):
            return None

        input_text = self._cleanse(input_text)
        if input_text not in self._d_graffl_ner:
            return None

        return self._d_graffl_ner[input_text]

    def find_ner(self,
                 input_text: str) -> str or None:
        """Find NER from either the GRAFFL or spaCy taxonomy

        Reference:
            https://github.com/grafflr/graffl-core/issues/55#issuecomment-944572462

        Args:
            input_text (str): any input entity

        Returns:
            str or None: NER
        """
        input_text = self._cleanse(input_text)

        ners = []

        graffl_ners = self._find_graffl_ner(input_text)
        if graffl_ners:
            [ners.append(x) for x in graffl_ners]

        spacy_ners = self._find_spacy_ner(input_text)
        if spacy_ners:
            [ners.append(x) for x in spacy_ners]

        if not len(ners):
            ners.append('NER')

        if len(ners) == 1:
            return ners[0]

        d_depth = {self._depth(x): x for x in ners}
        return d_depth[max(d_depth)]
