"""
Text content validator.
"""

import json

try:
    from defusedxml.ElementTree import ParseError
    from defusedxml.ElementTree import fromstring as safe_fromstring
except ImportError:
    from xml.etree.ElementTree import ParseError
    from xml.etree.ElementTree import fromstring as safe_fromstring  # nosec B314

from .base_validator import BaseValidator, ValidationError


class TextValidator(BaseValidator):
    """Validator for text-based content types."""

    TEXT_MIME_TYPES = {
        "text/plain",
        "application/json",
        "application/xml",
        "text/xml",
        "image/svg+xml",
        "text/html",
        "text/markdown",
    }

    def can_validate(self, mime_type: str) -> bool:
        return mime_type in self.TEXT_MIME_TYPES

    def validate(self, content: str | bytes, mime_type: str) -> None:
        """Validate text content based on MIME type."""
        if isinstance(content, bytes):
            try:
                text_content = content.decode("utf-8")
            except UnicodeDecodeError as e:
                raise ValidationError("Invalid content: not valid UTF-8") from e
        else:
            text_content = content

        if mime_type == "text/plain":
            self._validate_plain_text(text_content)
        elif mime_type == "application/json":
            self._validate_json(text_content)
        elif mime_type in ("application/xml", "text/xml", "image/svg+xml"):
            self._validate_xml(text_content)

    def _validate_plain_text(self, content: str) -> None:
        """Validate plain text content."""
        if not content.strip():
            raise ValidationError("Invalid content: empty text")
        if len(content.strip()) < 3:
            raise ValidationError("Invalid content: too short")
        if all(ord(c) < 32 for c in content.strip()):
            raise ValidationError("Invalid content: contains only control characters")
        # Heuristic: valid plain text should contain some whitespace
        if not any(c.isspace() for c in content):
            # Check for multi-word content without spaces
            if (
                len(content.split()) == 1 and len(content) > 20
            ):  # Arbitrary length check
                raise ValidationError("Invalid content: likely not plain text")

    def _validate_json(self, content: str) -> None:
        """Validate JSON content."""
        try:
            # Check for comments before attempting to parse
            lines = content.split("\n")
            if any(line.strip().startswith("//") for line in lines):
                raise ValidationError("Invalid JSON content: contains comments")
            json.loads(content)
        except json.JSONDecodeError as e:
            raise ValidationError("Invalid JSON content") from e

    def _validate_xml(self, content: str) -> None:
        """Validate XML content."""
        try:
            safe_fromstring(content)  # nosec B314
        except ParseError as e:
            raise ValidationError("Invalid XML content") from e
