"""
Utility functions and helpers for CutySoup
"""
from typing import Dict, List, Union, Any, Pattern, Callable, TypeVar
import re
from .exceptions import ValidationError

T = TypeVar('T')


def normalize_whitespace(text: str, separator: str = " ") -> str:
    """Normalize whitespace in text by collapsing multiple spaces."""
    return re.sub(r'\s+', separator, text.strip())


def is_string_type(obj: Any) -> bool:
    """Check if object is a string-like type."""
    return isinstance(obj, (str, bytes))


def is_tag_name_match(tag_or_name, search_name: Union[str, List[str], Pattern, Callable, bool]) -> bool:
    """Check if tag name matches search criteria."""
    if search_name is True:
        return True
    elif search_name is False or search_name is None:
        return False
    elif callable(search_name):
        # For callable, pass the tag object itself, not just the name
        return bool(search_name(tag_or_name))
    
    # For non-callable searches, extract the tag name if we got a tag object
    if hasattr(tag_or_name, 'name'):
        tag_name = tag_or_name.name
    else:
        tag_name = tag_or_name
        
    if isinstance(search_name, str):
        # Empty string means match all tags (BeautifulSoup4 behavior)
        if search_name == "":
            return True
        return tag_name == search_name
    elif isinstance(search_name, list):
        return tag_name in search_name
    elif hasattr(search_name, 'match'):  # regex pattern
        return bool(search_name.match(tag_name))
    return False


def validate_html_content(content: Union[str, bytes]) -> str:
    """Validate and normalize HTML content input."""
    if content is None:
        raise ValidationError("HTML content cannot be None")
    
    if isinstance(content, bytes):
        try:
            content = content.decode('utf-8')
        except UnicodeDecodeError as e:
            raise ValidationError(f"Failed to decode bytes content: {e}")
    
    if not isinstance(content, str):
        raise ValidationError(f"HTML content must be str or bytes, got {type(content)}")
    
    return content


def validate_tag_name(name: str) -> str:
    """Validate HTML tag name."""
    if not isinstance(name, str):
        raise ValidationError(f"Tag name must be string, got {type(name)}")
    
    if not name.strip():
        raise ValidationError("Tag name cannot be empty")
    
    # Basic HTML tag name validation
    if not re.match(r'^[a-zA-Z][a-zA-Z0-9\-]*$', name.strip()):
        raise ValidationError(f"Invalid tag name: {name}")
    
    return name.strip().lower()


def validate_attribute_name(name: str) -> str:
    """Validate HTML attribute name."""
    if not isinstance(name, str):
        raise ValidationError(f"Attribute name must be string, got {type(name)}")
    
    if not name.strip():
        raise ValidationError("Attribute name cannot be empty")
    
    # Basic HTML attribute name validation
    if not re.match(r'^[a-zA-Z][a-zA-Z0-9\-_:]*$', name.strip()):
        raise ValidationError(f"Invalid attribute name: {name}")
    
    return name.strip().lower()


def escape_html_attribute(value: str) -> str:
    """Escape HTML attribute value."""
    if not isinstance(value, str):
        value = str(value)
    
    return (value
            .replace('&', '&amp;')
            .replace('<', '&lt;')
            .replace('>', '&gt;')
            .replace('"', '&quot;')
            .replace("'", '&#x27;'))


def unescape_html_attribute(value: str) -> str:
    """Unescape HTML attribute value."""
    if not isinstance(value, str):
        return str(value)
    
    return (value
            .replace('&quot;', '"')
            .replace('&#x27;', "'")
            .replace('&lt;', '<')
            .replace('&gt;', '>')
            .replace('&amp;', '&'))


def build_tag_string(name: str, attrs: Dict[str, str], self_closing: bool = False) -> str:
    """Build HTML tag string from name and attributes."""
    name = validate_tag_name(name)
    
    if not attrs:
        if self_closing:
            return f"<{name} />"
        return f"<{name}>"
    
    attr_parts = []
    for attr_name, attr_value in attrs.items():
        validated_name = validate_attribute_name(attr_name)
        escaped_value = escape_html_attribute(attr_value)
        attr_parts.append(f'{validated_name}="{escaped_value}"')
    
    attrs_str = " ".join(attr_parts)
    
    if self_closing:
        return f"<{name} {attrs_str} />"
    return f"<{name} {attrs_str}>"


def filter_none_values(d: Dict[str, Any]) -> Dict[str, Any]:
    """Filter out None values from dictionary."""
    return {k: v for k, v in d.items() if v is not None}


def flatten_list(nested_list: List[Union[T, List[T]]]) -> List[T]:
    """Flatten a nested list structure."""
    result = []
    for item in nested_list:
        if isinstance(item, list):
            result.extend(flatten_list(item))
        else:
            result.append(item)
    return result


def merge_dicts(*dicts: Dict[str, Any]) -> Dict[str, Any]:
    """Merge multiple dictionaries, with later ones taking precedence."""
    result = {}
    for d in dicts:
        if d:
            result.update(d)
    return result