"""Document wrapper class for VibeDB."""

from typing import Any, TYPE_CHECKING

if TYPE_CHECKING:
    from vibedb.core.collection import Collection


class Document:
    """
    Wrapper around document dict with convenience methods.

    Provides both dict-like and attribute-like access to document fields,
    plus instance methods for updating and deleting the document.

    Example:
        >>> doc = Document({"_id": "123", "name": "Alice", "age": 28})
        >>>
        >>> # Dict-like access
        >>> print(doc["name"])
        'Alice'
        >>>
        >>> # Dot notation
        >>> print(doc.name)
        'Alice'
        >>> print(doc.age)
        28
        >>>
        >>> # Update
        >>> doc.update({"age": 29})
        >>>
        >>> # Delete
        >>> doc.delete()
    """

    def __init__(self, data: dict, collection: "Collection" = None):
        """
        Initialize Document wrapper.

        Args:
            data: Document data as dict
            collection: Collection this document belongs to (optional)
        """
        # Use __dict__ to avoid triggering __setattr__
        object.__setattr__(self, '_data', data)
        object.__setattr__(self, '_collection', collection)

    def __getitem__(self, key: str) -> Any:
        """
        Dict-like access: doc["field"]

        Args:
            key: Field name

        Returns:
            Field value

        Raises:
            KeyError: If field doesn't exist
        """
        return self._data[key]

    def __setitem__(self, key: str, value: Any):
        """
        Dict-like assignment: doc["field"] = value

        Args:
            key: Field name
            value: Field value
        """
        self._data[key] = value

    def __getattr__(self, key: str) -> Any:
        """
        Dot notation access: doc.field

        Args:
            key: Field name

        Returns:
            Field value or None if field doesn't exist
        """
        # Avoid recursion for private attributes
        if key.startswith('_'):
            raise AttributeError(f"'{type(self).__name__}' object has no attribute '{key}'")

        return self._data.get(key)

    def __setattr__(self, key: str, value: Any):
        """
        Dot notation assignment: doc.field = value

        Args:
            key: Field name
            value: Field value
        """
        if key.startswith('_'):
            # Allow setting private attributes normally
            object.__setattr__(self, key, value)
        else:
            self._data[key] = value

    def __contains__(self, key: str) -> bool:
        """
        Check if field exists: "field" in doc

        Args:
            key: Field name

        Returns:
            bool: True if field exists
        """
        return key in self._data

    def __repr__(self) -> str:
        """String representation."""
        return f"Document({self._data})"

    def __str__(self) -> str:
        """String representation."""
        return str(self._data)

    def __eq__(self, other) -> bool:
        """Equality comparison."""
        if isinstance(other, Document):
            return self._data == other._data
        elif isinstance(other, dict):
            return self._data == other
        return False

    def __len__(self) -> int:
        """Number of fields in document."""
        return len(self._data)

    def __iter__(self):
        """Iterate over field names."""
        return iter(self._data)

    def get(self, key: str, default: Any = None) -> Any:
        """
        Get field value with default.

        Args:
            key: Field name
            default: Default value if field doesn't exist

        Returns:
            Field value or default

        Example:
            >>> doc = Document({"name": "Alice"})
            >>> doc.get("age", 0)
            0
        """
        return self._data.get(key, default)

    def keys(self):
        """Return field names."""
        return self._data.keys()

    def values(self):
        """Return field values."""
        return self._data.values()

    def items(self):
        """Return (field, value) pairs."""
        return self._data.items()

    def to_dict(self) -> dict:
        """
        Convert to plain dict.

        Returns:
            dict: Copy of document data

        Example:
            >>> doc = Document({"name": "Alice", "age": 28})
            >>> data = doc.to_dict()
            >>> print(type(data))
            <class 'dict'>
        """
        return self._data.copy()

    @property
    def id(self) -> str:
        """
        Convenience accessor for _id field.

        Returns:
            str: Document ID

        Example:
            >>> doc = Document({"_id": "abc123", "name": "Alice"})
            >>> print(doc.id)
            'abc123'
        """
        return self._data.get('_id')

    def update(self, updates: dict) -> None:
        """
        Update this document in the database.

        Args:
            updates: Fields to update

        Raises:
            RuntimeError: If document is not associated with a collection

        Example:
            >>> # Assuming doc is from collection.get()
            >>> doc.update({"age": 29, "city": "NYC"})
        """
        if self._collection is None:
            raise RuntimeError(
                "Cannot update document: not associated with a collection. "
                "Use collection.update(doc_id, updates) instead."
            )

        if '_id' not in self._data:
            raise RuntimeError("Cannot update document: missing _id field")

        # Update in database
        self._collection.update(self._data['_id'], updates)

        # Update local data
        self._data.update(updates)

    def delete(self) -> None:
        """
        Delete this document from the database.

        Raises:
            RuntimeError: If document is not associated with a collection

        Example:
            >>> # Assuming doc is from collection.get()
            >>> doc.delete()
        """
        if self._collection is None:
            raise RuntimeError(
                "Cannot delete document: not associated with a collection. "
                "Use collection.delete(doc_id) instead."
            )

        if '_id' not in self._data:
            raise RuntimeError("Cannot delete document: missing _id field")

        # Delete from database
        self._collection.delete(self._data['_id'])
