"""
Comprehensive tests for StandardCitationHandler.
Tests initialization, analyze_initial, analyze_followup, and edge cases.
"""

from datetime import datetime, timezone
from unittest.mock import Mock


class TestStandardCitationHandlerInit:
    """Tests for StandardCitationHandler initialization."""

    def test_init_with_llm_only(self, mock_llm):
        """Test initialization with just an LLM."""
        from local_deep_research.citation_handlers.standard_citation_handler import (
            StandardCitationHandler,
        )

        handler = StandardCitationHandler(llm=mock_llm)

        assert handler.llm == mock_llm
        assert handler.settings_snapshot == {}

    def test_init_with_settings_snapshot(
        self, mock_llm, settings_with_fact_checking
    ):
        """Test initialization with settings snapshot."""
        from local_deep_research.citation_handlers.standard_citation_handler import (
            StandardCitationHandler,
        )

        handler = StandardCitationHandler(
            llm=mock_llm, settings_snapshot=settings_with_fact_checking
        )

        assert handler.settings_snapshot == settings_with_fact_checking

    def test_init_with_none_settings(self, mock_llm):
        """Test initialization with None settings defaults to empty dict."""
        from local_deep_research.citation_handlers.standard_citation_handler import (
            StandardCitationHandler,
        )

        handler = StandardCitationHandler(llm=mock_llm, settings_snapshot=None)

        assert handler.settings_snapshot == {}


class TestStandardCitationHandlerGetSetting:
    """Tests for get_setting method inherited from base."""

    def test_get_setting_returns_value(
        self, mock_llm, settings_with_fact_checking
    ):
        """Test get_setting returns correct value."""
        from local_deep_research.citation_handlers.standard_citation_handler import (
            StandardCitationHandler,
        )

        handler = StandardCitationHandler(
            llm=mock_llm, settings_snapshot=settings_with_fact_checking
        )

        result = handler.get_setting("general.enable_fact_checking")

        assert result is True

    def test_get_setting_returns_default_for_missing_key(self, mock_llm):
        """Test get_setting returns default for missing key."""
        from local_deep_research.citation_handlers.standard_citation_handler import (
            StandardCitationHandler,
        )

        handler = StandardCitationHandler(llm=mock_llm)

        result = handler.get_setting("nonexistent.key", default="default_value")

        assert result == "default_value"

    def test_get_setting_extracts_value_from_dict(
        self, mock_llm, settings_with_dict_value
    ):
        """Test get_setting extracts value from dict format."""
        from local_deep_research.citation_handlers.standard_citation_handler import (
            StandardCitationHandler,
        )

        handler = StandardCitationHandler(
            llm=mock_llm, settings_snapshot=settings_with_dict_value
        )

        result = handler.get_setting("general.enable_fact_checking")

        assert result is True


