"""Language detection utilities.

Script-based and dictionary-based language detection.
"""

import re
from typing import Optional


class ScriptDetector:
    """Detect language based on character scripts (CJK, Cyrillic, etc.)."""

    def __init__(self):
        self.cjk_ranges = [
            (0x4E00, 0x9FFF),   # CJK Unified Ideographs
            (0x3400, 0x4DBF),   # CJK Extension A
            (0x20000, 0x2A6DF), # CJK Extension B
            (0x2A700, 0x2B73F), # CJK Extension C
            (0x2B740, 0x2B81F), # CJK Extension D
            (0x3040, 0x309F),   # Hiragana
            (0x30A0, 0x30FF),   # Katakana
            (0xAC00, 0xD7AF),   # Hangul Syllables
        ]

    def detect_language(self, text: str) -> str:
        """
        Detect language using character scripts.

        Args:
            text: Text to analyze

        Returns:
            Language code (ko, ja, zh, ru, or en)
        """
        if not text or not text.strip():
            return 'unknown'

        cleaned_text = self.clean_text(text)
        if not cleaned_text:
            return 'unknown'

        if self.contains_cjk(text):
            if self.contains_korean(text):
                return 'ko'
            elif self.contains_japanese(text):
                return 'ja'
            else:
                return 'zh'

        if self.contains_cyrillic(text):
            return 'ru'

        return 'en'

    def contains_cjk(self, text: str) -> bool:
        """Check if text contains CJK characters."""
        for char in text:
            char_code = ord(char)
            for start, end in self.cjk_ranges:
                if start <= char_code <= end:
                    return True
        return False

    def contains_korean(self, text: str) -> bool:
        """Check if text contains Korean (Hangul) characters."""
        for char in text:
            char_code = ord(char)
            if 0xAC00 <= char_code <= 0xD7AF:
                return True
        return False

    def contains_japanese(self, text: str) -> bool:
        """Check if text contains Japanese (Hiragana/Katakana) characters."""
        for char in text:
            char_code = ord(char)
            if (0x3040 <= char_code <= 0x309F or
                0x30A0 <= char_code <= 0x30FF):
                return True
        return False

    def contains_cyrillic(self, text: str) -> bool:
        """Check if text contains Cyrillic characters."""
        for char in text:
            char_code = ord(char)
            if 0x0400 <= char_code <= 0x04FF:
                return True
        return False

    @staticmethod
    def clean_text(text: str) -> str:
        """Clean text for better language detection."""
        if not text:
            return ""

        text = ' '.join(text.split())
        text = re.sub(r'https?://\S+', '', text)
        text = re.sub(r'www\.\S+', '', text)
        text = re.sub(r'\b\d+\b', '', text)

        return text.strip()


class LanguageDetector:
    """Detect language using common word dictionaries."""

    def __init__(self):
        self.english_words = {
            'the', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of',
            'with', 'by', 'from', 'about', 'into', 'through', 'during', 'before',
            'after', 'above', 'below', 'up', 'down', 'out', 'off', 'over', 'under',
            'again', 'further', 'then', 'once', 'here', 'there', 'when', 'where',
            'why', 'how', 'all', 'any', 'both', 'each', 'few', 'more', 'most',
            'other', 'some', 'such', 'no', 'nor', 'not', 'only', 'own', 'same',
            'so', 'than', 'too', 'very', 'can', 'will', 'just', 'should', 'now'
        }

        self.russian_words = {
            'и', 'в', 'не', 'на', 'я', 'быть', 'он', 'с', 'что', 'а', 'по',
            'это', 'она', 'этот', 'к', 'но', 'они', 'мы', 'как', 'из', 'у',
            'который', 'то', 'за', 'свой', 'от', 'со', 'для', 'о', 'же', 'ты',
            'все', 'если', 'люди', 'время', 'так', 'его', 'жизнь', 'может',
            'год', 'только', 'над', 'еще', 'дом', 'после', 'большой', 'должен',
            'хотеть', 'между'
        }

        self.spanish_words = {
            'el', 'la', 'de', 'que', 'y', 'a', 'en', 'un', 'es', 'se', 'no',
            'te', 'lo', 'le', 'da', 'su', 'por', 'son', 'con', 'para', 'al',
            'del', 'los', 'las', 'una', 'como', 'pero', 'sus', 'ha', 'me', 'si',
            'sin', 'sobre', 'este', 'ya', 'entre', 'cuando', 'todo', 'esta',
            'ser', 'dos', 'también', 'fue', 'había', 'muy', 'hasta', 'desde',
            'está'
        }

        self.portuguese_words = {
            'o', 'a', 'de', 'que', 'e', 'do', 'da', 'em', 'um', 'para', 'é',
            'com', 'não', 'uma', 'os', 'no', 'se', 'na', 'por', 'mais', 'as',
            'dos', 'como', 'mas', 'foi', 'ao', 'ele', 'das', 'tem', 'à', 'seu',
            'sua', 'ou', 'ser', 'quando', 'muito', 'há', 'nos', 'já', 'está',
            'eu', 'também', 'só', 'pelo', 'pela', 'até', 'isso', 'ela', 'entre',
            'era', 'depois', 'sem', 'mesmo', 'aos', 'ter', 'seus', 'suas',
            'numa', 'pelos', 'pelas'
        }

    def detect_language(self, text: str) -> Optional[str]:
        """
        Detect language using common word dictionaries.

        Args:
            text: Text to analyze

        Returns:
            Language code or None if detection fails
        """
        if not text or len(text.strip()) < 3:
            return None

        text_lower = text.lower().strip()
        words = set(text_lower.split())

        en_matches = len(words & self.english_words)
        ru_matches = len(words & self.russian_words)
        es_matches = len(words & self.spanish_words)
        pt_matches = len(words & self.portuguese_words)

        max_matches = max(en_matches, ru_matches, es_matches, pt_matches)

        if max_matches == 0:
            return 'en'

        if en_matches == max_matches:
            return 'en'
        elif ru_matches == max_matches:
            return 'ru'
        elif es_matches == max_matches:
            return 'es'
        elif pt_matches == max_matches:
            return 'pt'

        return 'en'
