"""Prompt injection hardening sanitizer based on OpenClaw patterns."""

import re
import unicodedata
from typing import Optional


class PromptInjectionSanitizer:
    """Sanitizes external content for safe LLM consumption."""
    
    # Zero-width and invisible characters (OpenClaw pattern)
    INVISIBLE_CHARS = [
        '\u200b', '\u200c', '\u200d', '\u200e', '\u200f',  # Zero-width spaces
        '\u2060', '\u2061', '\u2062', '\u2063', '\u2064',  # Word joiners
        '\ufeff', '\ufffe',                                  # BOM
        '\u00ad',                                           # Soft hyphen
        '\u034f',                                           # Combining grapheme
        '\u061c',                                           # Arabic letter mark
        '\u115f', '\u1160',                                  # Hangul fillers
        '\u17b4', '\u17b5',                                  # Khmer vowels
        '\u180e',                                           # Mongolian separator
        '\u3164',                                           # Hangul filler
        '\uffa0',                                           # Halfwidth Hangul
    ]
    
    def sanitize(self, text: str, max_length: Optional[int] = None) -> str:
        """Apply all sanitization layers.
        
        Args:
            text: Raw text to sanitize
            max_length: Optional maximum length (with ellipsis)
            
        Returns:
            Sanitized text safe for LLM prompts
        """
        if not text:
            return ''
        
        # Layer 1: Remove invisible/zero-width characters
        text = self._remove_invisible(text)
        
        # Layer 2: Remove control characters (except \n, \t)
        text = self._remove_control_chars(text)
        
        # Layer 3: Remove symbols (So, Sk categories)
        text = self._remove_symbols(text)
        
        # Layer 4: Normalize Unicode (NFC)
        text = unicodedata.normalize('NFC', text)
        
        # Layer 5: Remove Private Use Area
        text = self._remove_pua(text)
        
        # Layer 6: Remove tag characters
        text = self._remove_tag_chars(text)
        
        # Layer 7: Collapse horizontal whitespace; preserve \n and \t so that
        # list/table structure from web pages survives. Also collapse runs of
        # 3+ blank lines down to a single blank line.
        text = re.sub(r"[ \u00a0\u2000-\u200a\u202f\u205f\u3000]+", " ", text)
        text = re.sub(r"\n{3,}", "\n\n", text)
        text = text.strip()
        
        # Layer 8: Length limiting
        if max_length and len(text) > max_length:
            text = text[:max_length-3] + '...'
        
        return text
    
    def _remove_invisible(self, text: str) -> str:
        for char in self.INVISIBLE_CHARS:
            text = text.replace(char, '')
        return text
    
    def _remove_control_chars(self, text: str) -> str:
        return ''.join(c for c in text 
                      if unicodedata.category(c) != 'Cc' or c in '\n\t')
    
    def _remove_symbols(self, text: str) -> str:
        return ''.join(c for c in text 
                      if unicodedata.category(c) not in ('So', 'Sk'))
    
    def _remove_pua(self, text: str) -> str:
        return ''.join(c for c in text 
                      if not (0xE000 <= ord(c) <= 0xF8FF 
                             or 0xF0000 <= ord(c) <= 0x10FFFF))
    
    def _remove_tag_chars(self, text: str) -> str:
        return ''.join(c for c in text 
                      if not (0xE0000 <= ord(c) <= 0xE007F))


# Global instance
sanitizer = PromptInjectionSanitizer()
