"""
Extended tests for research_functions API - Programmatic research access.

Tests cover:
- Search system initialization
- Quick summary generation
- Report generation
- Detailed research
- Document analysis
- Settings handling
- Error handling and edge cases
"""

from datetime import datetime, UTC


class TestSearchSystemInitialization:
    """Tests for _init_search_system function."""

    def test_default_search_strategy(self):
        """Default search strategy should be source_based."""
        default_strategy = "source_based"
        assert default_strategy == "source_based"

    def test_default_iterations(self):
        """Default iterations should be 1."""
        default_iterations = 1
        assert default_iterations == 1

    def test_default_questions_per_iteration(self):
        """Default questions per iteration should be 1."""
        default_questions = 1
        assert default_questions == 1

    def test_default_temperature(self):
        """Default temperature should be 0.7."""
        default_temp = 0.7
        assert default_temp == 0.7

    def test_programmatic_mode_default_true(self):
        """Programmatic mode should default to True for API."""
        programmatic_mode = True
        assert programmatic_mode is True

    def test_search_original_query_default_true(self):
        """Search original query should default to True."""
        search_original_query = True
        assert search_original_query is True

    def test_retriever_registration_format(self):
        """Retrievers should be registered as dict."""
        retrievers = {"custom": "retriever_instance"}
        assert "custom" in retrievers
        assert isinstance(retrievers, dict)

    def test_llm_registration_format(self):
        """LLMs should be registered as dict."""
        llms = {"custom_llm": "llm_instance"}
        assert "custom_llm" in llms
        assert isinstance(llms, dict)


class TestQuickSummary:
    """Tests for quick_summary function."""

    def test_required_query_parameter(self):
        """Query parameter is required."""
        query = "What is quantum computing?"
        assert query is not None
        assert len(query) > 0

    def test_return_structure_has_summary(self):
        """Return should have summary key."""
        result = {
            "summary": "Summary text",
            "findings": [],
            "iterations": 1,
            "questions": {},
        }
        assert "summary" in result

    def test_return_structure_has_findings(self):
        """Return should have findings key."""
        result = {
            "summary": "Summary text",
            "findings": [{"content": "finding1"}],
        }
        assert "findings" in result

    def test_return_structure_has_iterations(self):
        """Return should have iterations key."""
        result = {
            "summary": "Summary text",
            "iterations": 3,
        }
        assert "iterations" in result
        assert result["iterations"] == 3

    def test_return_structure_has_questions(self):
        """Return should have questions key."""
        result = {
            "summary": "Summary text",
            "questions": {"1": ["Q1", "Q2"]},
        }
        assert "questions" in result

    def test_return_structure_has_sources(self):
        """Return should have sources key."""
        result = {
            "summary": "Summary text",
            "sources": ["http://example.com"],
        }
        assert "sources" in result

    def test_research_id_auto_generation(self):
        """Research ID should be auto-generated if not provided."""
        import uuid

        research_id = None
        if research_id is None:
            research_id = str(uuid.uuid4())

        assert research_id is not None
        assert len(research_id) == 36  # UUID format

    def test_search_context_structure(self):
        """Search context should have required fields."""
        query = "test query"
        research_id = "test-id"

        search_context = {
            "research_id": research_id,
            "research_query": query,
            "research_mode": "quick",
            "research_phase": "init",
            "search_iteration": 0,
        }

        assert search_context["research_mode"] == "quick"
        assert search_context["research_phase"] == "init"


class TestGenerateReport:
    """Tests for generate_report function."""

    def test_output_file_optional(self):
        """Output file parameter should be optional."""
        output_file = None
        assert output_file is None

    def test_default_searches_per_section(self):
        """Default searches per section should be 2."""
        default_searches = 2
        assert default_searches == 2

    def test_return_has_content(self):
        """Return should have content key."""
        result = {
            "content": "# Report\n\nContent here",
            "metadata": {},
        }
        assert "content" in result

    def test_return_has_metadata(self):
        """Return should have metadata key."""
        result = {
            "content": "Report",
            "metadata": {"timestamp": "2024-01-01"},
        }
        assert "metadata" in result

    def test_file_path_in_return_when_saved(self):
        """File path should be in return when saved."""
        result = {
            "content": "Report",
            "file_path": "/path/to/report.md",
        }
        assert "file_path" in result

    def test_progress_callback_optional(self):
        """Progress callback should be optional."""
        progress_callback = None
        assert progress_callback is None


