"""CRDT-specific exceptions for document parsing and validation.

This module defines exceptions for handling errors that occur during
CRDT document parsing, validation, and schema processing.
"""

from __future__ import annotations


class CRDTError(Exception):
    """Base exception for all CRDT-related errors.

    Args:
        message: Human-readable error message

    Example:
        >>> raise CRDTError("Failed to process CRDT document")
    """

    def __init__(self, message: str) -> None:
        self.message = message
        super().__init__(self._format_message())

    def _format_message(self) -> str:
        """Format the error message."""
        return self.message


class CRDTParseError(CRDTError):
    """Raised when parsing a CRDT document fails.

    This exception is raised when:
    - Document structure is malformed
    - Expected nodes are missing
    - Node content cannot be parsed

    Args:
        message: Human-readable error message
        platform: Name of the platform being parsed
        node_type: Type of node that failed to parse
        position: Position in document where error occurred

    Example:
        >>> raise CRDTParseError(
        ...     "Failed to parse card content",
        ...     platform="twitter",
        ...     node_type="twittercard",
        ...     position=0
        ... )
    """

    def __init__(
        self,
        message: str,
        platform: str | None = None,
        node_type: str | None = None,
        position: int | None = None,
    ) -> None:
        self.platform = platform
        self.node_type = node_type
        self.position = position
        super().__init__(message)

    def _format_message(self) -> str:
        """Format the error message with context."""
        parts = [self.message]
        if self.platform:
            parts.append(f"Platform: {self.platform}")
        if self.node_type:
            parts.append(f"Node type: {self.node_type}")
        if self.position is not None:
            parts.append(f"Position: {self.position}")
        return " | ".join(parts)


class CRDTValidationError(CRDTError):
    """Raised when CRDT document validation fails.

    This exception is raised when:
    - Content exceeds platform limits (character count, media count)
    - Required fields are missing
    - Field values don't meet platform requirements

    Args:
        message: Human-readable error message
        field: Name of the field that failed validation
        expected: Expected value or constraint
        actual: Actual value that was found

    Example:
        >>> raise CRDTValidationError(
        ...     "Tweet exceeds character limit",
        ...     field="text",
        ...     expected="max 280 characters",
        ...     actual="295 characters"
        ... )
    """

    def __init__(
        self,
        message: str,
        field: str | None = None,
        expected: str | None = None,
        actual: str | None = None,
    ) -> None:
        self.field = field
        self.expected = expected
        self.actual = actual
        super().__init__(message)

    def _format_message(self) -> str:
        """Format the error message with validation details."""
        parts = [self.message]
        if self.field:
            parts.append(f"Field: {self.field}")
        if self.expected:
            parts.append(f"Expected: {self.expected}")
        if self.actual:
            parts.append(f"Actual: {self.actual}")
        return " | ".join(parts)


class CRDTSchemaError(CRDTError):
    """Raised when document schema structure is invalid.

    This exception is raised when:
    - Document doesn't follow expected ProseMirror schema
    - Required node types are missing from schema
    - Schema version is incompatible

    Args:
        message: Human-readable error message
        schema_element: Name of the schema element that caused the error

    Example:
        >>> raise CRDTSchemaError(
        ...     "Missing required node type in schema",
        ...     schema_element="twittercard"
        ... )
    """

    def __init__(
        self,
        message: str,
        schema_element: str | None = None,
    ) -> None:
        self.schema_element = schema_element
        super().__init__(message)

    def _format_message(self) -> str:
        """Format the error message with schema details."""
        base_message = self.message
        if self.schema_element:
            return f"{base_message} | Schema element: {self.schema_element}"
        return base_message


class CRDTUnsupportedPlatformError(CRDTError):
    """Raised when an unsupported platform is requested.

    This exception is raised when:
    - Attempting to parse a document for an unknown platform
    - Platform parser is not implemented

    Args:
        platform: Name of the unsupported platform
        supported_platforms: List of supported platform names

    Example:
        >>> raise CRDTUnsupportedPlatformError(
        ...     "facebook",
        ...     supported_platforms=["twitter", "linkedin", "instagram", "tiktok"]
        ... )
    """

    def __init__(
        self,
        platform: str,
        supported_platforms: list[str] | None = None,
    ) -> None:
        self.platform = platform
        self.supported_platforms = supported_platforms or []
        message = f"Unsupported platform: {platform}"
        super().__init__(message)

    def _format_message(self) -> str:
        """Format the error message with supported platforms."""
        base_message = self.message
        if self.supported_platforms:
            supported = ", ".join(self.supported_platforms)
            return f"{base_message} | Supported platforms: {supported}"
        return base_message


class CRDTEmptyDocumentError(CRDTError):
    """Raised when a CRDT document is empty or has no content.

    This exception is raised when:
    - Document has no nodes
    - Document fragment is empty
    - All content nodes are empty

    Args:
        message: Human-readable error message
        fragment_name: Name of the empty fragment

    Example:
        >>> raise CRDTEmptyDocumentError(
        ...     "Document contains no content",
        ...     fragment_name="default"
        ... )
    """

    def __init__(
        self,
        message: str = "Document is empty",
        fragment_name: str | None = None,
    ) -> None:
        self.fragment_name = fragment_name
        super().__init__(message)

    def _format_message(self) -> str:
        """Format the error message with fragment info."""
        base_message = self.message
        if self.fragment_name:
            return f"{base_message} | Fragment: {self.fragment_name}"
        return base_message
