"""Main unmunch functionality - the core Punmunch class."""

from pathlib import Path
from typing import List, Optional, Set, Union

from .affix import AffixFile, FlagType
from .dictionary import Dictionary
from .expander import WordExpander, ExpansionOptions
from .exceptions import PunmunchError


class Punmunch:
    """
    Main class for unmunching hunspell dictionaries.
    
    This class provides both library functionality and the core logic
    for the command-line interface.
    """
    
    def __init__(self, expansion_options: Optional[ExpansionOptions] = None) -> None:
        self.affix_file: Optional[AffixFile] = None
        self.dictionary: Optional[Dictionary] = None
        self.expander: Optional[WordExpander] = None
        self.expansion_options = expansion_options or ExpansionOptions.conservative()
        
    def load_affix_file(self, path: Union[str, Path]) -> None:
        """Load a hunspell affix file."""
        self.affix_file = AffixFile.load(path)
        self._update_expander()
        
    def load_dictionary(self, path: Union[str, Path]) -> None:
        """Load a hunspell dictionary file."""
        if not self.affix_file:
            raise PunmunchError("Must load affix file before dictionary file")
        
        self.dictionary = Dictionary.load(path, self.affix_file.flag_type)
        self._update_expander()
        
    def _update_expander(self) -> None:
        """Update the word expander with current affix file and dictionary."""
        self.expander = WordExpander(self.affix_file, self.dictionary, self.expansion_options)
        
    def unmunch_all(self) -> List[str]:
        """
        Unmunch the entire dictionary - expand all root words to their surface forms.
        
        Returns:
            List of all expanded word forms, deduplicated and sorted.
        """
        if not self.dictionary or not self.expander:
            raise PunmunchError("Must load both affix file and dictionary")
        
        all_forms = set()
        
        for word, flags in self.dictionary.iter_entries():
            expanded = self.expander.expand_word(word, flags)
            all_forms.update(expanded)
        
        return sorted(all_forms)
    
    def expand_words(self, words: List[str], use_dictionary: bool = True, find_base: bool = False) -> List[str]:
        """
        Expand specific words using affix rules.

        Args:
            words: List of words to expand
            use_dictionary: If True, use dictionary flags for expansion.
                          If False, try all possible affix rules (--expand mode).
            find_base: If True, find base words first then expand from those.

        Returns:
            List of all expanded forms, deduplicated and sorted.
        """
        if not self.expander:
            raise PunmunchError("Must load affix file")

        all_forms = set()

        for word in words:
            if find_base and self.dictionary:
                # Find base words and expand from those
                expanded = self.expander.expand_from_base(word)
            elif use_dictionary and self.dictionary and self.dictionary.has_word(word):
                # Use dictionary flags for expansion
                expanded = self.expander.expand_dictionary_word(word)
            else:
                # Use simple expansion (try all possible rules)
                expanded = self.expander.expand_word_simple(word)

            all_forms.update(expanded)

        return sorted(all_forms)
    
    def expand_word(self, word: str, use_dictionary: bool = True, find_base: bool = False) -> List[str]:
        """
        Expand a single word.

        Args:
            word: The word to expand
            use_dictionary: If True, use dictionary flags. If False, try all rules.
            find_base: If True, find base words first then expand from those.

        Returns:
            List of expanded forms for the word, sorted.
        """
        return self.expand_words([word], use_dictionary, find_base)
    
    def is_valid_word(self, word: str) -> bool:
        """
        Check if a word is valid according to the dictionary.
        
        Returns True if the word exists in the dictionary or can be generated
        from a dictionary word using affix rules.
        """
        if not self.dictionary or not self.expander:
            return False
        
        # Check if word is directly in dictionary
        if self.dictionary.has_word(word):
            return True
        
        # Check if word can be generated from any dictionary word
        for dict_word, flags in self.dictionary.iter_entries():
            expanded = self.expander.expand_word(dict_word, flags)
            if word in expanded:
                return True
        
        return False
    
    def get_root_words(self, word: str) -> List[str]:
        """
        Find possible root words that could generate the given word.
        
        This is the reverse of expansion - finding which dictionary words
        could produce the given surface form.
        """
        if not self.dictionary or not self.expander:
            return []
        
        roots = []
        
        for dict_word, flags in self.dictionary.iter_entries():
            expanded = self.expander.expand_word(dict_word, flags)
            if word in expanded:
                roots.append(dict_word)
        
        return roots

    def find_base_words(self, word: str) -> List[str]:
        """
        Find possible base words that could generate the given inflected word.

        This uses morphological analysis to reverse-engineer the base forms
        that could produce the input word through affix rules.

        Args:
            word: The inflected word to analyze

        Returns:
            List of possible base words from the dictionary
        """
        if not self.expander:
            raise PunmunchError("Must load affix file and dictionary")

        return self.expander.find_base_words(word)
    
    def set_expansion_options(self, options: ExpansionOptions) -> None:
        """
        Set expansion options for morphological analysis.
        
        Args:
            options: ExpansionOptions instance (conservative, balanced, or deep)
        """
        self.expansion_options = options
        self._update_expander()
    
    def unmunch_all_deep(self) -> List[str]:
        """
        Unmunch the entire dictionary using deep expansion.
        
        This method uses advanced expansion features:
        - Deep recursive expansion
        - Intermediate flag processing  
        - Extensive cross-product combinations
        
        Returns:
            List of all expanded word forms with deep morphological analysis.
        """
        # Temporarily switch to deep expansion
        original_options = self.expansion_options
        self.set_expansion_options(ExpansionOptions.deep())
        
        try:
            return self.unmunch_all()
        finally:
            # Restore original options
            self.set_expansion_options(original_options)
    
    def unmunch_all_balanced(self) -> List[str]:
        """
        Unmunch the entire dictionary using balanced expansion.
        
        This provides a good compromise between completeness and performance.
        
        Returns:
            List of all expanded word forms with balanced morphological analysis.
        """
        # Temporarily switch to balanced expansion
        original_options = self.expansion_options
        self.set_expansion_options(ExpansionOptions.balanced())
        
        try:
            return self.unmunch_all()
        finally:
            # Restore original options
            self.set_expansion_options(original_options)
    
    def expand_words_deep(self, words: List[str], use_dictionary: bool = True) -> List[str]:
        """
        Expand words using deep recursive expansion with all advanced features.
        
        Args:
            words: List of words to expand
            use_dictionary: If True, use dictionary flags. If False, try all rules.
        
        Returns:
            List of expanded forms using deep morphological analysis.
        """
        # Temporarily switch to deep expansion
        original_options = self.expansion_options
        self.set_expansion_options(ExpansionOptions.deep())
        
        try:
            return self.expand_words(words, use_dictionary)
        finally:
            # Restore original options
            self.set_expansion_options(original_options)
    
    def expand_words_balanced(self, words: List[str], use_dictionary: bool = True) -> List[str]:
        """
        Expand words using balanced expansion.
        
        Args:
            words: List of words to expand
            use_dictionary: If True, use dictionary flags. If False, try all rules.
        
        Returns:
            List of expanded forms using balanced morphological analysis.
        """
        # Temporarily switch to balanced expansion
        original_options = self.expansion_options
        self.set_expansion_options(ExpansionOptions.balanced())
        
        try:
            return self.expand_words(words, use_dictionary)
        finally:
            # Restore original options
            self.set_expansion_options(original_options)

    def get_statistics(self) -> dict:
        """Get statistics about the loaded dictionary and affix file."""
        stats = {
            'affix_file_loaded': self.affix_file is not None,
            'dictionary_loaded': self.dictionary is not None,
            'flag_type': self.affix_file.flag_type.value if self.affix_file else None,
            'encoding': self.affix_file.encoding if self.affix_file else None,
            'word_count': self.dictionary.get_word_count() if self.dictionary else 0,
            'prefix_flags': len(self.affix_file.prefixes) if self.affix_file else 0,
            'suffix_flags': len(self.affix_file.suffixes) if self.affix_file else 0,
            'total_prefix_rules': sum(len(rules) for rules in self.affix_file.prefixes.values()) if self.affix_file else 0,
            'total_suffix_rules': sum(len(rules) for rules in self.affix_file.suffixes.values()) if self.affix_file else 0,
            'expansion_mode': 'deep' if self.expansion_options.enable_deep_recursion else 'conservative',
            'max_recursion_depth': self.expansion_options.max_recursion_depth,
            'process_intermediate_flags': self.expansion_options.process_intermediate_flags,
            'extensive_cross_products': self.expansion_options.extensive_cross_products,
        }
        
        return stats