import logging
from typing import Dict, List
from chunkipy.language_detectors import BaseLanguageDetector, LangdetectLanguageDetector
from chunkipy.text_splitters.semantic.base_semantic_text_splitter import BaseSemanticTextSplitter


from chunkipy.utils import MissingDependencyError, import_dependencies

SPACY_INSTRUCTIONS = """
It seems you do not have the model installed.
Generally, it is sufficient to run

    python -m spacy download {model_name}

Check https://spacy.io/usage to see all the models available.
"""


class SpacySentenceTextSplitter(BaseSemanticTextSplitter):
    """Sentence splitter using spaCy for semantic text splitting.
    This class uses spaCy to split text into sentences based on the language detected in the text.
    It supports multiple languages by loading different spaCy models based on the detected language.
    If the language is not supported, it defaults to English.
    
    Attributes:
        models (Dict[str, str]): A dictionary mapping language codes to spaCy model names.
        models_map (Dict[str, str]): A dictionary mapping language codes to spaCy model names.
        text_limit (int): The maximum length of text to process at once. If None, DEFAULT_LIMIT from base class is applied.    
    """
    
    DEFAULT_LANG = "en"
    DEFAULT_MODELS_MAP = {
        "en": "en_core_web_sm"
    }

    def __init__(self, models_map: Dict[str, str] | None = None, text_limit: int = None,
                 language_detector: BaseLanguageDetector | None = None):
        """Initialize the spaCy-based sentence splitter.

        Args:
            models_map: Optional mapping from detected language code to spaCy
                model name.
            text_limit: Maximum input window processed at once.
            language_detector: Optional detector used to resolve the input
                language before selecting a spaCy model.
        """
        super().__init__(text_limit)
        self.models_map = dict(models_map or self.DEFAULT_MODELS_MAP)
        self.models = dict()
        self.language_detector = language_detector or LangdetectLanguageDetector()
        self._spacy_module = None

    def _get_spacy_module(self):
        """Return the spaCy module dependency.

        This method can be overridden by subclasses for advanced integrations.
        """
        if self._spacy_module is None:
            self._spacy_module = import_dependencies(
                extra="spacy",
                package_name="spacy"
            )
        return self._spacy_module

    def _load_model(self, lang: str):
        """Load and cache the spaCy model matching the detected language."""
        spacy = self._get_spacy_module()

        requested_lang = lang
        if lang not in self.models_map:
            lang = self.DEFAULT_LANG
            logging.warning(
                f"Language '{requested_lang}' not supported. Defaulting to '{self.DEFAULT_LANG}'. If you want to use a different language, please provide a valid model name in the 'models_map' parameter, e.g. models_map['it'] = 'it_core_news_sm'."
            )
        if lang not in self.models:
            try:
                self.models[lang] = spacy.load(self.models_map[lang])
            except OSError as e:
                raise MissingDependencyError(SPACY_INSTRUCTIONS.format(model_name=self.models_map[lang])) from e
        return self.models[lang]


    def _split(self, text: str) -> List[str]:
        """Split text into sentences using language detection plus a spaCy model."""
        if not text:
            return []

        lang = self.language_detector.detect(text)
        sentence_tokenizer = self._load_model(lang)
        with sentence_tokenizer.select_pipes(enable=["tok2vec", "parser", "senter"]):
            doc = sentence_tokenizer(text)
        return [s.text + " " for s in doc.sents]