class TestStandardCitationHandlerAnalyzeInitial:
    """Tests for analyze_initial method."""

    def test_analyze_initial_returns_content_and_documents(
        self, mock_llm, sample_search_results
    ):
        """Test analyze_initial returns proper structure."""
        from local_deep_research.citation_handlers.standard_citation_handler import (
            StandardCitationHandler,
        )

        handler = StandardCitationHandler(llm=mock_llm)

        result = handler.analyze_initial(
            "What is the topic?", sample_search_results
        )

        assert "content" in result
        assert "documents" in result
        assert len(result["documents"]) == 3

    def test_analyze_initial_calls_llm_invoke(
        self, mock_llm, sample_search_results
    ):
        """Test analyze_initial calls LLM invoke."""
        from local_deep_research.citation_handlers.standard_citation_handler import (
            StandardCitationHandler,
        )

        handler = StandardCitationHandler(llm=mock_llm)

        handler.analyze_initial("Test query", sample_search_results)

        mock_llm.invoke.assert_called_once()

    def test_analyze_initial_includes_query_in_prompt(
        self, mock_llm, sample_search_results
    ):
        """Test analyze_initial includes query in LLM prompt."""
        from local_deep_research.citation_handlers.standard_citation_handler import (
            StandardCitationHandler,
        )

        handler = StandardCitationHandler(llm=mock_llm)

        handler.analyze_initial("Specific test query", sample_search_results)

        call_args = mock_llm.invoke.call_args[0][0]
        assert "Specific test query" in call_args

    def test_analyze_initial_includes_sources_in_prompt(
        self, mock_llm, sample_search_results
    ):
        """Test analyze_initial includes formatted sources in prompt."""
        from local_deep_research.citation_handlers.standard_citation_handler import (
            StandardCitationHandler,
        )

        handler = StandardCitationHandler(llm=mock_llm)

        handler.analyze_initial("Test query", sample_search_results)

        call_args = mock_llm.invoke.call_args[0][0]
        assert "[1]" in call_args
        assert "full content of the first test article" in call_args

    def test_analyze_initial_includes_timestamp(
        self, mock_llm, sample_search_results
    ):
        """Test analyze_initial includes current timestamp."""
        from local_deep_research.citation_handlers.standard_citation_handler import (
            StandardCitationHandler,
        )

        handler = StandardCitationHandler(llm=mock_llm)

        handler.analyze_initial("Test query", sample_search_results)

        call_args = mock_llm.invoke.call_args[0][0]
        assert "UTC" in call_args
        # Should contain year
        current_year = str(datetime.now(timezone.utc).year)
        assert current_year in call_args

    def test_analyze_initial_handles_string_response(
        self, mock_llm_string_response, sample_search_results
    ):
        """Test analyze_initial handles string LLM response."""
        from local_deep_research.citation_handlers.standard_citation_handler import (
            StandardCitationHandler,
        )

        handler = StandardCitationHandler(llm=mock_llm_string_response)

        result = handler.analyze_initial("Test query", sample_search_results)

        assert result["content"] == "Test string response with citation [1]."

    def test_analyze_initial_handles_object_response(
        self, mock_llm, sample_search_results
    ):
        """Test analyze_initial handles object LLM response with .content."""
        from local_deep_research.citation_handlers.standard_citation_handler import (
            StandardCitationHandler,
        )

        handler = StandardCitationHandler(llm=mock_llm)

        result = handler.analyze_initial("Test query", sample_search_results)

        assert result["content"] == "Test response with citation [1]."

    def test_analyze_initial_strips_think_tags(
        self, mock_llm, sample_search_results
    ):
        """Test analyze_initial strips <think> reasoning from the response."""
        from local_deep_research.citation_handlers.standard_citation_handler import (
            StandardCitationHandler,
        )

        mock_llm.invoke.return_value = Mock(
            content="<think>internal reasoning</think>Answer with citation [1]."
        )

        handler = StandardCitationHandler(llm=mock_llm)

        result = handler.analyze_initial("Test query", sample_search_results)

        assert "<think>" not in result["content"]
        assert "internal reasoning" not in result["content"]
        assert result["content"] == "Answer with citation [1]."

    def test_analyze_initial_with_output_instructions(
        self, mock_llm, sample_search_results, settings_with_output_instructions
    ):
        """Test analyze_initial includes output instructions in prompt."""
        from local_deep_research.citation_handlers.standard_citation_handler import (
            StandardCitationHandler,
        )

        handler = StandardCitationHandler(
            llm=mock_llm, settings_snapshot=settings_with_output_instructions
        )

        handler.analyze_initial("Test query", sample_search_results)

        call_args = mock_llm.invoke.call_args[0][0]
        assert "formal academic English" in call_args

    def test_analyze_initial_with_empty_results(
        self, mock_llm, empty_search_results
    ):
        """Test analyze_initial with empty search results."""
        from local_deep_research.citation_handlers.standard_citation_handler import (
            StandardCitationHandler,
        )

        handler = StandardCitationHandler(llm=mock_llm)

        result = handler.analyze_initial("Test query", empty_search_results)

        assert result["documents"] == []

    def test_analyze_initial_with_string_results(
        self, mock_llm, string_search_results
    ):
        """Test analyze_initial with string search results (edge case)."""
        from local_deep_research.citation_handlers.standard_citation_handler import (
            StandardCitationHandler,
        )

        handler = StandardCitationHandler(llm=mock_llm)

        result = handler.analyze_initial("Test query", string_search_results)

        assert result["documents"] == []


