"""Pluggable serialization interfaces and built-in implementations."""

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

from kurier.core.kurier_message import KurierMessage


class Serializer(ABC):
    """Abstract base for converting dicts to/from a wire format."""

    @abstractmethod
    def serialize(self, data: Any) -> str | bytes:
        """Convert a payload to wire format."""
        ...

    @abstractmethod
    def deserialize(self, raw: str | bytes) -> Any:
        """Convert wire format back to a Python payload."""
        ...

    def serialize_envelope(self, envelope: KurierMessage) -> str | bytes:
        """Convert a Kurier envelope to the transport wire format."""
        return envelope.model_dump_json()

    def deserialize_envelope(self, raw: str | bytes | dict[str, Any]) -> dict[str, Any]:
        """Convert a transport envelope back to Kurier's internal envelope shape."""
        if isinstance(raw, dict):
            return raw
        return json.loads(raw)


class JsonSerializer(Serializer):
    """Default JSON-based serializer."""

    def serialize(self, data: Any) -> str:
        return json.dumps(data)

    def deserialize(self, raw: str | bytes) -> Any:
        return json.loads(raw)


class CloudEventSerializer(Serializer):
    """Serializer that emits and consumes CloudEvents JSON envelopes."""

    def __init__(self, source: str = "kurier://unknown"):
        self.source = source

    def serialize(self, data: Any) -> str:
        return json.dumps(data)

    def deserialize(self, raw: str | bytes) -> Any:
        return json.loads(raw)

    def serialize_envelope(self, envelope: KurierMessage) -> str:
        metadata = dict(envelope.metadata or {})
        event_type = metadata.pop("cloudevents_type", None) or metadata.pop("event_type", None) or envelope.qualified_object_type_name or envelope.object_type_name
        source = metadata.pop("cloudevents_source", None) or self.source
        data = envelope.message_payload
        if isinstance(data, str | bytes):
            data = self.deserialize(data)

        cloud_event = {
            "specversion": "1.0",
            "id": envelope.message_id,
            "source": source,
            "type": event_type,
            "time": envelope.timestamp,
            "datacontenttype": "application/json",
            "data": data,
            "correlationid": envelope.correlation_id,
            "metroobjecttypename": envelope.object_type_name,
        }

        if envelope.qualified_object_type_name:
            cloud_event["metroqualifiedobjecttypename"] = envelope.qualified_object_type_name
        if metadata:
            cloud_event["metrometadata"] = metadata

        return json.dumps(cloud_event)

    def deserialize_envelope(self, raw: str | bytes | dict[str, Any]) -> dict[str, Any]:
        cloud_event = raw if isinstance(raw, dict) else json.loads(raw)
        event_type = cloud_event.get("type")
        metadata = cloud_event.get("metrometadata")

        return {
            "timestamp": cloud_event.get("time"),
            "message_id": cloud_event.get("id"),
            "correlation_id": cloud_event.get("correlationid"),
            "object_type_name": cloud_event.get("metroobjecttypename") or self._infer_object_type_name(event_type),
            "qualified_object_type_name": cloud_event.get("metroqualifiedobjecttypename"),
            "message_payload": cloud_event.get("data"),
            "metadata": metadata if isinstance(metadata, dict) else {},
            "event_type": event_type,
            "source": cloud_event.get("source"),
        }

    @staticmethod
    def _infer_object_type_name(event_type: str | None) -> str | None:
        if not event_type:
            return None
        return event_type.rsplit(".", 1)[-1].rsplit(":", 1)[-1]


def resolve_serializer(name: str | None = None, source: str | None = None) -> Serializer:
    """Construct a serializer instance from a configuration name."""
    serializer_name = (name or "json").strip().lower()
    if serializer_name == "json":
        return JsonSerializer()
    if serializer_name == "cloudevents":
        return CloudEventSerializer(source=source or "kurier://unknown")
    raise ValueError(f"Unsupported serializer: {name!r}")
