"""
Coverage tests for DecompositionQuestionGenerator.

Tests cover missing branches in:
- generate_questions: "No language models available" response -> default questions
- generate_questions: empty sub_queries from first attempt -> simplified prompt
- generate_questions: simplified prompt also returns LLM error -> default questions
- generate_questions: exception from model.invoke -> default questions
- generate_questions: respects max_subqueries limit
- _generate_default_questions: CSRF subject -> CSRF-specific questions
- _generate_default_questions: empty query -> generic default
- _generate_default_questions: security-related query
- _generate_default_questions: programming-related query
- generate_questions: compound question splitting at conjunction
- generate_questions: article removal ("the ", "a ", "an ")
"""

from unittest.mock import MagicMock


from local_deep_research.advanced_search_system.questions.decomposition_question import (
    DecompositionQuestionGenerator,
)

MODULE = "local_deep_research.advanced_search_system.questions.decomposition_question"


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------


def _make_generator(max_subqueries=5):
    model = MagicMock()
    return DecompositionQuestionGenerator(
        model, max_subqueries=max_subqueries
    ), model


def _response(text):
    m = MagicMock()
    m.content = text
    return m


# ---------------------------------------------------------------------------
# LLM error message -> default questions
# ---------------------------------------------------------------------------


class TestLLMErrorFallback:
    """When LLM returns error messages, default questions are used."""

    def test_no_language_models_available_triggers_default(self):
        gen, model = _make_generator()
        model.invoke.return_value = _response(
            "No language models are available. Please install Ollama."
        )

        result = gen.generate_questions("What is Python?", context="")

        assert len(result) >= 1
        # Should contain subject-derived questions (not the raw LLM error)
        assert all("No language models" not in q for q in result)

    def test_please_install_ollama_triggers_default(self):
        gen, model = _make_generator()
        model.invoke.return_value = _response(
            "Please install Ollama to continue."
        )

        result = gen.generate_questions("climate change", context="")

        assert len(result) >= 1


# ---------------------------------------------------------------------------
# Empty first response -> simplified prompt
# ---------------------------------------------------------------------------


class TestEmptyFirstResponseSimplifiedPrompt:
    """Empty LLM output triggers a simplified second attempt."""

    def test_empty_first_simplified_second_succeeds(self):
        gen, model = _make_generator()
        # First call: empty content; second call: valid questions
        model.invoke.side_effect = [
            _response(""),
            _response(
                "1. What is it?\n2. How does it work?\n3. Why does it matter?"
            ),
        ]

        result = gen.generate_questions("machine learning", context="")

        assert len(result) >= 1
        assert model.invoke.call_count == 2

    def test_empty_both_attempts_uses_default(self):
        gen, model = _make_generator()
        model.invoke.side_effect = [_response(""), _response("")]

        result = gen.generate_questions("quantum computing", context="")

        assert len(result) >= 1
        assert all(isinstance(q, str) for q in result)


# ---------------------------------------------------------------------------
# Simplified prompt also returns LLM error
# ---------------------------------------------------------------------------


class TestSimplifiedPromptLLMError:
    """When simplified prompt also returns error, default questions used."""

    def test_simplified_prompt_ollama_error_returns_default(self):
        gen, model = _make_generator()
        model.invoke.side_effect = [
            _response(""),
            _response(
                "No language models are available. Please install Ollama."
            ),
        ]

        result = gen.generate_questions("neural networks", context="")

        assert len(result) >= 1


# ---------------------------------------------------------------------------
# Exception from model.invoke
# ---------------------------------------------------------------------------


class TestExceptionFallback:
    """Exception from model.invoke falls back to default questions."""

    def test_exception_returns_default_questions(self):
        gen, model = _make_generator()
        model.invoke.side_effect = RuntimeError("connection failed")

        result = gen.generate_questions("cryptography", context="")

        assert len(result) >= 1
        assert all(isinstance(q, str) for q in result)


# ---------------------------------------------------------------------------
# max_subqueries limit
# ---------------------------------------------------------------------------


class TestMaxSubqueriesLimit:
    """Result is capped at max_subqueries."""

    def test_result_capped_at_max_subqueries(self):
        gen, model = _make_generator(max_subqueries=2)
        model.invoke.return_value = _response(
            "Q1: What is A?\nQ2: How does B work?\nQ3: Why C?\nQ4: When D?\nQ5: Where E?"
        )

        result = gen.generate_questions("topic", context="")

        assert len(result) <= 2


# ---------------------------------------------------------------------------
# _generate_default_questions: special cases
# ---------------------------------------------------------------------------


class TestGenerateDefaultQuestionsSpecialCases:
    """_generate_default_questions handles special subjects."""

    def test_csrf_subject_returns_csrf_questions(self):
        gen, _ = _make_generator()
        result = gen._generate_default_questions("What is CSRF?")
        assert any("CSRF" in q or "Cross-Site" in q for q in result)

    def test_cross_site_request_forgery_subject(self):
        gen, _ = _make_generator()
        result = gen._generate_default_questions("cross-site request forgery")
        assert any("CSRF" in q or "Cross-Site" in q for q in result)

    def test_empty_query_returns_generic_defaults(self):
        gen, _ = _make_generator()
        result = gen._generate_default_questions("")
        assert len(result) >= 1

    def test_security_query_returns_security_questions(self):
        gen, _ = _make_generator()
        result = gen._generate_default_questions("SQL injection vulnerability")
        assert any(
            "vulnerabilit" in q.lower()
            or "attack" in q.lower()
            or "SQL injection" in q
            for q in result
        )

    def test_programming_query_returns_programming_questions(self):
        gen, _ = _make_generator()
        result = gen._generate_default_questions("Python programming language")
        assert len(result) >= 1
        assert all(isinstance(q, str) for q in result)


# ---------------------------------------------------------------------------
# Subject extraction from questions
# ---------------------------------------------------------------------------


class TestSubjectExtraction:
    """Subject is correctly extracted before generating default questions."""

    def test_what_is_prefix_removed(self):
        gen, model = _make_generator()
        model.invoke.side_effect = RuntimeError("fail")
        result = gen.generate_questions("What is machine learning?", context="")
        # All questions should mention the extracted subject
        assert len(result) >= 1

    def test_article_removed_from_subject(self):
        gen, _ = _make_generator()
        result = gen._generate_default_questions("What is the blockchain?")
        # "the" should be stripped, leaving "blockchain"
        assert any("blockchain" in q.lower() for q in result)

    def test_compound_question_split_at_and(self):
        gen, _ = _make_generator()
        result = gen._generate_default_questions(
            "What is Python and how does it work?"
        )
        # Split at " and " -> subject is "Python"
        assert len(result) >= 1