class TestStandardCitationHandlerAnalyzeFollowup:
    """Tests for analyze_followup method."""

    def test_analyze_followup_returns_content_and_documents(
        self, mock_llm, sample_search_results, sample_previous_knowledge
    ):
        """Test analyze_followup returns proper structure."""
        from local_deep_research.citation_handlers.standard_citation_handler import (
            StandardCitationHandler,
        )

        handler = StandardCitationHandler(llm=mock_llm)

        result = handler.analyze_followup(
            "Follow-up question?",
            sample_search_results,
            sample_previous_knowledge,
            nr_of_links=3,
        )

        assert "content" in result
        assert "documents" in result

    def test_analyze_followup_with_fact_checking_enabled(
        self,
        mock_llm,
        sample_search_results,
        sample_previous_knowledge,
        settings_with_fact_checking,
    ):
        """Test analyze_followup calls LLM twice when fact-checking enabled."""
        from local_deep_research.citation_handlers.standard_citation_handler import (
            StandardCitationHandler,
        )

        handler = StandardCitationHandler(
            llm=mock_llm, settings_snapshot=settings_with_fact_checking
        )

        handler.analyze_followup(
            "Follow-up question?",
            sample_search_results,
            sample_previous_knowledge,
            nr_of_links=0,
        )

        # Should call LLM twice: once for fact-checking, once for main response
        assert mock_llm.invoke.call_count == 2

    def test_analyze_followup_with_fact_checking_disabled(
        self,
        mock_llm,
        sample_search_results,
        sample_previous_knowledge,
        settings_without_fact_checking,
    ):
        """Test analyze_followup calls LLM once when fact-checking disabled."""
        from local_deep_research.citation_handlers.standard_citation_handler import (
            StandardCitationHandler,
        )

        handler = StandardCitationHandler(
            llm=mock_llm, settings_snapshot=settings_without_fact_checking
        )

        handler.analyze_followup(
            "Follow-up question?",
            sample_search_results,
            sample_previous_knowledge,
            nr_of_links=0,
        )

        # Should call LLM only once
        assert mock_llm.invoke.call_count == 1

    def test_analyze_followup_includes_previous_knowledge(
        self, mock_llm, sample_search_results, sample_previous_knowledge
    ):
        """Test analyze_followup includes previous knowledge in prompt."""
        from local_deep_research.citation_handlers.standard_citation_handler import (
            StandardCitationHandler,
        )

        handler = StandardCitationHandler(
            llm=mock_llm,
            settings_snapshot={"general.enable_fact_checking": False},
        )

        handler.analyze_followup(
            "Follow-up question?",
            sample_search_results,
            sample_previous_knowledge,
            nr_of_links=0,
        )

        call_args = mock_llm.invoke.call_args[0][0]
        assert "first studied in 1995" in call_args

    def test_analyze_followup_uses_nr_of_links_offset(
        self, mock_llm, sample_search_results, sample_previous_knowledge
    ):
        """Test analyze_followup applies nr_of_links offset to document indices."""
        from local_deep_research.citation_handlers.standard_citation_handler import (
            StandardCitationHandler,
        )

        handler = StandardCitationHandler(
            llm=mock_llm,
            settings_snapshot={"general.enable_fact_checking": False},
        )

        result = handler.analyze_followup(
            "Follow-up question?",
            sample_search_results,
            sample_previous_knowledge,
            nr_of_links=5,
        )

        # Documents should have indices starting at 6 (5 + 1)
        assert result["documents"][0].metadata["index"] == 6
        assert result["documents"][1].metadata["index"] == 7
        assert result["documents"][2].metadata["index"] == 8

    def test_analyze_followup_fact_check_prompt_structure(
        self, mock_llm, sample_search_results, sample_previous_knowledge
    ):
        """Test fact-check prompt includes required elements."""
        from local_deep_research.citation_handlers.standard_citation_handler import (
            StandardCitationHandler,
        )

        handler = StandardCitationHandler(
            llm=mock_llm,
            settings_snapshot={"general.enable_fact_checking": True},
        )

        handler.analyze_followup(
            "Follow-up question?",
            sample_search_results,
            sample_previous_knowledge,
            nr_of_links=0,
        )

        # First call should be fact-check
        first_call_args = mock_llm.invoke.call_args_list[0][0][0]
        assert "Cross-reference" in first_call_args
        assert "contradictions" in first_call_args

    def test_analyze_followup_handles_string_response(
        self,
        mock_llm_string_response,
        sample_search_results,
        sample_previous_knowledge,
    ):
        """analyze_followup returns raw string responses unchanged (fact-check off)."""
        from local_deep_research.citation_handlers.standard_citation_handler import (
            StandardCitationHandler,
        )

        handler = StandardCitationHandler(
            llm=mock_llm_string_response,
            settings_snapshot={"general.enable_fact_checking": False},
        )

        result = handler.analyze_followup(
            "Follow-up question?",
            sample_search_results,
            sample_previous_knowledge,
            nr_of_links=0,
        )

        assert result["content"] == "Test string response with citation [1]."
        assert mock_llm_string_response.invoke.call_count == 1

    def test_analyze_followup_string_response_with_fact_checking(
        self,
        mock_llm_string_response,
        sample_search_results,
        sample_previous_knowledge,
        settings_with_fact_checking,
    ):
        """With fact-check on, string responses pass through and the LLM is called twice."""
        from local_deep_research.citation_handlers.standard_citation_handler import (
            StandardCitationHandler,
        )

        handler = StandardCitationHandler(
            llm=mock_llm_string_response,
            settings_snapshot=settings_with_fact_checking,
        )

        result = handler.analyze_followup(
            "Follow-up question?",
            sample_search_results,
            sample_previous_knowledge,
            nr_of_links=0,
        )

        assert result["content"] == "Test string response with citation [1]."
        assert mock_llm_string_response.invoke.call_count == 2

    def test_analyze_followup_strips_think_tags(
        self,
        mock_llm,
        sample_search_results,
        sample_previous_knowledge,
    ):
        """analyze_followup strips <think>...</think> reasoning from the response."""
        from local_deep_research.citation_handlers.standard_citation_handler import (
            StandardCitationHandler,
        )

        mock_llm.invoke.return_value = Mock(
            content="<think>internal reasoning here</think>Answer with citation [1]."
        )

        handler = StandardCitationHandler(
            llm=mock_llm,
            settings_snapshot={"general.enable_fact_checking": False},
        )

        result = handler.analyze_followup(
            "Follow-up question?",
            sample_search_results,
            sample_previous_knowledge,
            nr_of_links=0,
        )

        assert "<think>" not in result["content"]
        assert "internal reasoning" not in result["content"]
        assert result["content"] == "Answer with citation [1]."


