"""
Tests for Question Generator classes.

Phase 31: Knowledge & Questions - Tests for question generation functionality.
Tests StandardQuestionGenerator and related question generation components.
"""

from unittest.mock import MagicMock

from local_deep_research.advanced_search_system.questions.standard_question import (
    StandardQuestionGenerator,
)


class TestStandardQuestionGenerator:
    """Tests for StandardQuestionGenerator implementation."""

    def test_initialization(self):
        """Test StandardQuestionGenerator initializes correctly."""
        mock_model = MagicMock()
        generator = StandardQuestionGenerator(mock_model)
        assert generator.model is mock_model

    def test_generate_questions_basic(self):
        """Test basic question generation."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = (
            "Q: What is the latest development?\nQ: How does it compare?"
        )
        mock_model.invoke.return_value = mock_response

        generator = StandardQuestionGenerator(mock_model)
        questions = generator.generate_questions(
            current_knowledge="Some knowledge about AI", query="What is AI?"
        )

        assert len(questions) == 2
        assert "What is the latest development?" in questions
        assert "How does it compare?" in questions

    def test_generate_questions_with_iteration_count(self):
        """Test question generation respects iteration count."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = (
            "Q: Question 1?\nQ: Question 2?\nQ: Question 3?\nQ: Question 4?"
        )
        mock_model.invoke.return_value = mock_response

        generator = StandardQuestionGenerator(mock_model)
        questions = generator.generate_questions(
            current_knowledge="Knowledge",
            query="Query",
            questions_per_iteration=2,
        )

        # Should be limited to questions_per_iteration
        assert len(questions) <= 2

    def test_generate_questions_with_past_questions(self):
        """Test question generation with past questions context."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = "Q: New question 1?\nQ: New question 2?"
        mock_model.invoke.return_value = mock_response

        generator = StandardQuestionGenerator(mock_model)
        questions = generator.generate_questions(
            current_knowledge="Current knowledge",
            query="Main query",
            questions_per_iteration=2,
            questions_by_iteration={1: ["Past question 1", "Past question 2"]},
        )

        assert len(questions) == 2
        call_args = mock_model.invoke.call_args[0][0]
        assert "Past questions:" in call_args

    def test_generate_questions_empty_response(self):
        """Test handling of empty response."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = ""
        mock_model.invoke.return_value = mock_response

        generator = StandardQuestionGenerator(mock_model)
        questions = generator.generate_questions(
            current_knowledge="Knowledge", query="Query"
        )

        assert questions == []

    def test_generate_questions_no_q_prefix(self):
        """Test handling response without Q: prefix."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = "Question 1\nQuestion 2"
        mock_model.invoke.return_value = mock_response

        generator = StandardQuestionGenerator(mock_model)
        questions = generator.generate_questions(
            current_knowledge="Knowledge", query="Query"
        )

        # Questions without Q: prefix should be filtered out
        assert questions == []

    def test_generate_questions_mixed_format(self):
        """Test handling mixed format response."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = (
            "Some text\nQ: Valid question?\nInvalid line\nQ: Another question?"
        )
        mock_model.invoke.return_value = mock_response

        generator = StandardQuestionGenerator(mock_model)
        questions = generator.generate_questions(
            current_knowledge="Knowledge",
            query="Query",
            questions_per_iteration=5,
        )

        assert len(questions) == 2
        assert "Valid question?" in questions
        assert "Another question?" in questions

    def test_generate_questions_string_response(self):
        """Test handling string response instead of object with content."""
        mock_model = MagicMock()
        mock_model.invoke.return_value = "Q: String response question?"

        generator = StandardQuestionGenerator(mock_model)
        questions = generator.generate_questions(
            current_knowledge="Knowledge", query="Query"
        )

        assert len(questions) == 1
        assert "String response question?" in questions

    def test_generate_questions_includes_timestamp(self):
        """Test question generation includes current timestamp."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = "Q: Question?"
        mock_model.invoke.return_value = mock_response

        generator = StandardQuestionGenerator(mock_model)
        generator.generate_questions(
            current_knowledge="Knowledge", query="Query"
        )

        call_args = mock_model.invoke.call_args[0][0]
        assert "Today:" in call_args

    def test_generate_sub_questions_success(self):
        """Test sub-question generation succeeds."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = (
            "1. Sub-question one\n2. Sub-question two\n3. Sub-question three"
        )
        mock_model.invoke.return_value = mock_response

        generator = StandardQuestionGenerator(mock_model)
        sub_questions = generator.generate_sub_questions("Complex main query")

        assert len(sub_questions) == 3
        assert "Sub-question one" in sub_questions
        assert "Sub-question two" in sub_questions

    def test_generate_sub_questions_with_context(self):
        """Test sub-question generation with context."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = "1. First sub-question\n2. Second sub-question"
        mock_model.invoke.return_value = mock_response

        generator = StandardQuestionGenerator(mock_model)
        sub_questions = generator.generate_sub_questions(
            "Main query", context="Focus on technical aspects"
        )

        assert len(sub_questions) == 2
        call_args = mock_model.invoke.call_args[0][0]
        assert "Focus on technical aspects" in call_args

    def test_generate_sub_questions_limits_to_five(self):
        """Test sub-question generation limits to 5 questions."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = """1. Question 1
