import json
from copy import copy
from typing import Any, Dict, List, Text, Tuple

import numpy
import requests
from rich.console import Console

from neuralspace.constants import (
    AUTHORIZATION,
    COMMON_HEADERS,
    COUNT,
    DATA,
    ENTITIES,
    ENTITY,
    ENTITY_TYPE,
    EXAMPLES,
    FILTER,
    LANGUAGE,
    LIST_ENTITY_URL,
    LOOKUP,
    PAGE_NUMBER,
    PAGE_SIZE,
    PRE_TRAINED,
    REGEX,
    SEARCH,
    neuralspace_url,
)
from neuralspace.utils import get_auth_token, is_success_status

console = Console()


def convert_files(convert, rasa_data):
    lookup_data = convert.lookup_converter(rasa_data.lookup_tables)
    regex_data = convert.regex_converter(rasa_data.regex_features)
    synonym_data = convert.synonym_converter(rasa_data.entity_synonyms)
    training_data = convert.training_data_converter(rasa_data)
    return lookup_data, regex_data, synonym_data, training_data


def __replace_entities_with_mapping(
    entities: List[Dict[Text, Any]], entity_mapping: Dict[Text, Text]
):
    for entity_value in entities:
        if entity_value[ENTITY] in entity_mapping:
            entity_value[ENTITY] = entity_mapping[entity_value[ENTITY]]


def map_entities(nlu_data: List[Dict[Text, Any]], entity_mapping: Dict[Text, Text]):
    for example in nlu_data:
        entities = example.get(ENTITIES, [])
        if entities:
            __replace_entities_with_mapping(entities, entity_mapping)
    return nlu_data


def get_entity_list_from_remote(
    entity_type: Text,
    language: Text,
    keyword_search: Text = "",
    page_size: int = 1,
    page_number: int = 10,
) -> Tuple[List[Dict[Text, Any]], int]:
    payload = {
        FILTER: {ENTITY_TYPE: entity_type, LANGUAGE: language},
        SEARCH: keyword_search,
        PAGE_NUMBER: page_number,
        PAGE_SIZE: page_size,
    }
    HEADERS = copy(COMMON_HEADERS)
    HEADERS[AUTHORIZATION] = get_auth_token()

    response = requests.request(
        "POST",
        f"{neuralspace_url()}/{LIST_ENTITY_URL}",
        headers=HEADERS,
        data=json.dumps(payload, ensure_ascii=False),
    )
    json_response = response.json(encoding="utf-8")

    if not is_success_status(response.status_code):
        raise Exception(
            f"Error while fetching entities. "
            f"Error message: {json.dumps(json_response, indent=4)}"
        )
    return json_response[DATA][ENTITIES], json_response[DATA][COUNT]


def auto_tag_pretrained_entities(data: List[Dict[Text, Any]], language: Text):
    sample_pretrained_entities, count = get_entity_list_from_remote(
        entity_type=PRE_TRAINED, language=language
    )
    all_pretrained_entities, _ = get_entity_list_from_remote(
        entity_type=PRE_TRAINED,
        language=language,
        page_number=1,
        page_size=count,
    )
    all_pretrained_entity_names = {
        entity[ENTITY]: entity[ENTITY] for entity in all_pretrained_entities
    }
    for example in data:
        entities = example.get(ENTITIES, [])
        for entity_value in entities:
            if entity_value[ENTITY] in all_pretrained_entity_names:
                entity_value[ENTITY] = all_pretrained_entity_names[entity_value[ENTITY]]
                entity_value[ENTITY_TYPE] = PRE_TRAINED
    return data


def auto_tag_lookup(
    data: List[Dict[Text, Any]], lookup_entities: List[Dict[Text, Any]]
):
    all_lookup_entity_names = {
        entity[ENTITY]: entity[ENTITY] for entity in lookup_entities
    }
    for example in data:
        entities = example.get(ENTITIES, [])
        for entity_value in entities:
            if (
                entity_value[ENTITY] in all_lookup_entity_names
                and ENTITY_TYPE not in entity_value
            ):
                entity_value[ENTITY] = all_lookup_entity_names[entity_value[ENTITY]]
                entity_value[ENTITY_TYPE] = LOOKUP
    return data


def auto_tag_regex(data: List[Dict[Text, Any]], regex_entities: List[Dict[Text, Any]]):
    all_regex_entity_names = {
        entity[ENTITY]: entity[ENTITY] for entity in regex_entities
    }
    for example in data:
        entities = example.get(ENTITIES, [])
        for entity_value in entities:
            if (
                entity_value[ENTITY] in all_regex_entity_names
                and ENTITY_TYPE not in entity_value
            ):
                entity_value[ENTITY] = all_regex_entity_names[entity_value[ENTITY]]
                entity_value[ENTITY_TYPE] = REGEX
    return data


def remove_nlu_duplicates(
    data_to_clear_duplicates: List[Dict[Text, Any]]
) -> List[Dict[Text, Any]]:
    numpy_format_for_the_list: numpy.array = numpy.array(data_to_clear_duplicates)
    for data in data_to_clear_duplicates:
        indexes_where_the_data_present = numpy.where(data == numpy_format_for_the_list)
        for index in indexes_where_the_data_present[0][1:]:
            numpy_format_for_the_list = numpy.delete(numpy_format_for_the_list, index)
    return list(numpy_format_for_the_list)


def remove_entity_duplicates(
    entities: List[Dict[Text, Any]],
) -> List[Dict[Text, Any]]:
    for entity in entities:
        entity[EXAMPLES] = sorted(list(set(entity[EXAMPLES])), key=str.casefold)
    return entities