class TestStandardCitationHandlerDocumentCreation:
    """Tests for document creation functionality."""

    def test_documents_have_correct_metadata(
        self, mock_llm, sample_search_results
    ):
        """Test created documents have correct metadata."""
        from local_deep_research.citation_handlers.standard_citation_handler import (
            StandardCitationHandler,
        )

        handler = StandardCitationHandler(llm=mock_llm)

        result = handler.analyze_initial("Test query", sample_search_results)

        doc = result["documents"][0]
        assert doc.metadata["source"] == "https://example.com/article1"
        assert doc.metadata["title"] == "Test Article 1"
        assert doc.metadata["index"] == 1

    def test_documents_use_full_content_when_available(
        self, mock_llm, sample_search_results
    ):
        """Test documents use full_content over snippet."""
        from local_deep_research.citation_handlers.standard_citation_handler import (
            StandardCitationHandler,
        )

        handler = StandardCitationHandler(llm=mock_llm)

        result = handler.analyze_initial("Test query", sample_search_results)

        # page_content should be the full content, not snippet
        assert (
            "full content of the first test article"
            in result["documents"][0].page_content
        )

    def test_documents_fallback_to_snippet(self, mock_llm):
        """Test documents fall back to snippet when full_content missing."""
        from local_deep_research.citation_handlers.standard_citation_handler import (
            StandardCitationHandler,
        )

        search_results = [
            {
                "title": "Test",
                "link": "https://example.com",
                "snippet": "Only snippet available",
            }
        ]

        handler = StandardCitationHandler(llm=mock_llm)

        result = handler.analyze_initial("Test query", search_results)

        assert result["documents"][0].page_content == "Only snippet available"

    def test_documents_preserve_existing_index(self, mock_llm):
        """Test documents preserve pre-existing index."""
        from local_deep_research.citation_handlers.standard_citation_handler import (
            StandardCitationHandler,
        )

        search_results = [
            {
                "title": "Test",
                "link": "https://example.com",
                "snippet": "Test",
                "index": "42",  # Pre-existing index
            }
        ]

        handler = StandardCitationHandler(llm=mock_llm)

        result = handler.analyze_initial("Test query", search_results)

        assert result["documents"][0].metadata["index"] == 42


class TestStandardCitationHandlerSourceFormatting:
    """Tests for source formatting functionality."""

    def test_format_sources_includes_index(
        self, mock_llm, sample_search_results
    ):
        """Test formatted sources include citation indices."""
        from local_deep_research.citation_handlers.standard_citation_handler import (
            StandardCitationHandler,
        )

        handler = StandardCitationHandler(llm=mock_llm)

        handler.analyze_initial("Test query", sample_search_results)

        call_args = mock_llm.invoke.call_args[0][0]
        assert "[1]" in call_args
        assert "[2]" in call_args
        assert "[3]" in call_args

    def test_format_sources_separates_with_newlines(
        self, mock_llm, sample_search_results
    ):
        """Test formatted sources are separated by double newlines."""
        from local_deep_research.citation_handlers.standard_citation_handler import (
            StandardCitationHandler,
        )

        handler = StandardCitationHandler(llm=mock_llm)

        handler.analyze_initial("Test query", sample_search_results)

        call_args = mock_llm.invoke.call_args[0][0]
        # Sources should be separated by \n\n
        assert "\n\n" in call_args