2. Question 2
3. Question 3
4. Question 4
5. Question 5
6. Question 6
7. Question 7"""
        mock_model.invoke.return_value = mock_response

        generator = StandardQuestionGenerator(mock_model)
        sub_questions = generator.generate_sub_questions("Query")

        assert len(sub_questions) <= 5

    def test_generate_sub_questions_handles_bullets(self):
        """Test sub-question generation handles bullet points."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = (
            "- First bullet question\n- Second bullet question"
        )
        mock_model.invoke.return_value = mock_response

        generator = StandardQuestionGenerator(mock_model)
        sub_questions = generator.generate_sub_questions("Query")

        assert len(sub_questions) == 2

    def test_generate_sub_questions_error_handling(self):
        """Test sub-question generation handles errors."""
        mock_model = MagicMock()
        mock_model.invoke.side_effect = Exception("API error")

        generator = StandardQuestionGenerator(mock_model)
        sub_questions = generator.generate_sub_questions("Query")

        assert sub_questions == []

    def test_generate_sub_questions_empty_response(self):
        """Test sub-question generation with empty response."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = ""
        mock_model.invoke.return_value = mock_response

        generator = StandardQuestionGenerator(mock_model)
        sub_questions = generator.generate_sub_questions("Query")

        assert sub_questions == []

    def test_generate_sub_questions_string_response(self):
        """Test sub-question generation with string response."""
        mock_model = MagicMock()
        mock_model.invoke.return_value = "1. String response question"

        generator = StandardQuestionGenerator(mock_model)
        sub_questions = generator.generate_sub_questions("Query")

        assert len(sub_questions) == 1


class TestQuestionGeneratorPrompts:
    """Tests for question generator prompts."""

    def test_followup_prompt_without_past_questions(self):
        """Test prompt format without past questions."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = "Q: Question?"
        mock_model.invoke.return_value = mock_response

        generator = StandardQuestionGenerator(mock_model)
        generator.generate_questions(
            current_knowledge="Knowledge", query="Main query"
        )

        call_args = mock_model.invoke.call_args[0][0]
        assert "Main query" in call_args
        assert "Past questions:" not in call_args
        assert "outdated" in call_args.lower()

    def test_followup_prompt_with_past_questions(self):
        """Test prompt format with past questions."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = "Q: Question?"
        mock_model.invoke.return_value = mock_response

        generator = StandardQuestionGenerator(mock_model)
        generator.generate_questions(
            current_knowledge="Knowledge",
            query="Main query",
            questions_by_iteration={1: ["Old question"]},
        )

        call_args = mock_model.invoke.call_args[0][0]
        assert "Main query" in call_args
        assert "Past questions:" in call_args
        assert "Old question" in call_args
        assert "critically reflect" in call_args.lower()

    def test_sub_question_prompt_structure(self):
        """Test sub-question prompt has correct structure."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = "1. Sub question"
        mock_model.invoke.return_value = mock_response

        generator = StandardQuestionGenerator(mock_model)
        generator.generate_sub_questions("Complex query")

        call_args = mock_model.invoke.call_args[0][0]
        assert "Original Question:" in call_args
        assert "Complex query" in call_args
        assert "Break down" in call_args
        assert "2-5" in call_args


