"""Tests for CloudEvents serialization, event-type routing, and explicit topology config models."""

import json

import pytest
from pydantic import BaseModel

from kurier.configurations.models.azureservicebus import AzureServiceBusConfig
from kurier.configurations.models.kafka import KafkaConfig
from kurier.configurations.models.rabbitmq import RabbitMQConfig
from kurier.core.kurier_message import KurierMessage
from kurier.hermes import Hermes
from kurier.utilities.serializers import CloudEventSerializer, resolve_serializer

Hermes.is_enabled = False


class OrderCreated(BaseModel):
    order_id: int
    product: str


# -- CloudEvents serializer ---------------------------------------------------


class TestCloudEventsDeserializeEnvelope:
    def test_round_trip(self):
        """Serialize a KurierMessage to CloudEvents and deserialize back."""
        serializer = CloudEventSerializer(source="kurier://test")
        envelope = KurierMessage(
            object_type_name="OrderCreated",
            qualified_object_type_name="tests.test_cloudevents_and_topology.OrderCreated",
            message_payload='{"order_id": 1, "product": "widget"}',
            metadata={"tenant": "acme"},
        )

        wire = serializer.serialize_envelope(envelope)
        restored = serializer.deserialize_envelope(wire)

        assert restored["object_type_name"] == "OrderCreated"
        assert restored["qualified_object_type_name"] == "tests.test_cloudevents_and_topology.OrderCreated"
        assert restored["correlation_id"] == envelope.correlation_id
        assert restored["event_type"] == "tests.test_cloudevents_and_topology.OrderCreated"
        assert restored["source"] == "kurier://test"
        assert restored["metadata"] == {"tenant": "acme"}
        assert restored["message_payload"] == {"order_id": 1, "product": "widget"}

    def test_deserialize_dict_input(self):
        """deserialize_envelope accepts a dict directly."""
        serializer = CloudEventSerializer()
        cloud_event = {
            "specversion": "1.0",
            "id": "msg-123",
            "source": "kurier://ext",
            "type": "com.example.OrderCreated",
            "time": "2026-03-08T00:00:00Z",
            "data": {"order_id": 5},
            "correlationid": "corr-1",
            "metroobjecttypename": "OrderCreated",
        }
        restored = serializer.deserialize_envelope(cloud_event)

        assert restored["object_type_name"] == "OrderCreated"
        assert restored["event_type"] == "com.example.OrderCreated"
        assert restored["message_payload"] == {"order_id": 5}

    def test_infer_object_type_name_from_event_type(self):
        """When metroobjecttypename is missing, infer from the event type."""
        serializer = CloudEventSerializer()
        cloud_event = {
            "specversion": "1.0",
            "id": "msg-456",
            "source": "kurier://ext",
            "type": "com.company.events.PaymentReceived",
            "time": "2026-03-08T00:00:00Z",
            "data": {"amount": 100},
        }
        restored = serializer.deserialize_envelope(cloud_event)

        assert restored["object_type_name"] == "PaymentReceived"

    def test_missing_metadata_returns_empty_dict(self):
        """When metrometadata is absent, metadata should be an empty dict."""
        serializer = CloudEventSerializer()
        cloud_event = {
            "specversion": "1.0",
            "id": "msg-789",
            "source": "kurier://ext",
            "type": "SomeEvent",
            "data": {},
        }
        restored = serializer.deserialize_envelope(cloud_event)
        assert restored["metadata"] == {}

    def test_custom_source_in_metadata_overrides_default(self):
        """cloudevents_source in metadata overrides the serializer default."""
        serializer = CloudEventSerializer(source="kurier://default")
        envelope = KurierMessage(
            object_type_name="OrderCreated",
            message_payload='{"order_id": 1}',
            metadata={"cloudevents_source": "kurier://override"},
        )
        wire = json.loads(serializer.serialize_envelope(envelope))
        assert wire["source"] == "kurier://override"

    def test_custom_event_type_in_metadata(self):
        """cloudevents_type in metadata overrides the default type derivation."""
        serializer = CloudEventSerializer(source="kurier://test")
        envelope = KurierMessage(
            object_type_name="OrderCreated",
            qualified_object_type_name="app.OrderCreated",
            message_payload='{"order_id": 1}',
            metadata={"cloudevents_type": "com.custom.OrderSubmitted"},
        )
        wire = json.loads(serializer.serialize_envelope(envelope))
        assert wire["type"] == "com.custom.OrderSubmitted"


