from typing import Any, Dict, Text

from rasa.nlu.emulators.emulator import Emulator
from rasa.shared.nlu.constants import (
    ENTITIES,
    ENTITY_ATTRIBUTE_TYPE,
    ENTITY_ATTRIBUTE_ROLE,
    ENTITY_ATTRIBUTE_VALUE,
    ENTITY_ATTRIBUTE_START,
    ENTITY_ATTRIBUTE_END,
    EXTRACTOR,
    INTENT_RANKING_KEY,
    TEXT,
    INTENT,
    INTENT_NAME_KEY,
    PREDICTED_CONFIDENCE_KEY,
)


class LUISEmulator(Emulator):
    """Emulates the response format of the LUIS Endpoint API v3.0 /predict endpoint.

    https://westcentralus.dev.cognitive.microsoft.com/docs/services/luis-endpoint-api-v3-0/
    https://docs.microsoft.com/en-us/azure/cognitive-services/LUIS/luis-concept-data-extraction?tabs=V3
    """

    def _intents(self, data: Dict[Text, Any]) -> Dict[Text, Any]:
        if data.get(INTENT_RANKING_KEY):
            return {
                intent[INTENT_NAME_KEY]: {"score": intent[PREDICTED_CONFIDENCE_KEY]}
                for intent in data[INTENT_RANKING_KEY]
            }

        top = data.get(INTENT)
        if not top:
            return {}

        return {top[INTENT_NAME_KEY]: {"score": top[PREDICTED_CONFIDENCE_KEY]}}

    def _entities(self, data: Dict[Text, Any]) -> Dict[Text, Any]:
        if ENTITIES not in data:
            return {}

        entities: Dict[Text, Any] = {"$instance": {}}
        for e in data[ENTITIES]:
            # LUIS API v3 uses entity roles instead of entity names
            # (it's possible because its roles are unique):
            # https://docs.microsoft.com/en-us/azure/cognitive-services/LUIS/luis-migration-api-v3#entity-role-name-instead-of-entity-name
            key = e.get(ENTITY_ATTRIBUTE_ROLE, e[ENTITY_ATTRIBUTE_TYPE])
            entities[key] = [e[ENTITY_ATTRIBUTE_VALUE]]

            entities["$instance"][key] = [
                {
                    "role": e.get(ENTITY_ATTRIBUTE_ROLE),
                    "type": e[ENTITY_ATTRIBUTE_TYPE],
                    "text": e[ENTITY_ATTRIBUTE_VALUE],
                    "startIndex": e.get(ENTITY_ATTRIBUTE_START),
                    "length": (e[ENTITY_ATTRIBUTE_END] - e[ENTITY_ATTRIBUTE_START])
                    if ENTITY_ATTRIBUTE_START in e and ENTITY_ATTRIBUTE_END in e
                    else None,
                    "score": e.get(PREDICTED_CONFIDENCE_KEY),
                    "modelType": e.get(EXTRACTOR),
                }
            ]
        return entities

    def normalise_response_json(self, data: Dict[Text, Any]) -> Dict[Text, Any]:
        """Transform response JSON to LUIS format.

        Args:
            data: input JSON data as a dictionary.

        Returns:
            The transformed input data.
        """
        top = data.get(INTENT)

        return {
            "query": data[TEXT],
            "prediction": {
                "normalizedQuery": data[TEXT],
                "topIntent": top[INTENT_NAME_KEY] if top else None,
                "intents": self._intents(data),
                "entities": self._entities(data),
            },
        }