class TestQuestionGeneratorEdgeCases:
    """Edge case tests for question generator."""

    def test_handle_whitespace_in_questions(self):
        """Test handling of extra whitespace in questions."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = "Q:   Lots of spaces   \n  Q:  Another one  "
        mock_model.invoke.return_value = mock_response

        generator = StandardQuestionGenerator(mock_model)
        questions = generator.generate_questions(
            current_knowledge="Knowledge", query="Query"
        )

        assert all(not q.startswith(" ") for q in questions)
        assert all(not q.endswith(" ") for q in questions)

    def test_handle_unicode_content(self):
        """Test handling of unicode characters in questions."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = "Q: What about café?\nQ: How about naïve?"
        mock_model.invoke.return_value = mock_response

        generator = StandardQuestionGenerator(mock_model)
        questions = generator.generate_questions(
            current_knowledge="Knowledge", query="Query"
        )

        assert len(questions) == 2
        assert "café" in questions[0]

    def test_handle_long_knowledge(self):
        """Test handling of very long knowledge string."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = "Q: Question about long content?"
        mock_model.invoke.return_value = mock_response

        generator = StandardQuestionGenerator(mock_model)
        long_knowledge = "Knowledge content " * 10000

        questions = generator.generate_questions(
            current_knowledge=long_knowledge, query="Query"
        )

        # Should still work with long content
        mock_model.invoke.assert_called_once()
        assert len(questions) >= 0

    def test_handle_special_characters(self):
        """Test handling of special characters in query."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = "Q: Related question?"
        mock_model.invoke.return_value = mock_response

        generator = StandardQuestionGenerator(mock_model)
        _questions = generator.generate_questions(  # noqa: F841
            current_knowledge="Knowledge",
            query="What about <script>alert('xss')</script>?",
        )

        # Should handle special characters without breaking
        mock_model.invoke.assert_called_once()

    def test_handle_empty_knowledge(self):
        """Test handling of empty knowledge string."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = "Q: Basic question?"
        mock_model.invoke.return_value = mock_response

        generator = StandardQuestionGenerator(mock_model)
        questions = generator.generate_questions(
            current_knowledge="", query="Query"
        )

        assert len(questions) == 1

    def test_zero_questions_per_iteration(self):
        """Test handling of zero questions per iteration."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = "Q: Question 1?\nQ: Question 2?"
        mock_model.invoke.return_value = mock_response

        generator = StandardQuestionGenerator(mock_model)
        questions = generator.generate_questions(
            current_knowledge="Knowledge",
            query="Query",
            questions_per_iteration=0,
        )

        # Should return empty or limited list
        assert len(questions) <= 0


class TestQuestionGeneratorIntegration:
    """Integration tests for question generator."""

    def test_full_question_generation_workflow(self):
        """Test complete question generation workflow."""
        mock_model = MagicMock()

        # First call - initial questions
        first_response = MagicMock()
        first_response.content = (
            "Q: Initial question 1?\nQ: Initial question 2?"
        )

        # Second call - follow-up questions
        second_response = MagicMock()
        second_response.content = (
            "Q: Follow-up question 1?\nQ: Follow-up question 2?"
        )

        # Third call - sub-questions
        third_response = MagicMock()
        third_response.content = "1. Sub-question 1\n2. Sub-question 2"

        mock_model.invoke.side_effect = [
            first_response,
            second_response,
            third_response,
        ]

        generator = StandardQuestionGenerator(mock_model)

        # Generate initial questions
        initial = generator.generate_questions(
            current_knowledge="Initial knowledge", query="Main query"
        )
        assert len(initial) == 2

        # Generate follow-up questions with history
        followup = generator.generate_questions(
            current_knowledge="Updated knowledge",
            query="Main query",
            questions_by_iteration={1: initial},
        )
        assert len(followup) == 2

        # Generate sub-questions
        sub = generator.generate_sub_questions("Complex sub-topic")
        assert len(sub) == 2

    def test_multiple_iteration_tracking(self):
        """Test tracking questions across multiple iterations."""
        mock_model = MagicMock()
        mock_response = MagicMock()
        mock_response.content = "Q: New question?"
        mock_model.invoke.return_value = mock_response

        generator = StandardQuestionGenerator(mock_model)
        questions_history = {
            1: ["Q1 from iteration 1", "Q2 from iteration 1"],
            2: ["Q1 from iteration 2", "Q2 from iteration 2"],
            3: ["Q1 from iteration 3"],
        }

        generator.generate_questions(
            current_knowledge="Knowledge",
            query="Query",
            questions_by_iteration=questions_history,
        )

        call_args = mock_model.invoke.call_args[0][0]
        # All past questions should be included in context
        assert (
            "Q1 from iteration 1" in call_args
            or str(questions_history) in call_args
        )
