"""
Text Normalization Module

This module provides comprehensive text normalization capabilities for the
Semantica framework, enabling standardization of text content across various
formats and encodings.

Key Features:
    - Text cleaning and sanitization
    - Unicode normalization (NFC, NFD, NFKC, NFKD)
    - Case normalization (lower, upper, title, preserve)
    - Whitespace handling (normalization, line breaks, indentation)
    - Special character processing (punctuation, diacritics)
    - Format standardization

Main Classes:
    - TextNormalizer: Main text normalization coordinator
    - UnicodeNormalizer: Unicode processing engine
    - WhitespaceNormalizer: Whitespace handling engine
    - SpecialCharacterProcessor: Special character processing engine

Example Usage:
    >>> from semantica.normalize import TextNormalizer
    >>> normalizer = TextNormalizer()
    >>> normalized = normalizer.normalize_text("Hello   World", case="lower")
    >>> cleaned = normalizer.clean_text(text, remove_html=True)

Author: Semantica Contributors
License: MIT
"""

import re
import unicodedata
from typing import Any, Dict, List, Optional

from ..utils.exceptions import ValidationError, ProcessingError
from ..utils.logging import get_logger
from ..utils.progress_tracker import get_progress_tracker
from .text_cleaner import TextCleaner


class TextNormalizer:
    """
    Text normalization and cleaning coordinator.
    
    This class provides comprehensive text normalization capabilities, coordinating
    Unicode normalization, whitespace handling, special character processing,
    and text cleaning.
    
    Features:
        - Text cleaning and sanitization
        - Unicode normalization
        - Case normalization
        - Whitespace handling
        - Special character processing
        - Format standardization
        - Batch processing
    
    Example Usage:
        >>> normalizer = TextNormalizer()
        >>> normalized = normalizer.normalize_text("Hello   World", case="lower")
        >>> cleaned = normalizer.clean_text(text, remove_html=True)
        >>> batch = normalizer.process_batch(texts)
    """
    
    def __init__(self, config: Optional[Dict[str, Any]] = None, **kwargs):
        """
        Initialize text normalizer.
        
        Sets up the normalizer with text cleaner, Unicode normalizer, whitespace
        normalizer, and special character processor components.
        
        Args:
            config: Configuration dictionary (optional)
            **kwargs: Additional configuration options (merged into config)
        """
        self.logger = get_logger("text_normalizer")
        self.config = config or {}
        self.config.update(kwargs)
        
        self.text_cleaner = TextCleaner(**self.config)
        self.unicode_normalizer = UnicodeNormalizer(**self.config)
        self.whitespace_normalizer = WhitespaceNormalizer(**self.config)
        self.special_char_processor = SpecialCharacterProcessor(**self.config)
        
        # Initialize progress tracker
        self.progress_tracker = get_progress_tracker()
        
        self.logger.debug("Text normalizer initialized")
    
    def normalize_text(
        self,
        text: str,
        unicode_form: str = "NFC",
        case: str = "preserve",
        normalize_diacritics: bool = False,
        line_break_type: str = "unix",
        **options
    ) -> str:
        """
        Normalize text content.
        
        This method performs comprehensive text normalization by applying
        Unicode normalization, whitespace normalization, special character
        processing, and case normalization in sequence.
        
        Args:
            text: Input text to normalize
            unicode_form: Unicode normalization form (default: "NFC"):
                - "NFC": Canonical composition
                - "NFD": Canonical decomposition
                - "NFKC": Compatibility composition
                - "NFKD": Compatibility decomposition
            case: Case normalization type (default: "preserve"):
                - "preserve": Keep original case
                - "lower": Convert to lowercase
                - "upper": Convert to uppercase
                - "title": Convert to title case
            normalize_diacritics: Whether to normalize diacritics (default: False)
            line_break_type: Line break type for whitespace normalization
                           (default: "unix")
            **options: Additional normalization options (passed to sub-processors)
        
        Returns:
            str: Normalized text
        """
        tracking_id = self.progress_tracker.start_tracking(
            message="Semantica: Normalizing text",
            file=None
        )
        try:
            if not text:
                self.progress_tracker.stop_tracking(tracking_id, status="completed")
                return ""
            
            normalized = text
            
            # Unicode normalization
            normalized = self.unicode_normalizer.normalize_unicode(normalized, form=unicode_form)
            
            # Whitespace normalization
            normalized = self.whitespace_normalizer.normalize_whitespace(
                normalized, line_break_type=line_break_type, **options
            )
            
            # Special character processing
        normalized = self.special_char_processor.process_special_chars(
            normalized, normalize_diacritics=normalize_diacritics, **options
        )
        
        # Case normalization
        if case == "lower":
            normalized = normalized.lower()
        elif case == "upper":
            normalized = normalized.upper()
        elif case == "title":
            normalized = normalized.title()
        
        return normalized.strip()
    
    def clean_text(self, text: str, **options) -> str:
        """
        Clean and sanitize text content.
        
        This method delegates to the text cleaner for comprehensive text cleaning.
        
        Args:
            text: Input text to clean
            **options: Cleaning options (passed to TextCleaner.clean)
        
        Returns:
            str: Cleaned text
        """
        return self.text_cleaner.clean(text, **options)
    
    def standardize_format(
        self,
        text: str,
        format_type: str = "standard"
    ) -> str:
        """
        Standardize text format.
        
        This method standardizes text format by applying format-specific
        transformations (compact, preserve, or standard).
        
        Args:
            text: Input text to standardize
            format_type: Format type (default: "standard"):
                - "standard": Apply standard formatting
                - "compact": Remove extra whitespace
                - "preserve": Preserve original formatting
        
        Returns:
            str: Formatted text
        """
        if format_type == "compact":
            # Remove extra whitespace
            text = re.sub(r'\s+', ' ', text)
        elif format_type == "preserve":
            # Preserve original formatting
            pass
        
        return text.strip()
    
    def process_batch(
        self,
        texts: List[str],
        **options
    ) -> List[str]:
        """
        Process multiple texts in batch.
        
        This method processes multiple texts in batch, applying the same
        normalization operations to each text.
        
        Args:
            texts: List of texts to process
            **options: Processing options (passed to normalize_text method)
        
        Returns:
            list: List of normalized texts (one per input text)
        """
        return [self.normalize_text(text, **options) for text in texts]


