"""Unit tests for edge cases and error handling in owl_dsl.renderer.CNLRenderer."""

import pytest
from unittest.mock import Mock, patch
from owlready2 import ThingClass
from rdflib import URIRef


class TestClassRenderingEdgeCases:
    """Tests for edge cases in OWL class rendering."""

    def test_class_without_label_attribute(self, cnl_renderer):
        """Test rendering of a class without label attribute."""
        Person = cnl_renderer.ontology.search_one(iri="*Person")

        assert Person is not None

        # Temporarily remove label to simulate missing label
        original_label = list(Person.label) if hasattr(Person, "label") else []
        Person.label = []

        rendered = cnl_renderer.render_readable_owl_class(Person)

        # Restore label
        Person.label = original_label

        # When label is empty and no_indef_article=True (default), returns None
        assert rendered is None

    def test_class_with_empty_label(self, cnl_renderer):
        """Test rendering of a class with empty label list."""
        Person = cnl_renderer.ontology.search_one(iri="*Person")

        assert Person is not None

        # Set empty label
        original_label = list(Person.label) if hasattr(Person, "label") else []
        Person.label = []

        rendered = cnl_renderer.render_readable_owl_class(Person, no_indef_article=True)

        # Restore label
        Person.label = original_label

        # When label is empty and no_indef_article=True (default), returns None
        assert rendered is None

    def test_lowercase_labels_parameter(self, cnl_renderer):
        """Test rendering with lowercase_labels=False preserves case."""
        Person = cnl_renderer.ontology.search_one(iri="*Person")

        assert Person is not None

        # lowercase_labels is set on the renderer instance, not passed as parameter
        original_case = cnl_renderer.lowercase_labels
        cnl_renderer.lowercase_labels = True

        rendered_lower = cnl_renderer.render_readable_owl_class(Person)

        # Restore setting
        cnl_renderer.lowercase_labels = original_case

        assert isinstance(rendered_lower, str)


class TestConceptGroupingEdgeCases:
    """Tests for concept grouping and key identification edge cases."""

    def test_empty_property_iris_list(self, cnl_renderer):
        """Test behavior when property_iris is empty."""
        # Verify RESTRICTION_START_KEY logic with empty properties
        assert cnl_renderer.RESTRICTION_START_KEY == 2

        # Keys above RESTRICTION_START_KEY should be named owl classes
        test_key = cnl_renderer.RESTRICTION_START_KEY + 10
        assert cnl_renderer.is_named_owl_class_key(test_key)

    def test_concept_group_key_not_type_handling(self, cnl_renderer):
        """Test handling of unknown concept types in concept_group_key."""
        # Create a mock that doesn't match any expected type
        unknown_mock = Mock()

        with pytest.raises(NotImplementedError):
            cnl_renderer.concept_group_key(unknown_mock)


class TestExtractDefinitionalPhrasesEdgeCases:
    """Tests for extract_definitional_phrases edge cases."""

    def test_empty_owl_classes_list(self, cnl_renderer):
        """Test with empty list of OWL classes."""
        definitional_phrases = []

        cnl_renderer.extract_definitional_phrases(
            definitional_phrases=definitional_phrases,
            owl_classes=[],
            owl_class_definition="test definition",
            owl_class_id="http://test.org/onto.owl#TestClass",
            owl_class_name_phrase="the test class",
        )

        assert isinstance(definitional_phrases, list)

    def test_empty_is_a_list(self, cnl_renderer):
        """Test OWL class with empty is_a list."""
        Person = cnl_renderer.ontology.search_one(iri="*Person")

        assert Person is not None

        # Create mock with no parent classes - must be ThingClass for isinstance check
        mock_dog = Mock(spec=ThingClass)
        mock_dog.label = ["dog"]
        mock_dog.is_a = []
        mock_dog.Classes = []

        definitional_phrases = []
        cnl_renderer.extract_definitional_phrases(
            definitional_phrases=definitional_phrases,
            owl_classes=[mock_dog],
            owl_class_definition="test dog",
            owl_class_id="http://test.org/onto.owl#Dog",
            owl_class_name_phrase="the test dog",
        )

        assert isinstance(definitional_phrases, list)

    def test_multiple_parent_classes(self, cnl_renderer):
        """Test OWL class with multiple parent classes."""
        Person = cnl_renderer.ontology.search_one(iri="*Person")
        Animal = cnl_renderer.ontology.search_one(iri="*Animal")

        assert Person is not None and Animal is not None

        # Create mock with multiple parents - must be ThingClass for isinstance check
        mock_dog = Mock(spec=ThingClass)
        mock_dog.label = ["dog"]
        mock_dog.is_a = [Person, Animal]
        mock_dog.Classes = []

        definitional_phrases = []
        cnl_renderer.extract_definitional_phrases(
            definitional_phrases=definitional_phrases,
            owl_classes=[mock_dog],
            owl_class_definition="test dog",
            owl_class_id="http://test.org/onto.owl#Dog",
            owl_class_name_phrase="the test dog",
        )

        assert isinstance(definitional_phrases, list)


