"""
Unit tests for the CallbackRegistry system.
"""

import unittest
from unittest.mock import Mock

from pyWebLayout.core.callback_registry import CallbackRegistry
from pyWebLayout.concrete.functional import LinkText, ButtonText
from pyWebLayout.abstract.functional import Link, Button, LinkType
from pyWebLayout.style import Font


class TestCallbackRegistry(unittest.TestCase):
    """Test cases for CallbackRegistry class."""

    def setUp(self):
        """Set up test fixtures."""
        self.registry = CallbackRegistry()
        self.font = Font(font_size=12, colour=(0, 0, 0))
        self.mock_draw = Mock()

    def test_registry_initialization(self):
        """Test that registry initializes empty."""
        self.assertEqual(self.registry.count(), 0)
        self.assertEqual(len(self.registry.get_all_ids()), 0)
        self.assertEqual(len(self.registry.get_all_types()), 0)

    def test_register_with_html_id(self):
        """Test registering an interactable with HTML id."""
        link = Link("https://example.com", LinkType.EXTERNAL, html_id="my-link")
        link_text = LinkText(link, "Example", self.font, self.mock_draw)

        registered_id = self.registry.register(link_text, html_id="my-link")

        self.assertEqual(registered_id, "my-link")
        self.assertEqual(self.registry.count(), 1)
        self.assertIn("my-link", self.registry)

    def test_register_without_html_id(self):
        """Test registering an interactable without HTML id (auto-generated)."""
        button = Button("Click Me", callback=None)
        button_text = ButtonText(button, self.font, self.mock_draw)

        registered_id = self.registry.register(button_text)

        # Should generate auto id like "auto_button_0"
        self.assertTrue(registered_id.startswith("auto_button_"))
        self.assertEqual(self.registry.count(), 1)

    def test_get_by_id(self):
        """Test retrieving an interactable by id."""
        link = Link("test", html_id="test-link")
        link_text = LinkText(link, "Test", self.font, self.mock_draw)
        self.registry.register(link_text, html_id="test-link")

        retrieved = self.registry.get_by_id("test-link")

        self.assertEqual(retrieved, link_text)

    def test_get_by_id_not_found(self):
        """Test retrieving non-existent id returns None."""
        result = self.registry.get_by_id("nonexistent")
        self.assertIsNone(result)

    def test_get_by_type(self):
        """Test retrieving all interactables of a type."""
        # Register multiple buttons
        button1 = Button("Button 1", callback=None)
        button2 = Button("Button 2", callback=None)
        button_text1 = ButtonText(button1, self.font, self.mock_draw)
        button_text2 = ButtonText(button2, self.font, self.mock_draw)

        self.registry.register(button_text1, html_id="btn1")
        self.registry.register(button_text2, html_id="btn2")

        # Also register a link
        link = Link("test")
        link_text = LinkText(link, "Test", self.font, self.mock_draw)
        self.registry.register(link_text, html_id="link1")

        buttons = self.registry.get_by_type("button")
        links = self.registry.get_by_type("link")

        self.assertEqual(len(buttons), 2)
        self.assertIn(button_text1, buttons)
        self.assertIn(button_text2, buttons)

        self.assertEqual(len(links), 1)
        self.assertIn(link_text, links)

    def test_set_callback(self):
        """Test setting a callback by id."""
        button = Button("Test", callback=None)
        button_text = ButtonText(button, self.font, self.mock_draw)
        self.registry.register(button_text, html_id="test-btn")

        def my_callback(point):
            return "clicked"

        success = self.registry.set_callback("test-btn", my_callback)

        self.assertTrue(success)
        self.assertEqual(button_text._callback, my_callback)

    def test_set_callback_not_found(self):
        """Test setting callback for non-existent id returns False."""
        result = self.registry.set_callback("nonexistent", lambda: None)
        self.assertFalse(result)

    def test_set_callbacks_by_type(self):
        """Test setting callbacks for all objects of a type."""
        # Register multiple buttons
        button1 = Button("Button 1", callback=None)
        button2 = Button("Button 2", callback=None)
        button_text1 = ButtonText(button1, self.font, self.mock_draw)
        button_text2 = ButtonText(button2, self.font, self.mock_draw)

        self.registry.register(button_text1)
        self.registry.register(button_text2)

        def click_handler(point):
            return "clicked"

        count = self.registry.set_callbacks_by_type("button", click_handler)

        self.assertEqual(count, 2)
        self.assertEqual(button_text1._callback, click_handler)
        self.assertEqual(button_text2._callback, click_handler)

    def test_unregister(self):
        """Test unregistering an interactable."""
        button = Button("Test", callback=None)
        button_text = ButtonText(button, self.font, self.mock_draw)
        self.registry.register(button_text, html_id="test-btn")

        self.assertEqual(self.registry.count(), 1)

        success = self.registry.unregister("test-btn")

        self.assertTrue(success)
        self.assertEqual(self.registry.count(), 0)
        self.assertNotIn("test-btn", self.registry)

    def test_clear(self):
        """Test clearing all registrations."""
        button = Button("Test", callback=None)
        button_text = ButtonText(button, self.font, self.mock_draw)
        link = Link("test")
        link_text = LinkText(link, "Test", self.font, self.mock_draw)

        self.registry.register(button_text)
        self.registry.register(link_text)

        self.assertEqual(self.registry.count(), 2)

        self.registry.clear()

        self.assertEqual(self.registry.count(), 0)
        self.assertEqual(len(self.registry.get_all_types()), 0)

    def test_count_by_type(self):
        """Test counting objects by type."""
        # Register 2 buttons and 1 link
        for i in range(2):
            button = Button(f"Button {i}", callback=None)
            button_text = ButtonText(button, self.font, self.mock_draw)
            self.registry.register(button_text)

        link = Link("test")
        link_text = LinkText(link, "Test", self.font, self.mock_draw)
        self.registry.register(link_text)

        self.assertEqual(self.registry.count_by_type("button"), 2)
        self.assertEqual(self.registry.count_by_type("link"), 1)
        self.assertEqual(self.registry.count_by_type("form_field"), 0)

    def test_contains_operator(self):
        """Test 'in' operator for checking if id exists."""
        button = Button("Test", callback=None)
        button_text = ButtonText(button, self.font, self.mock_draw)
        self.registry.register(button_text, html_id="test-btn")

        self.assertIn("test-btn", self.registry)
        self.assertNotIn("nonexistent", self.registry)

    def test_len_operator(self):
        """Test len() operator."""
        self.assertEqual(len(self.registry), 0)

        button = Button("Test", callback=None)
        button_text = ButtonText(button, self.font, self.mock_draw)
        self.registry.register(button_text)

        self.assertEqual(len(self.registry), 1)

    def test_repr(self):
        """Test string representation."""
        button = Button("Test", callback=None)
        button_text = ButtonText(button, self.font, self.mock_draw)
        self.registry.register(button_text)

        repr_str = repr(self.registry)

        self.assertIn("CallbackRegistry", repr_str)
        self.assertIn("total=1", repr_str)
        self.assertIn("button", repr_str)


if __name__ == '__main__':
    unittest.main()