class UnicodeNormalizer:
    """
    Unicode normalization engine.
    
    This class provides Unicode normalization capabilities, handling different
    Unicode forms and special character processing.
    
    Features:
        - Unicode normalization (NFC, NFD, NFKC, NFKD)
        - Character encoding conversion
        - Special Unicode character processing
        - Support for various scripts and languages
    
    Example Usage:
        >>> normalizer = UnicodeNormalizer()
        >>> normalized = normalizer.normalize_unicode(text, form="NFC")
        >>> processed = normalizer.process_special_chars(text)
    """
    
    def __init__(self, **config):
        """
        Initialize Unicode normalizer.
        
        Sets up the normalizer with configuration options.
        
        Args:
            **config: Configuration options (currently unused)
        """
        self.logger = get_logger("unicode_normalizer")
        self.config = config
        
        self.logger.debug("Unicode normalizer initialized")
    
    def normalize_unicode(self, text: str, form: str = "NFC") -> str:
        """
        Normalize Unicode text.
        
        This method normalizes Unicode characters using the specified
        normalization form.
        
        Args:
            text: Input text to normalize
            form: Unicode normalization form (default: "NFC"):
                - "NFC": Canonical composition
                - "NFD": Canonical decomposition
                - "NFKC": Compatibility composition
                - "NFKD": Compatibility decomposition
        
        Returns:
            str: Unicode-normalized text (returns original text if normalization fails)
        """
        if not text:
            return ""
        
        try:
            return unicodedata.normalize(form, text)
        except Exception as e:
            self.logger.warning(f"Unicode normalization failed: {e}")
            return text
    
    def handle_encoding(
        self,
        text: str,
        source_encoding: str,
        target_encoding: str = "utf-8"
    ) -> str:
        """
        Handle text encoding conversion.
        
        This method converts text from one encoding to another, handling
        both string and bytes input.
        
        Args:
            text: Input text (string or bytes)
            source_encoding: Source encoding name (e.g., "latin-1", "cp1252")
            target_encoding: Target encoding name (default: "utf-8")
        
        Returns:
            str: Converted text in target encoding (falls back to UTF-8 with
                 error replacement if conversion fails)
        """
        if isinstance(text, bytes):
            try:
                return text.decode(source_encoding).encode(target_encoding).decode(target_encoding)
            except Exception:
                return text.decode('utf-8', errors='replace')
        else:
            return text
    
    def process_special_chars(self, text: str) -> str:
        """
        Process special Unicode characters.
        
        This method replaces common special Unicode characters (smart quotes,
        dashes, ellipsis) with their ASCII equivalents.
        
        Args:
            text: Input text containing special Unicode characters
        
        Returns:
            str: Text with special Unicode characters replaced with ASCII equivalents
        """
        # Replace common special characters
        replacements = {
            '\u2018': "'",  # Left single quotation mark
            '\u2019': "'",  # Right single quotation mark
            '\u201C': '"',  # Left double quotation mark
            '\u201D': '"',  # Right double quotation mark
            '\u2013': '-',  # En dash
            '\u2014': '--',  # Em dash
            '\u2026': '...',  # Horizontal ellipsis
        }
        
        for old, new in replacements.items():
            text = text.replace(old, new)
        
        return text


