"""Dictionary file parser for hunspell .dic files."""

from pathlib import Path
from typing import Dict, Iterator, List, Optional, Tuple, Union

from .affix import FlagType
from .exceptions import DictionaryError


class Dictionary:
    """Parser and container for hunspell dictionary files."""
    
    def __init__(self, flag_type: FlagType = FlagType.CHAR) -> None:
        self.flag_type = flag_type
        self.entries: Dict[str, List[str]] = {}
        self.word_count: int = 0
        
    @classmethod
    def load(cls, path: Union[str, Path], flag_type: FlagType = FlagType.CHAR) -> "Dictionary":
        """Load and parse a dictionary file."""
        dictionary = cls(flag_type)
        path = Path(path)
        
        if not path.exists():
            raise DictionaryError(f"Dictionary file not found: {path}")
        
        try:
            with open(path, 'r', encoding='utf-8', errors='ignore') as f:
                dictionary._parse_file(f.readlines())
        except UnicodeDecodeError:
            # Try with latin-1 encoding as fallback
            try:
                with open(path, 'r', encoding='latin-1') as f:
                    dictionary._parse_file(f.readlines())
            except Exception as e:
                raise DictionaryError(f"Could not read dictionary file {path}: {e}")
        except Exception as e:
            raise DictionaryError(f"Error loading dictionary file {path}: {e}")
        
        return dictionary
    
    def _parse_file(self, lines: List[str]) -> None:
        """Parse the lines of a dictionary file."""
        line_num = 0
        expected_count: Optional[int] = None
        
        for line in lines:
            line_num += 1
            line = line.strip()
            
            # Skip empty lines and comments (lines starting with tab are comments in .dic files)
            if not line or line.startswith('\t'):
                continue
            
            try:
                # First non-comment line should be the word count
                if expected_count is None:
                    try:
                        expected_count = int(line)
                        continue
                    except ValueError:
                        # Some dictionary files don't have word count, treat as first word
                        expected_count = 0
                
                # Parse word entry
                self._parse_entry(line, line_num)
                
            except Exception as e:
                raise DictionaryError(f"Parse error: {e}", line_num, line)
        
        self.word_count = len(self.entries)
    
    def _parse_entry(self, line: str, line_num: int) -> None:
        """Parse a single dictionary entry."""
        # Dictionary entries are in format: word/flags
        # or just: word (if no flags)
        
        if '/' in line:
            word, flag_string = line.split('/', 1)
            # Remove any additional comments after flags
            if ' ' in flag_string:
                flag_string = flag_string.split()[0]
            flags = self._parse_flags(flag_string)
        else:
            word = line
            # Remove any comments
            if ' ' in word:
                word = word.split()[0]
            flags = []
        
        if not word:
            raise DictionaryError("Empty word", line_num, line)
        
        # Store the entry
        self.entries[word] = flags
    
    def _parse_flags(self, flag_string: str) -> List[str]:
        """Parse a flag string according to the flag type."""
        if not flag_string:
            return []
        
        flags = []
        
        if self.flag_type == FlagType.CHAR:
            # Each character is a flag
            flags = list(flag_string)
        elif self.flag_type == FlagType.LONG:
            # Check if this looks like numeric aliases (common with long flag affix files)
            if flag_string.isdigit():
                # Single numeric alias like "360"
                flags = [flag_string]
            elif ',' in flag_string and all(f.strip().isdigit() for f in flag_string.split(',') if f.strip()):
                # Comma-separated numeric aliases like "360,273"
                flags = [f.strip() for f in flag_string.split(',') if f.strip()]
            else:
                # Two characters per flag (traditional long flags)
                for i in range(0, len(flag_string), 2):
                    if i + 1 < len(flag_string):
                        flags.append(flag_string[i:i+2])
                    else:
                        flags.append(flag_string[i])
        elif self.flag_type == FlagType.NUM:
            # Comma-separated numeric flags
            flags = [f.strip() for f in flag_string.split(',') if f.strip()]
        
        return flags
    
    def get_flags(self, word: str) -> List[str]:
        """Get the flags for a specific word."""
        return self.entries.get(word, [])
    
    def has_word(self, word: str) -> bool:
        """Check if a word exists in the dictionary."""
        return word in self.entries
    
    def has_flag(self, word: str, flag: str) -> bool:
        """Check if a word has a specific flag."""
        return flag in self.entries.get(word, [])
    
    def get_words_with_flag(self, flag: str) -> List[str]:
        """Get all words that have a specific flag."""
        return [word for word, flags in self.entries.items() if flag in flags]
    
    def iter_entries(self) -> Iterator[Tuple[str, List[str]]]:
        """Iterate over all dictionary entries."""
        yield from self.entries.items()
    
    def get_all_words(self) -> List[str]:
        """Get all words in the dictionary."""
        return list(self.entries.keys())
    
    def get_word_count(self) -> int:
        """Get the number of words in the dictionary."""
        return len(self.entries)
    
    def add_word(self, word: str, flags: Optional[List[str]] = None) -> None:
        """Add a word to the dictionary."""
        if flags is None:
            flags = []
        self.entries[word] = flags
    
    def remove_word(self, word: str) -> bool:
        """Remove a word from the dictionary. Returns True if word was present."""
        if word in self.entries:
            del self.entries[word]
            return True
        return False
    
    def clear(self) -> None:
        """Clear all entries from the dictionary."""
        self.entries.clear()
        self.word_count = 0