class TestDetailedResearch:
    """Tests for detailed_research function."""

    def test_return_has_query(self):
        """Return should have query key."""
        result = {
            "query": "test query",
            "research_id": "id",
        }
        assert "query" in result

    def test_return_has_research_id(self):
        """Return should have research_id key."""
        result = {
            "query": "test",
            "research_id": "test-id-123",
        }
        assert "research_id" in result

    def test_return_has_metadata(self):
        """Return should have metadata with details."""
        result = {
            "metadata": {
                "timestamp": datetime.now(UTC).isoformat(),
                "search_tool": "auto",
                "iterations_requested": 1,
                "strategy": "source_based",
            }
        }
        assert "timestamp" in result["metadata"]
        assert "strategy" in result["metadata"]

    def test_metadata_timestamp_format(self):
        """Metadata timestamp should be ISO format."""
        timestamp = datetime.now(UTC).isoformat()

        # Should contain T separator
        assert "T" in timestamp

    def test_default_search_tool_auto(self):
        """Default search tool should be 'auto'."""
        search_tool = "auto"
        assert search_tool == "auto"


class TestAnalyzeDocuments:
    """Tests for analyze_documents function."""

    def test_collection_name_required(self):
        """Collection name parameter is required."""
        collection_name = "my_collection"
        assert collection_name is not None

    def test_default_max_results(self):
        """Default max results should be 10."""
        max_results = 10
        assert max_results == 10

    def test_default_temperature(self):
        """Default temperature should be 0.7."""
        temperature = 0.7
        assert temperature == 0.7

    def test_force_reindex_default_false(self):
        """Force reindex should default to False."""
        force_reindex = False
        assert force_reindex is False

    def test_return_has_summary(self):
        """Return should have summary key."""
        result = {
            "summary": "Analysis summary",
            "documents": [],
        }
        assert "summary" in result

    def test_return_has_documents(self):
        """Return should have documents key."""
        result = {
            "summary": "Summary",
            "documents": [{"title": "Doc1"}],
        }
        assert "documents" in result

    def test_return_has_collection_name(self):
        """Return should have collection name."""
        result = {
            "summary": "Summary",
            "documents": [],
            "collection": "my_collection",
        }
        assert result["collection"] == "my_collection"

    def test_return_has_document_count(self):
        """Return should have document count."""
        result = {
            "summary": "Summary",
            "documents": [{"title": "D1"}, {"title": "D2"}],
            "document_count": 2,
        }
        assert result["document_count"] == 2

    def test_collection_not_found_error(self):
        """Should return error when collection not found."""
        collection_name = "nonexistent"
        search = None

        if not search:
            result = {
                "summary": f"Error: Collection '{collection_name}' not found",
                "documents": [],
            }
        else:
            result = {"summary": "Found", "documents": []}

        assert "not found" in result["summary"]

    def test_no_documents_found_message(self):
        """Should return message when no documents found."""
        collection_name = "my_collection"
        query = "test query"
        results = []

        if not results:
            summary = f"No documents found in collection '{collection_name}' for query: '{query}'"
        else:
            summary = "Found documents"

        assert "No documents found" in summary


class TestSettingsSnapshot:
    """Tests for settings snapshot handling."""

    def test_snapshot_from_explicit_params(self):
        """Should build snapshot from explicit parameters."""
        provider = "openai"
        api_key = "sk-test"
        temperature = 0.5

        snapshot_kwargs = {}
        if provider is not None:
            snapshot_kwargs["provider"] = provider
        if api_key is not None:
            snapshot_kwargs["api_key"] = api_key
        if temperature is not None:
            snapshot_kwargs["temperature"] = temperature

        assert snapshot_kwargs["provider"] == "openai"
        assert snapshot_kwargs["temperature"] == 0.5

    def test_snapshot_overrides(self):
        """Should apply settings overrides."""
        settings_override = {
            "llm.max_tokens": 4000,
            "search.engines.arxiv.enabled": True,
        }

        assert "llm.max_tokens" in settings_override
        assert settings_override["llm.max_tokens"] == 4000

    def test_base_settings_support(self):
        """Should support base settings dict."""
        base_settings = {
            "llm.provider": "anthropic",
            "search.tool": "wikipedia",
        }

        assert isinstance(base_settings, dict)
        assert "llm.provider" in base_settings


