"""
Main class to detect the language of a text.
"""

import re
import unicodedata
from typing import List

import emoji

from .constants import FASTTEXT_WEIGHTS, LANGUAGES
from .dict_detector import DictDetector
from .fasttext_detector import FastTextDetector
from .lingua_detector import LinguaDetector
from .output_formater import OutputFormater


class Detector:
    """Main class to detect the language of a text."""

    def __init__(
        self,
        reliability_threshold: float = 0.35,
        filter_threshold: float = 0.3,
        equal_traitements: bool = False,
    ):
        """Initializes the Detector object.

        Args:
            reliability_threshold (float, optional): The threshold above which the language is considered reliable. Defaults to 0.5.
            filter_threshold (float, optional): The threshold below which the language is filtered out. Defaults to 0.35.
            equal_traitements (bool, optional): If True, all detectors are considered equally important. Defaults to False (more focus on english and popular languages).
        """

        self._reliability_threshold = reliability_threshold
        self._filter_threshold = filter_threshold
        self._equal_traitements = equal_traitements

        self._detectors = []
        self._detectors.append(DictDetector())
        self._detectors.append(LinguaDetector())

        for i, weight in enumerate(FASTTEXT_WEIGHTS):
            # The first detector has a weight of 1, the others have a weight of 0.5
            importance = 1 if i == 0 else 0.5

            self._detectors.append(
                FastTextDetector(weight, weighted_reliability=importance)
            )

        # Count the number of detectors for each language
        self._detectors_for_each_language = {}
        for detector in self._detectors:
            for lang in detector.supported_languages:
                if lang not in self._detectors_for_each_language:
                    self._detectors_for_each_language[lang] = 0

                self._detectors_for_each_language[lang] += 1

        self._output_format = OutputFormater(
            self._detectors_for_each_language,
            self._reliability_threshold,
            self._filter_threshold,
            self._equal_traitements,
        )

    def _clean_text(self, text: str) -> str:
        """Cleans the text by removing special characters, emojis, and numbers.

        Args:
            text (str): The text to clean.

        Returns:
            str: The cleaned text.
        """

        text = text[:1000]  # limit the text to 1000 characters for performance reasons
        text = re.sub(
            r"[!\"#$%&\'()*+,\-.\/:;<=>?@\[\\\]^_`{|}~ ]{2,}",
            lambda match: match.group()[0] + (" " if " " in match.group() else ""),
            text,
        )
        text = re.sub(r"(\w)\1{2,}", r"\1\1", text)
        text = re.sub(r"\d+|\^", "", text)

        s = "@#$<>[]*_-~&%+/§{}=\|:▬"
        for char in text:
            if char in s:
                text = text.replace(char, "", 1)

        text = emoji.replace_emoji(text, replace="").strip()

        return unicodedata.normalize("NFKC", text.replace("\n", ""))[:200]

    def detect(self, text: str) -> List[dict[str, float, bool]]:
        """Detects the language of a text.

        Args:
            text (str): The text to detect.

        Returns:
            Output: The detected languages and their scores.
        """

        text = self._clean_text(text)

        if text == "":
            return []

        predictions = [detector.detect(text) for detector in self._detectors]

        # If the language is detected with a score of 1 from the dictionary detector, return it
        if len(predictions[0]) != 0:
            if predictions[0][0][1] == 1:
                return self._output_format.format_predictions(
                    [predictions[0]],
                    apply_end_score_normalization=False,  # because only one detector
                )

        return self._output_format.format_predictions(predictions)

    @property
    def supported_languages(self) -> dict:
        """Returns the supported languages with their ISO 639-1 code.

        Returns:
            dict: The supported languages.
        """

        return {
            lang: str(LANGUAGES.from_iso_code_639_1_str(lang))
            for lang in self._detectors_for_each_language.keys()
        }