class WhitespaceNormalizer:
    """
    Whitespace normalization engine.
    
    This class provides whitespace normalization capabilities, handling
    different whitespace types, line breaks, and indentation.
    
    Features:
        - Whitespace character normalization
        - Line break handling (Unix, Windows, Mac)
        - Indentation processing
        - Multiple whitespace collapse
    
    Example Usage:
        >>> normalizer = WhitespaceNormalizer()
        >>> normalized = normalizer.normalize_whitespace(text, line_break_type="unix")
        >>> processed = normalizer.process_indentation(text, indent_type="spaces")
    """
    
    def __init__(self, **config):
        """
        Initialize whitespace normalizer.
        
        Sets up the normalizer with configuration options.
        
        Args:
            **config: Configuration options (currently unused)
        """
        self.logger = get_logger("whitespace_normalizer")
        self.config = config
        
        self.logger.debug("Whitespace normalizer initialized")
    
    def normalize_whitespace(
        self,
        text: str,
        line_break_type: str = "unix",
        **options
    ) -> str:
        """
        Normalize whitespace in text.
        
        This method normalizes whitespace by replacing tabs with spaces,
        normalizing line breaks, and collapsing multiple spaces.
        
        Args:
            text: Input text with potentially irregular whitespace
            line_break_type: Line break type (default: "unix"):
                - "unix": Unix-style line breaks (\n)
                - "windows": Windows-style line breaks (\r\n)
            **options: Additional normalization options (unused)
        
        Returns:
            str: Text with normalized whitespace
        """
        if not text:
            return ""
        
        # Replace tabs with spaces
        text = text.replace('\t', ' ')
        
        # Normalize line breaks
        text = self.handle_line_breaks(text, line_break_type)
        
        # Remove excessive whitespace
        text = re.sub(r' +', ' ', text)
        text = re.sub(r'\n\s*\n', '\n\n', text)  # Normalize multiple newlines
        
        return text.strip()
    
    def handle_line_breaks(
        self,
        text: str,
        line_break_type: str = "unix"
    ) -> str:
        """
        Normalize line breaks.
        
        This method normalizes line breaks to the specified type (Unix, Windows,
        or Mac).
        
        Args:
            text: Input text with potentially mixed line breaks
            line_break_type: Line break type (default: "unix"):
                - "unix": Unix-style (\n)
                - "windows": Windows-style (\r\n)
                - "mac": Mac-style (\r)
        
        Returns:
            str: Text with normalized line breaks
        """
        if line_break_type == "unix":
            text = text.replace('\r\n', '\n')
            text = text.replace('\r', '\n')
        elif line_break_type == "windows":
            text = text.replace('\r\n', '\r\n')
            text = text.replace('\r', '\r\n')
            text = text.replace('\n', '\r\n')
        
        return text
    
    def process_indentation(
        self,
        text: str,
        indent_type: str = "spaces"
    ) -> str:
        """
        Normalize text indentation.
        
        This method normalizes text indentation by converting between tabs
        and spaces.
        
        Args:
            text: Input text with potentially mixed indentation
            indent_type: Indentation type (default: "spaces"):
                - "spaces": Convert tabs to 4 spaces
                - "tabs": Convert 4 spaces to tabs
        
        Returns:
            str: Text with normalized indentation
        """
        if indent_type == "spaces":
            text = text.replace('\t', '    ')  # Convert tabs to 4 spaces
        elif indent_type == "tabs":
            text = re.sub(r'    ', '\t', text)  # Convert 4 spaces to tabs
        
        return text


