from unittest import TestCase

from edpdependencyinjection import DependencyLoader
from messagebus import SubscriptionInfo
from messageprocessor import DefaultMessageProcessorExecutor

import testmodule
from dimessageprocessor import (
    EventProcessorDefinition,
    MessageProcessorDefinition,
    DiEventMessageProcessorBuilder,
    DiMessageMessageProcessorBuilder,
    DiMessageProcessorExecutorFactory,
    DiMessageProcessorMessageHandler,
)
from testmodule.processor import SimpleProcessor, AnotherProcessor
from testmodule.callback import TestCallback


class TestEventProcessorDefinitionEdgeCases(TestCase):

    def test_subscription_type_zero_raises(self):
        with self.assertRaises(ValueError):
            EventProcessorDefinition(processor_type=SimpleProcessor, subscription_type=0)

    def test_subscription_type_negative_raises(self):
        with self.assertRaises(ValueError):
            EventProcessorDefinition(processor_type=SimpleProcessor, subscription_type=-1)

    def test_subscription_type_three_raises(self):
        with self.assertRaises(ValueError):
            EventProcessorDefinition(processor_type=SimpleProcessor, subscription_type=3)

    def test_reentrant_type_accepted(self):
        definition = EventProcessorDefinition(
            processor_type=SimpleProcessor,
            subscription_type=SubscriptionInfo.TYPE_REENTRANT
        )
        self.assertEqual(definition.subscription_type, SubscriptionInfo.TYPE_REENTRANT)

    def test_non_reentrant_type_accepted(self):
        definition = EventProcessorDefinition(
            processor_type=SimpleProcessor,
            subscription_type=SubscriptionInfo.TYPE_NON_REENTRANT
        )
        self.assertEqual(definition.subscription_type, SubscriptionInfo.TYPE_NON_REENTRANT)

    def test_str_includes_subscription_type(self):
        definition = EventProcessorDefinition(
            processor_type=SimpleProcessor,
            subscription_type=SubscriptionInfo.TYPE_REENTRANT
        )
        result = str(definition)
        self.assertIn(str(SubscriptionInfo.TYPE_REENTRANT), result)


class TestDiEventMessageProcessorBuilderEdgeCases(TestCase):

    def setUp(self):
        self.dependency_loader = DependencyLoader()
        self.dependency_loader.load_modules(modules=[testmodule])
        self.dependency_context = self.dependency_loader.build_context()

    def test_empty_definitions(self):
        builder = DiEventMessageProcessorBuilder(
            dependency_context=self.dependency_context,
            processor_definitions={}
        )
        result = builder.build_message_processor_dict()
        self.assertEqual(result, {})

    def test_empty_string_key(self):
        builder = DiEventMessageProcessorBuilder(
            dependency_context=self.dependency_context,
            processor_definitions={"": SimpleProcessor}
        )
        processor = builder.build_message_processor("")
        self.assertIsInstance(processor, SimpleProcessor)

    def test_build_processor_type_returns_same_singleton(self):
        builder = DiEventMessageProcessorBuilder(
            dependency_context=self.dependency_context,
            processor_definitions={"event": SimpleProcessor}
        )
        first = builder.build_message_processor("event")
        second = builder.build_message_processor("event")
        self.assertIs(first, second)

    def test_build_processor_instance_returns_same_each_call(self):
        instance = SimpleProcessor()
        builder = DiEventMessageProcessorBuilder(
            dependency_context=self.dependency_context,
            processor_definitions={"event": instance}
        )
        first = builder.build_message_processor("event")
        second = builder.build_message_processor("event")
        self.assertIs(first, second)


class TestDiMessageMessageProcessorBuilderEdgeCases(TestCase):

    def setUp(self):
        self.dependency_loader = DependencyLoader()
        self.dependency_loader.load_modules(modules=[testmodule])
        self.dependency_context = self.dependency_loader.build_context()

    def test_empty_definitions(self):
        builder = DiMessageMessageProcessorBuilder(
            dependency_context=self.dependency_context,
            processor_definitions={}
        )
        result = builder.build_message_processor_dict()
        self.assertEqual(result, {})

    def test_zero_token(self):
        builder = DiMessageMessageProcessorBuilder(
            dependency_context=self.dependency_context,
            processor_definitions={0x00: SimpleProcessor}
        )
        processor = builder.build_message_processor(0x00)
        self.assertIsInstance(processor, SimpleProcessor)

    def test_large_token(self):
        builder = DiMessageMessageProcessorBuilder(
            dependency_context=self.dependency_context,
            processor_definitions={0xFFFF: SimpleProcessor}
        )
        processor = builder.build_message_processor(0xFFFF)
        self.assertIsInstance(processor, SimpleProcessor)

    def test_build_processor_type_returns_same_singleton(self):
        builder = DiMessageMessageProcessorBuilder(
            dependency_context=self.dependency_context,
            processor_definitions={0x01: SimpleProcessor}
        )
        first = builder.build_message_processor(0x01)
        second = builder.build_message_processor(0x01)
        self.assertIs(first, second)

    def test_build_processor_instance_returns_same_each_call(self):
        instance = SimpleProcessor()
        builder = DiMessageMessageProcessorBuilder(
            dependency_context=self.dependency_context,
            processor_definitions={0x01: instance}
        )
        first = builder.build_message_processor(0x01)
        second = builder.build_message_processor(0x01)
        self.assertIs(first, second)