# -- resolve_serializer --------------------------------------------------------


class TestResolveSerializer:
    def test_default_is_json(self):
        s = resolve_serializer()
        assert type(s).__name__ == "JsonSerializer"

    def test_json_explicit(self):
        s = resolve_serializer("json")
        assert type(s).__name__ == "JsonSerializer"

    def test_cloudevents(self):
        s = resolve_serializer("cloudevents", source="kurier://svc")
        assert isinstance(s, CloudEventSerializer)
        assert s.source == "kurier://svc"

    def test_cloudevents_default_source(self):
        s = resolve_serializer("cloudevents")
        assert isinstance(s, CloudEventSerializer)
        assert s.source == "kurier://unknown"

    def test_unknown_raises(self):
        with pytest.raises(ValueError, match="Unsupported serializer"):
            resolve_serializer("msgpack")


# -- Explicit topology config models ------------------------------------------


class TestRabbitMQTopologyConfig:
    def test_explicit_topology_fields(self):
        config = RabbitMQConfig(
            name="orders",
            host="broker.local",
            port=5672,
            username="guest",
            password="guest",
            virtual_host="/",
            exchange_name="contracts.orders",
            exchange_type="topic",
            routing_key="order.created",
            queue_name="kurier.orders.queue",
        )
        assert config.exchange_name == "contracts.orders"
        assert config.exchange_type == "topic"
        assert config.routing_key == "order.created"
        assert config.queue_name == "kurier.orders.queue"

    def test_defaults(self):
        config = RabbitMQConfig(
            name="default",
            host="localhost",
            port=5672,
            username="guest",
            password="guest",
            virtual_host="/",
        )
        assert config.exchange_type == "topic"
        assert config.exchange_name is None
        assert config.routing_key is None
        assert config.queue_name is None

    def test_serializer_fields_inherited(self):
        config = RabbitMQConfig(
            name="ce",
            host="localhost",
            port=5672,
            username="guest",
            password="guest",
            virtual_host="/",
            serializer="cloudevents",
            cloudevents_source="kurier://orders",
        )
        assert config.serializer == "cloudevents"
        assert config.cloudevents_source == "kurier://orders"


class TestAzureTopologyConfig:
    def test_explicit_topology_fields(self):
        config = AzureServiceBusConfig(
            name="events",
            connection_string="Endpoint=sb://test.servicebus.windows.net/;SharedAccessKeyName=key;SharedAccessKey=val",
            topic_name="orders-topic",
            subscription_name="kurier-sub",
        )
        assert config.topic_name == "orders-topic"
        assert config.subscription_name == "kurier-sub"
        assert config.queue_name is None

    def test_queue_mode(self):
        config = AzureServiceBusConfig(
            name="queue-line",
            connection_string="Endpoint=sb://test.servicebus.windows.net/;SharedAccessKeyName=key;SharedAccessKey=val",
            queue_name="my-queue",
        )
        assert config.queue_name == "my-queue"
        assert config.topic_name is None

    def test_serializer_fields_inherited(self):
        config = AzureServiceBusConfig(
            name="ce",
            connection_string="Endpoint=sb://x;SharedAccessKeyName=k;SharedAccessKey=v",
            serializer="cloudevents",
            cloudevents_source="kurier://azure",
        )
        assert config.serializer == "cloudevents"
        assert config.cloudevents_source == "kurier://azure"


class TestKafkaTopologyConfig:
    def test_explicit_topic(self):
        config = KafkaConfig(
            name="kafka-line",
            bootstrap_servers=["localhost:9092"],
            topic_name="orders",
            group_id="kurier-group",
        )
        assert config.topic_name == "orders"
        assert config.group_id == "kurier-group"

    def test_serializer_fields_inherited(self):
        config = KafkaConfig(
            name="ce",
            bootstrap_servers=["localhost:9092"],
            topic_name="events",
            group_id="g1",
            serializer="cloudevents",
            cloudevents_source="kurier://kafka",
        )
        assert config.serializer == "cloudevents"
        assert config.cloudevents_source == "kurier://kafka"
