"""Metadata serializers for :mod:`storage_core`.

Serializers handle **metadata only** — object data is always raw bytes.
"""

import json
from abc import ABC, abstractmethod
from typing import Any


class MetadataSerializer(ABC):
    """Abstract contract for metadata serialization."""

    @abstractmethod
    def serialize(self, metadata: dict[str, Any]) -> dict[str, str]:
        """Serialize a metadata dict for storage.

        Returns a dict of string → string suitable for S3 object metadata
        headers.
        """
        ...

    @abstractmethod
    def deserialize(self, metadata: dict[str, str]) -> dict[str, Any]:
        """Deserialize metadata from storage back to a dict."""
        ...


class JsonMetadataSerializer(MetadataSerializer):
    """Serializes metadata as JSON-encoded strings.

    Values that are strings pass through unchanged; non-string values
    are JSON-encoded and prefixed with ``"json:"`` so deserialization
    can distinguish them.
    """

    JSON_PREFIX = "json:"

    def serialize(self, metadata: dict[str, Any]) -> dict[str, str]:
        result: dict[str, str] = {}
        for k, v in metadata.items():
            if isinstance(v, str):
                result[k] = v
            else:
                result[k] = self.JSON_PREFIX + json.dumps(v)
        return result

    def deserialize(self, metadata: dict[str, str]) -> dict[str, Any]:
        result: dict[str, Any] = {}
        for k, v in metadata.items():
            if v.startswith(self.JSON_PREFIX):
                result[k] = json.loads(v[len(self.JSON_PREFIX) :])
            else:
                result[k] = v
        return result


class PassthroughMetadataSerializer(MetadataSerializer):
    """Passes metadata through unchanged (assumes already string→string)."""

    def serialize(self, metadata: dict[str, Any]) -> dict[str, str]:
        return {k: str(v) for k, v in metadata.items()}

    def deserialize(self, metadata: dict[str, str]) -> dict[str, Any]:
        return dict(metadata)