class TestDiMessageProcessorExecutorFactoryEdgeCases(TestCase):

    def setUp(self):
        self.dependency_loader = DependencyLoader()
        self.dependency_loader.load_modules(modules=[testmodule])
        self.dependency_context = self.dependency_loader.build_context()

    def test_build_executor_twice_returns_different_instances(self):
        factory = DiMessageProcessorExecutorFactory(
            dependency_context=self.dependency_context,
            callback_definitions=[TestCallback]
        )
        processor = SimpleProcessor()
        first = factory.build_executor(processor)
        second = factory.build_executor(processor)
        self.assertIsNot(first, second)

    def test_build_executor_duplicate_callbacks_returns_same_singleton(self):
        factory = DiMessageProcessorExecutorFactory(
            dependency_context=self.dependency_context,
            callback_definitions=[TestCallback, TestCallback]
        )
        executor = factory.build_executor(SimpleProcessor())
        self.assertEqual(len(executor.callbacks), 2)
        self.assertIs(executor.callbacks[0], executor.callbacks[1])


class TestDiMessageProcessorMessageHandlerEdgeCases(TestCase):

    def setUp(self):
        self.dependency_loader = DependencyLoader()
        self.dependency_loader.load_modules(modules=[testmodule])
        self.dependency_context = self.dependency_loader.build_context()

    def test_empty_event_definitions_dict_no_processors(self):
        empty_loader = DependencyLoader()
        empty_context = empty_loader.build_context()
        handler = DiMessageProcessorMessageHandler(
            dependency_context=empty_context,
            event_processor_definitions={}
        )
        self.assertIsNone(handler.event_processors)

    def test_empty_message_definitions_dict_no_processors(self):
        empty_loader = DependencyLoader()
        empty_context = empty_loader.build_context()
        handler = DiMessageProcessorMessageHandler(
            dependency_context=empty_context,
            message_processor_definitions={}
        )
        self.assertIsNone(handler.message_processors)

    def test_empty_callback_definitions_creates_factory(self):
        handler = DiMessageProcessorMessageHandler(
            dependency_context=self.dependency_context,
            callback_definitions=[]
        )
        self.assertIsNotNone(handler.executor_factory)

    def test_none_callback_definitions_uses_default_factory(self):
        empty_loader = DependencyLoader()
        empty_context = empty_loader.build_context()
        handler = DiMessageProcessorMessageHandler(
            dependency_context=empty_context,
            callback_definitions=None
        )
        # Parent class provides a default factory when None is passed
        self.assertIsNotNone(handler.executor_factory)

    def test_discovered_key_overrides_explicit_on_collision(self):
        handler = DiMessageProcessorMessageHandler(
            dependency_context=self.dependency_context,
            event_processor_definitions={"builder_event": AnotherProcessor}
        )
        # Discovered builders run after explicit, so discovered overwrites
        self.assertIn("builder_event", handler.event_processors)

    def test_discovered_token_overrides_explicit_on_collision(self):
        handler = DiMessageProcessorMessageHandler(
            dependency_context=self.dependency_context,
            message_processor_definitions={0x10: AnotherProcessor}
        )
        # Discovered builders run after explicit, so discovered overwrites
        self.assertIn(0x10, handler.message_processors)

    def test_no_auto_mapped_builders_in_empty_context(self):
        empty_loader = DependencyLoader()
        empty_context = empty_loader.build_context()
        handler = DiMessageProcessorMessageHandler(
            dependency_context=empty_context,
            event_processor_definitions={"event": SimpleProcessor}
        )
        self.assertIn("event", handler.event_processors)
        self.assertEqual(len(handler.event_processors), 1)