class TestRoleRestrictionEdgeCases:
    """Tests for role restriction rendering edge cases."""

    def test_render_role_restriction_no_custom_phrasing(self, cnl_renderer):
        """Test rendering without custom phrasing (fallback to DL form)."""
        has_pet = cnl_renderer.ontology.search_one(iri="*has_pet")

        assert has_pet is not None

        # Disable custom rendering mode
        original_mode = cnl_renderer.custom_role_rendering
        cnl_renderer.custom_role_rendering = False

        rendered = cnl_renderer.render_role_restriction(
            has_pet, operand1="A", operand2="B"
        )

        # Restore mode
        cnl_renderer.custom_role_rendering = original_mode

        assert "has pet" in rendered.lower()

    def test_render_role_restriction_custom_phrasing(self, cnl_renderer):
        """Test rendering with custom phrasing enabled."""
        has_pet = cnl_renderer.ontology.search_one(iri="*has_pet")

        assert has_pet is not None

        # Enable custom rendering mode
        original_mode = cnl_renderer.custom_role_rendering
        cnl_renderer.custom_role_rendering = True

        rendered = cnl_renderer.render_role_restriction(
            has_pet, operand1="A", operand2="B"
        )

        # Restore mode
        cnl_renderer.custom_role_rendering = original_mode

    def test_role_restriction_wo_articles(self, cnl_renderer):
        """Test properties that should not have articles."""
        has_pet = cnl_renderer.ontology.search_one(iri="*has_pet")

        assert has_pet is not None

        # Add property to no-articles set
        prop_uri = URIRef(has_pet.iri)
        cnl_renderer.role_restriction_wo_articles.add(prop_uri)


class TestHandleFirstDefinitionalPhraseEdgeCases:
    """Tests for handle_first_definitional_phrase edge cases."""

    def test_with_ontology_title(self, cnl_renderer):
        """Test with ontology title set."""
        # Set a mock ontology title
        cnl_renderer.ontology_title = "Test Ontology"

        result = cnl_renderer.handle_first_definitional_phrase(
            definitional_phrases=[], name="person"
        )

        assert isinstance(result, str)

    def test_without_ontology_title(self, cnl_renderer):
        """Test without ontology title."""
        # Clear ontology title
        original_title = cnl_renderer.ontology_title
        cnl_renderer.ontology_title = None

        result = cnl_renderer.handle_first_definitional_phrase(
            definitional_phrases=[], name="person"
        )

        # Restore title
        cnl_renderer.ontology_title = original_title

        assert isinstance(result, str)

    def test_empty_phrases_list(self, cnl_renderer):
        """Test with empty phrases list."""
        result = cnl_renderer.handle_first_definitional_phrase(
            definitional_phrases=[], name="person"
        )

        assert isinstance(result, str)


class TestDefinitionInfoEdgeCases:
    """Tests for definition_info dictionary handling."""

    def test_definition_info_populated(self, cnl_renderer):
        """Test that definition_info is populated during processing."""
        Person = cnl_renderer.ontology.search_one(iri="*Person")

        assert Person is not None

        # Ensure mock has proper attributes for handling
        Person.equivalent_to = []
        Person.is_a = []

        # Process the class to populate definition_info
        definition = cnl_renderer.handle_owl_class(Person)

        # definition_info should now contain entries
        person_id = str(Person.iri) if hasattr(Person, "iri") else "person"
        # Note: definition_info keys are OWL class IDs/IRIs
        assert isinstance(definition, str)


class TestCustomRestrictionRenderingEdgeCases:
    """Tests for custom restriction rendering edge cases."""

    def test_custom_restriction_property_rendering(self, cnl_renderer):
        """Test adding custom rendering function for a property."""
        has_pet = cnl_renderer.ontology.search_one(iri="*has_pet")

        assert has_pet is not None

        # Add custom rendering function
        def custom_render(prop, operand1, operand2):
            return f"Custom: {operand1} {operand2}"

        prop_uri = URIRef(has_pet.iri)
        cnl_renderer.custom_restriction_property_rendering[prop_uri] = custom_render

        # Test that the custom function is used (this would require more complex mocking)


class TestRenderReadableOwlClassEdgeCases:
    """Tests for render_readable_owl_class edge cases."""

    def test_anonymous_class_rendering(self, cnl_renderer):
        """Test anonymous class rendering mode."""
        Person = cnl_renderer.ontology.search_one(iri="*Person")

        assert Person is not None

        rendered_named = cnl_renderer.render_readable_owl_class(
            Person, no_indef_article=False
        )
        rendered_anonymous = cnl_renderer.render_readable_owl_class(
            Person, no_indef_article=True
        )

        # Named should have article (if applicable), anonymous might differ

    def test_no_indefinite_article(self, cnl_renderer):
        """Test rendering without indefinite article."""
        Person = cnl_renderer.ontology.search_one(iri="*Person")

        assert Person is not None

        rendered_with = cnl_renderer.render_readable_owl_class(
            Person, no_indef_article=False
        )
        rendered_without = cnl_renderer.render_readable_owl_class(
            Person, no_indef_article=True
        )

        # Both should be valid strings


class TestPropertyIRILookupEdgeCases:
    """Tests for property IRI lookup edge cases."""

    def test_property_not_found_in_search(self, cnl_renderer):
        """Test when property is not found during search."""
        # Mock ontology to return empty list for unknown property
        original_search = cnl_renderer.ontology.search

        def mock_search(iri=None, label=None):
            if iri and "nonexistent" in str(iri):
                return []
            return original_search(iri=iri, label=label)

        cnl_renderer.ontology.search = Mock(side_effect=mock_search)

        # Should handle gracefully (might print warning or return None-based rendering)

    def test_property_with_multiple_labels(self, cnl_renderer):
        """Test property with multiple label values."""
        has_pet = cnl_renderer.ontology.search_one(iri="*has_pet")

        assert has_pet is not None

        # Add extra labels (simulating multi-language or alternative names)
        original_label = list(has_pet.label) if hasattr(has_pet, "label") else []
        has_pet.label = ["has pet", "pets", "pet relationship"]

        rendered = cnl_renderer.render_role_restriction(
            has_pet, operand1="A", operand2="B"
        )

        # Restore original label
        has_pet.label = original_label

        assert isinstance(rendered, str)