class TestSearchContextSetup:
    """Tests for search context setup."""

    def test_context_has_research_id(self):
        """Context should have research_id."""
        context = {
            "research_id": "test-123",
            "research_query": "test",
        }
        assert "research_id" in context

    def test_context_has_research_query(self):
        """Context should have research_query."""
        context = {
            "research_id": "id",
            "research_query": "What is AI?",
        }
        assert context["research_query"] == "What is AI?"

    def test_context_has_research_mode(self):
        """Context should have research_mode."""
        context = {
            "research_mode": "quick",
        }
        assert context["research_mode"] == "quick"

    def test_context_has_research_phase(self):
        """Context should have research_phase."""
        context = {
            "research_phase": "init",
        }
        assert context["research_phase"] == "init"

    def test_context_has_search_iteration(self):
        """Context should have search_iteration."""
        context = {
            "search_iteration": 0,
        }
        assert context["search_iteration"] == 0


class TestErrorHandling:
    """Tests for error handling."""

    def test_unable_to_generate_summary_fallback(self):
        """Should have fallback message for failed summary."""
        results = None

        if results and "current_knowledge" in results:
            summary = results["current_knowledge"]
        else:
            summary = "Unable to generate summary for the query."

        assert summary == "Unable to generate summary for the query."

    def test_search_engine_creation_warning(self):
        """Should warn when search engine creation fails."""
        search_tool = "invalid_engine"
        search_engine = None

        if search_engine is None:
            warning = f"Could not create search engine '{search_tool}', using default."
        else:
            warning = None

        assert warning is not None
        assert "invalid_engine" in warning


class TestRetrieverRegistration:
    """Tests for retriever registration."""

    def test_register_multiple_retrievers(self):
        """Should register multiple retrievers."""
        retrievers = {
            "custom1": "retriever1",
            "custom2": "retriever2",
        }

        registered_count = len(retrievers)
        registered_names = list(retrievers.keys())

        assert registered_count == 2
        assert "custom1" in registered_names


class TestLLMRegistration:
    """Tests for LLM registration."""

    def test_register_multiple_llms(self):
        """Should register multiple LLMs."""
        llms = {
            "llm1": "instance1",
            "llm2": "instance2",
        }

        registered_count = len(llms)
        assert registered_count == 2

    def test_llm_name_in_registration(self):
        """LLM name should be preserved in registration."""
        llms = {"my_custom_llm": "instance"}

        for name, _instance in llms.items():
            assert name == "my_custom_llm"


class TestOutputFileSaving:
    """Tests for output file saving."""

    def test_report_content_format(self):
        """Report content should be markdown format."""
        content = "# Report Title\n\n## Section 1\n\nContent..."

        assert content.startswith("#")
        assert "##" in content

    def test_analysis_output_format(self):
        """Analysis output should include all sections."""
        query = "test query"
        summary = "Analysis summary"
        doc_count = 5

        content = f"# Document Analysis: {query}\n\n"
        content += f"## Summary\n\n{summary}\n\n"
        content += f"## Documents Found: {doc_count}\n\n"

        assert "Document Analysis" in content
        assert "Summary" in content
        assert "Documents Found: 5" in content


class TestProgressCallback:
    """Tests for progress callback support."""

    def test_callback_function_optional(self):
        """Callback function should be optional."""
        callback = None
        assert callback is None

    def test_callback_receives_progress(self):
        """Callback should receive progress updates."""
        received_updates = []

        def callback(message, progress, data):
            received_updates.append((message, progress, data))

        # Simulate progress update
        callback("Processing", 50, {"phase": "analysis"})

        assert len(received_updates) == 1
        assert received_updates[0][1] == 50
