"""
JSON serializer for ActivityPub objects.
"""

import json
from typing import Any, ClassVar

from pydantic import BaseModel
from pydantic.alias_generators import to_camel
from pydantic.config import ConfigDict


def to_json_string(data: dict[str, Any]) -> str:
    """
    Convert dictionary to JSON string with standardized formatting.

    Args:
        data: Dictionary to convert

    Returns:
        JSON string with consistent formatting
    """
    return json.dumps(data, sort_keys=True, ensure_ascii=True, separators=(",", ":"))


class ActivityPubBase(BaseModel):
    """Base class for all ActivityPub objects."""

    def serialize(self, include_context: bool = True) -> dict[str, Any]:
        """Serialize the AP object to a dict.

        Args:
            include_context (bool, optional): If the JSON-LD context for ActivityPub should be included in the dict. Defaults to True.

        Returns:
            dict[str, Any]: JSON-LD representation of the object.
        """
        processed_data = self.model_dump(by_alias=True, exclude_none=True)

        # Add context if needed
        if include_context:
            context = ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1"]
            if "proof" in processed_data and isinstance(processed_data["proof"], dict):
                context.append("https://w3id.org/security/data-integrity/v2")
            processed_data["@context"] = context

        return processed_data

    @classmethod
    def deserialize(cls, data: str | dict[str, Any]) -> "ActivityPubBase":
        """Deserialize dict to an ActivityPub object.
        This is removing the json-ld context before deserialization.
        """
        if isinstance(data, dict):
            data.pop("@context", None)
        obj = cls.model_validate(data)
        return obj

    model_config: ClassVar[ConfigDict] = ConfigDict(
        alias_generator=to_camel,
        populate_by_name=True,
        extra="allow",
        arbitrary_types_allowed=True,
        loc_by_alias=True,
    )