class SpecialCharacterProcessor:
    """
    Special character processing engine.
    
    This class provides special character processing capabilities, including
    punctuation normalization and diacritic handling.
    
    Features:
        - Punctuation normalization (quotes, dashes, ellipsis)
        - Diacritic processing (normalization or removal)
        - Special character replacement
    
    Example Usage:
        >>> processor = SpecialCharacterProcessor()
        >>> processed = processor.process_special_chars(text, normalize_diacritics=True)
        >>> normalized = processor.normalize_punctuation(text)
    """
    
    def __init__(self, **config):
        """
        Initialize special character processor.
        
        Sets up the processor with configuration options.
        
        Args:
            **config: Configuration options (currently unused)
        """
        self.logger = get_logger("special_char_processor")
        self.config = config
        
        self.logger.debug("Special character processor initialized")
    
    def process_special_chars(
        self,
        text: str,
        normalize_diacritics: bool = False,
        **options
    ) -> str:
        """
        Process special characters in text.
        
        This method processes special characters by normalizing punctuation
        and optionally processing diacritics.
        
        Args:
            text: Input text to process
            normalize_diacritics: Whether to normalize diacritics (default: False)
            **options: Additional processing options (passed to process_diacritics)
        
        Returns:
            str: Text with special characters processed
        """
        # Normalize punctuation
        text = self.normalize_punctuation(text)
        
        # Process diacritics if requested
        if normalize_diacritics:
            text = self.process_diacritics(text, **options)
        
        return text
    
    def normalize_punctuation(self, text: str) -> str:
        """
        Normalize punctuation marks.
        
        This method normalizes various Unicode punctuation marks to their
        ASCII equivalents (quotes, dashes, ellipsis).
        
        Args:
            text: Input text with potentially mixed punctuation
        
        Returns:
            str: Text with normalized punctuation marks
        """
        # Normalize quotes
        text = re.sub(r'["""]', '"', text)
        text = re.sub(r'[''']', "'", text)
        
        # Normalize dashes
        text = re.sub(r'[–—]', '-', text)
        
        # Normalize ellipsis
        text = text.replace('…', '...')
        
        return text
    
    def process_diacritics(
        self,
        text: str,
        remove_diacritics: bool = False,
        **options
    ) -> str:
        """
        Process diacritical marks.
        
        This method processes diacritical marks by either normalizing them
        (NFC) or removing them entirely.
        
        Args:
            text: Input text with diacritical marks
            remove_diacritics: Whether to remove diacritics (default: False):
                - True: Remove all diacritical marks
                - False: Normalize diacritics using NFC
            **options: Additional processing options (unused)
        
        Returns:
            str: Text with diacritics processed (normalized or removed)
        """
        if remove_diacritics:
            # Remove diacritics
            nfd = unicodedata.normalize('NFD', text)
            return ''.join(c for c in nfd if unicodedata.category(c) != 'Mn')
        else:
            # Normalize diacritics
            return unicodedata.normalize('NFC', text)
