"""
Tests for TopicOrganizationStrategy.

Tests cover:
- Initialization and configuration
- Topic extraction from sources
- Topic relationship finding
- Relevance filtering
- Refinement questions
- Text generation
- Error handling
"""

from unittest.mock import Mock, patch


class TestTopicOrganizationStrategyInit:
    """Tests for TopicOrganizationStrategy initialization."""

    def test_init_with_required_params(self):
        """Initialize with required parameters."""
        from local_deep_research.advanced_search_system.strategies.topic_organization_strategy import (
            TopicOrganizationStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = TopicOrganizationStrategy(
            search=mock_search,
            model=mock_model,
        )

        assert strategy.model is mock_model
        assert strategy.search is mock_search
        assert strategy.min_sources_per_topic == 1
        assert strategy.max_topics == 5

    def test_init_with_custom_params(self):
        """Initialize with custom parameters."""
        from local_deep_research.advanced_search_system.strategies.topic_organization_strategy import (
            TopicOrganizationStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = TopicOrganizationStrategy(
            search=mock_search,
            model=mock_model,
            min_sources_per_topic=3,
            max_topics=10,
            similarity_threshold=0.8,
            enable_refinement=True,
            max_refinement_iterations=5,
        )

        assert strategy.min_sources_per_topic == 3
        assert strategy.max_topics == 10
        assert strategy.similarity_threshold == 0.8
        assert strategy.enable_refinement is True
        assert strategy.max_refinement_iterations == 5

    def test_init_creates_source_strategy(self):
        """Initialize creates source gathering strategy."""
        from local_deep_research.advanced_search_system.strategies.topic_organization_strategy import (
            TopicOrganizationStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = TopicOrganizationStrategy(
            search=mock_search,
            model=mock_model,
        )

        assert strategy.source_strategy is not None

    def test_init_with_focused_iteration(self):
        """Initialize with focused iteration strategy."""
        from local_deep_research.advanced_search_system.strategies.topic_organization_strategy import (
            TopicOrganizationStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = TopicOrganizationStrategy(
            search=mock_search,
            model=mock_model,
            use_focused_iteration=True,
        )

        assert strategy.use_focused_iteration is True

    def test_init_creates_topic_graph(self):
        """Initialize creates topic graph."""
        from local_deep_research.advanced_search_system.strategies.topic_organization_strategy import (
            TopicOrganizationStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = TopicOrganizationStrategy(
            search=mock_search,
            model=mock_model,
        )

        assert strategy.topic_graph is not None

    def test_init_with_citation_handler(self):
        """Initialize with custom citation handler."""
        from local_deep_research.advanced_search_system.strategies.topic_organization_strategy import (
            TopicOrganizationStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()
        mock_citation_handler = Mock()

        strategy = TopicOrganizationStrategy(
            search=mock_search,
            model=mock_model,
            citation_handler=mock_citation_handler,
        )

        assert strategy.citation_handler is mock_citation_handler


class TestTopicExtraction:
    """Tests for topic extraction methods."""

    def test_extract_topics_from_sources_empty(self):
        """Extract topics returns empty list for empty sources."""
        from local_deep_research.advanced_search_system.strategies.topic_organization_strategy import (
            TopicOrganizationStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = TopicOrganizationStrategy(
            search=mock_search,
            model=mock_model,
        )

        topics = strategy._extract_topics_from_sources([], "test query")

        assert topics == []

    def test_extract_topics_from_sources_creates_topics(self):
        """Extract topics creates topic objects."""
        from local_deep_research.advanced_search_system.strategies.topic_organization_strategy import (
            TopicOrganizationStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()
        # Return "-" to create new topics
        mock_model.invoke.return_value = Mock(content="-")

        strategy = TopicOrganizationStrategy(
            search=mock_search,
            model=mock_model,
        )

        # Initialize progress_callback
        strategy.progress_callback = None

        sources = [
            {
                "title": "Source 1",
                "snippet": "Content 1",
                "link": "http://test1.com",
            },
            {
                "title": "Source 2",
                "snippet": "Content 2",
                "link": "http://test2.com",
            },
        ]

        topics = strategy._extract_topics_from_sources(sources, "test query")

        assert isinstance(topics, list)

    def test_extract_topics_adds_to_existing(self):
        """Extract topics can add to existing topics."""
        from local_deep_research.advanced_search_system.strategies.topic_organization_strategy import (
            TopicOrganizationStrategy,
        )
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
        )

        mock_search = Mock()
        mock_model = Mock()
        # Return "0" to add to first topic
        mock_model.invoke.return_value = Mock(content="0")

        strategy = TopicOrganizationStrategy(
            search=mock_search,
            model=mock_model,
        )

        # Initialize progress_callback
        strategy.progress_callback = None

        existing_topic = Topic(
            id="existing1",
            title="Existing Topic",
            lead_source={
                "title": "Lead",
                "snippet": "Lead content",
                "link": "http://lead.com",
            },
        )

        sources = [
            {
                "title": "New Source",
                "snippet": "New content",
                "link": "http://new.com",
            },
        ]

        topics = strategy._extract_topics_from_sources(
            sources, "test query", existing_topics=[existing_topic]
        )

        # The new source should be added to existing topic
        assert isinstance(topics, list)

    def test_extract_topics_deletes_irrelevant(self):
        """Extract topics handles delete response."""
        from local_deep_research.advanced_search_system.strategies.topic_organization_strategy import (
            TopicOrganizationStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()
        # Return "d" to delete
        mock_model.invoke.return_value = Mock(content="d")

        strategy = TopicOrganizationStrategy(
            search=mock_search,
            model=mock_model,
        )

        # Initialize progress_callback
        strategy.progress_callback = None

        sources = [
            {
                "title": "Irrelevant",
                "snippet": "Content",
                "link": "http://test.com",
            },
        ]

        topics = strategy._extract_topics_from_sources(sources, "test query")

        # No topics should be created
        assert topics == []


class TestLeadSourceReselection:
    """Tests for lead source reselection methods."""

    def test_reselect_lead_for_single_topic_few_sources(self):
        """Reselect lead returns False for topics with few sources."""
        from local_deep_research.advanced_search_system.strategies.topic_organization_strategy import (
            TopicOrganizationStrategy,
        )
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = TopicOrganizationStrategy(
            search=mock_search,
            model=mock_model,
        )

        topic = Topic(
            id="t1",
            title="Topic",
            lead_source={
                "title": "Lead",
                "snippet": "Content",
                "link": "http://test.com",
            },
        )

        result = strategy._reselect_lead_for_single_topic(topic, [topic])

        assert result is False

    def test_reselect_lead_sources(self):
        """Reselect lead sources updates topics."""
        from local_deep_research.advanced_search_system.strategies.topic_organization_strategy import (
            TopicOrganizationStrategy,
        )
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
        )

        mock_search = Mock()
        mock_model = Mock()
        mock_model.invoke.return_value = Mock(content="0")

        strategy = TopicOrganizationStrategy(
            search=mock_search,
            model=mock_model,
        )

        topic = Topic(
            id="t1",
            title="Topic",
            lead_source={
                "title": "Lead",
                "snippet": "Content",
                "link": "http://lead.com",
            },
        )
        topic.add_supporting_source(
            {
                "title": "Support",
                "snippet": "Support content",
                "link": "http://support.com",
            }
        )

        strategy._reselect_lead_sources([topic])

        # Method should complete without error
        assert True


class TestTopicRelationships:
    """Tests for topic relationship methods."""

    def test_find_topic_relationships_single_topic(self):
        """Find relationships handles single topic."""
        from local_deep_research.advanced_search_system.strategies.topic_organization_strategy import (
            TopicOrganizationStrategy,
        )
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = TopicOrganizationStrategy(
            search=mock_search,
            model=mock_model,
        )

        topic = Topic(
            id="t1",
            title="Topic",
            lead_source={
                "title": "Lead",
                "snippet": "Content",
                "link": "http://test.com",
            },
        )

        # Should not raise error
        strategy._find_topic_relationships([topic])


class TestRelevanceFiltering:
    """Tests for relevance filtering methods."""

    def test_filter_topics_by_relevance_empty(self):
        """Filter topics returns empty for empty input."""
        from local_deep_research.advanced_search_system.strategies.topic_organization_strategy import (
            TopicOrganizationStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = TopicOrganizationStrategy(
            search=mock_search,
            model=mock_model,
        )

        result = strategy._filter_topics_by_relevance([], "test query")

        assert result == []

    def test_filter_topics_by_relevance_keeps_relevant(self):
        """Filter topics keeps relevant topics."""
        from local_deep_research.advanced_search_system.strategies.topic_organization_strategy import (
            TopicOrganizationStrategy,
        )
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
        )

        mock_search = Mock()
        mock_model = Mock()
        mock_model.invoke.return_value = Mock(content="yes")

        strategy = TopicOrganizationStrategy(
            search=mock_search,
            model=mock_model,
        )

        topic = Topic(
            id="t1",
            title="Relevant Topic",
            lead_source={
                "title": "Lead",
                "snippet": "Content",
                "link": "http://test.com",
            },
        )

        result = strategy._filter_topics_by_relevance([topic], "test query")

        assert len(result) == 1

    def test_filter_topics_by_relevance_removes_irrelevant(self):
        """Filter topics removes irrelevant topics."""
        from local_deep_research.advanced_search_system.strategies.topic_organization_strategy import (
            TopicOrganizationStrategy,
        )
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
        )

        mock_search = Mock()
        mock_model = Mock()
        mock_model.invoke.return_value = Mock(content="no")

        strategy = TopicOrganizationStrategy(
            search=mock_search,
            model=mock_model,
        )

        topic = Topic(
            id="t1",
            title="Irrelevant Topic",
            lead_source={
                "title": "Lead",
                "snippet": "Content",
                "link": "http://test.com",
            },
        )

        result = strategy._filter_topics_by_relevance([topic], "test query")

        assert len(result) == 0


class TestRefinementQuestions:
    """Tests for refinement question generation."""

    def test_generate_refinement_question_disabled(self):
        """Generate refinement question returns None when disabled."""
        from local_deep_research.advanced_search_system.strategies.topic_organization_strategy import (
            TopicOrganizationStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = TopicOrganizationStrategy(
            search=mock_search,
            model=mock_model,
            enable_refinement=False,
        )

        result = strategy._generate_refinement_question([], "test query")

        assert result is None

    def test_generate_refinement_question_no_topics(self):
        """Generate refinement question returns None for no topics."""
        from local_deep_research.advanced_search_system.strategies.topic_organization_strategy import (
            TopicOrganizationStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = TopicOrganizationStrategy(
            search=mock_search,
            model=mock_model,
            enable_refinement=True,
        )

        result = strategy._generate_refinement_question([], "test query")

        assert result is None

    def test_generate_refinement_question_returns_question(self):
        """Generate refinement question returns question string."""
        from local_deep_research.advanced_search_system.strategies.topic_organization_strategy import (
            TopicOrganizationStrategy,
        )
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
        )

        mock_search = Mock()
        mock_model = Mock()
        mock_model.invoke.return_value = Mock(
            content="What are the key factors?"
        )

        strategy = TopicOrganizationStrategy(
            search=mock_search,
            model=mock_model,
            enable_refinement=True,
        )

        topic = Topic(
            id="t1",
            title="Topic",
            lead_source={
                "title": "Lead",
                "snippet": "Content",
                "link": "http://test.com",
            },
        )

        result = strategy._generate_refinement_question([topic], "test query")

        assert result is not None
        assert "?" in result or len(result) > 0

    def test_generate_refinement_question_returns_none_for_complete(self):
        """Generate refinement question returns None when model says NONE."""
        from local_deep_research.advanced_search_system.strategies.topic_organization_strategy import (
            TopicOrganizationStrategy,
        )
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
        )

        mock_search = Mock()
        mock_model = Mock()
        mock_model.invoke.return_value = Mock(content="NONE")

        strategy = TopicOrganizationStrategy(
            search=mock_search,
            model=mock_model,
            enable_refinement=True,
        )

        topic = Topic(
            id="t1",
            title="Topic",
            lead_source={
                "title": "Lead",
                "snippet": "Content",
                "link": "http://test.com",
            },
        )

        result = strategy._generate_refinement_question([topic], "test query")

        assert result is None


class TestTopicReorganization:
    """Tests for topic reorganization methods."""

    def test_reorganize_topics_single_topic(self):
        """Reorganize topics handles single topic."""
        from local_deep_research.advanced_search_system.strategies.topic_organization_strategy import (
            TopicOrganizationStrategy,
        )
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = TopicOrganizationStrategy(
            search=mock_search,
            model=mock_model,
        )

        topic = Topic(
            id="t1",
            title="Topic",
            lead_source={
                "title": "Lead",
                "snippet": "Content",
                "link": "http://test.com",
            },
        )

        result = strategy._reorganize_topics([topic])

        assert result == [topic]


class TestAnalyzeTopic:
    """Tests for main analyze_topic method."""

    def test_analyze_topic_returns_expected_structure(self):
        """Analyze topic returns expected result structure."""
        from local_deep_research.advanced_search_system.strategies.topic_organization_strategy import (
            TopicOrganizationStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()
        mock_model.invoke.return_value = Mock(content="-")

        strategy = TopicOrganizationStrategy(
            search=mock_search,
            model=mock_model,
            generate_text=False,
        )

        # Initialize progress_callback
        strategy.progress_callback = None

        # Mock the source strategy
        with patch.object(
            strategy.source_strategy,
            "analyze_topic",
            return_value={
                "all_links_of_system": [
                    {
                        "title": "Source 1",
                        "snippet": "Content",
                        "link": "http://test.com",
                    }
                ],
                "iterations": 1,
                "questions_by_iteration": {},
            },
        ):
            result = strategy.analyze_topic("test query")

        assert isinstance(result, dict)
        assert "findings" in result
        assert "iterations" in result
        assert "topics" in result
        assert "topic_graph" in result

    def test_analyze_topic_no_sources(self):
        """Analyze topic handles no sources gracefully."""
        from local_deep_research.advanced_search_system.strategies.topic_organization_strategy import (
            TopicOrganizationStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = TopicOrganizationStrategy(
            search=mock_search,
            model=mock_model,
        )

        # Mock the source strategy to return no sources
        with patch.object(
            strategy.source_strategy,
            "analyze_topic",
            return_value={
                "all_links_of_system": [],
                "iterations": 0,
                "questions_by_iteration": {},
            },
        ):
            result = strategy.analyze_topic("test query")

        assert result["topics"] == []
        assert result["source_count"] == 0

    def test_analyze_topic_calls_progress_callback(self):
        """Analyze topic calls progress callback."""
        from local_deep_research.advanced_search_system.strategies.topic_organization_strategy import (
            TopicOrganizationStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()
        mock_model.invoke.return_value = Mock(content="-")

        strategy = TopicOrganizationStrategy(
            search=mock_search,
            model=mock_model,
            generate_text=False,
        )

        callback = Mock()
        strategy.set_progress_callback(callback)

        # Make sure progress_callback is set
        assert strategy.progress_callback is callback

        with patch.object(
            strategy.source_strategy,
            "analyze_topic",
            return_value={
                "all_links_of_system": [
                    {
                        "title": "Source",
                        "snippet": "Content",
                        "link": "http://test.com",
                    }
                ],
                "iterations": 1,
                "questions_by_iteration": {},
            },
        ):
            strategy.analyze_topic("test query")

        # Callback should be called at least once through _update_progress
        assert callback.call_count >= 1


class TestFormattingMethods:
    """Tests for formatting helper methods."""

    def test_format_single_topic_with_sources(self):
        """Format single topic includes source information."""
        from local_deep_research.advanced_search_system.strategies.topic_organization_strategy import (
            TopicOrganizationStrategy,
        )
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = TopicOrganizationStrategy(
            search=mock_search,
            model=mock_model,
        )

        topic = Topic(
            id="t1",
            title="Test Topic",
            lead_source={
                "title": "Lead Source",
                "snippet": "Lead content",
                "link": "http://lead.com",
            },
        )
        topic.add_supporting_source(
            {
                "title": "Support Source",
                "snippet": "Support content",
                "link": "http://support.com",
            }
        )

        result = strategy._format_single_topic_with_sources(topic)

        assert "Lead Source" in result
        assert "Support Source" in result

    def test_format_topic_graph_as_knowledge(self):
        """Format topic graph creates readable knowledge output."""
        from local_deep_research.advanced_search_system.strategies.topic_organization_strategy import (
            TopicOrganizationStrategy,
        )
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = TopicOrganizationStrategy(
            search=mock_search,
            model=mock_model,
        )

        topic = Topic(
            id="t1",
            title="Test Topic",
            lead_source={
                "title": "Lead",
                "snippet": "Content",
                "link": "http://test.com",
            },
        )

        result = strategy._format_topic_graph_as_knowledge(
            [topic], "test query"
        )

        assert "Topic Graph" in result
        assert "test query" in result

    def test_format_topic_graph_empty(self):
        """Format topic graph handles empty topics."""
        from local_deep_research.advanced_search_system.strategies.topic_organization_strategy import (
            TopicOrganizationStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = TopicOrganizationStrategy(
            search=mock_search,
            model=mock_model,
        )

        result = strategy._format_topic_graph_as_knowledge([], "test query")

        assert "No topics" in result

    def test_format_topic_findings(self):
        """Format topic findings creates comprehensive output."""
        from local_deep_research.advanced_search_system.strategies.topic_organization_strategy import (
            TopicOrganizationStrategy,
        )
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = TopicOrganizationStrategy(
            search=mock_search,
            model=mock_model,
        )

        topic = Topic(
            id="t1",
            title="Test Topic",
            lead_source={
                "title": "Lead",
                "snippet": "Content",
                "link": "http://test.com",
            },
        )

        result = strategy._format_topic_findings([topic], "test query")

        assert "Topic Organization" in result


class TestTextGeneration:
    """Tests for text generation methods."""

    def test_generate_topic_based_text_no_topics(self):
        """Generate topic based text returns empty for no topics."""
        from local_deep_research.advanced_search_system.strategies.topic_organization_strategy import (
            TopicOrganizationStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = TopicOrganizationStrategy(
            search=mock_search,
            model=mock_model,
        )

        result = strategy._generate_topic_based_text([], "test query")

        assert result == ""

    def test_generate_topic_based_text_with_topics(self):
        """Generate topic based text creates text from topics."""
        from local_deep_research.advanced_search_system.strategies.topic_organization_strategy import (
            TopicOrganizationStrategy,
        )
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
        )

        mock_search = Mock()
        mock_model = Mock()
        mock_model.invoke.return_value = Mock(
            content="Generated text about topic."
        )

        # Create citation handler mock
        mock_citation = Mock()
        mock_citation._create_documents.return_value = []
        mock_citation._format_sources.return_value = ""

        strategy = TopicOrganizationStrategy(
            search=mock_search,
            model=mock_model,
            citation_handler=mock_citation,
        )

        topic = Topic(
            id="t1",
            title="Test Topic",
            lead_source={
                "title": "Lead",
                "snippet": "Content",
                "link": "http://test.com",
            },
        )

        result = strategy._generate_topic_based_text([topic], "test query")

        assert len(result) > 0


class TestReselectLeadUrlparsing:
    """Tests for URL domain extraction in _reselect_lead_for_single_topic (PR #2020).

    PR #2020 changed bare `except:` to `except Exception:` when using
    urlparse to extract domain from source URLs.
    """

    def test_valid_url_domain_extraction(self):
        """Should extract domain from valid URL."""
        from urllib.parse import urlparse

        url = "https://www.example.com/path/to/resource"
        try:
            domain = urlparse(url).netloc if url else ""
        except Exception:
            domain = ""

        assert domain == "www.example.com"

    def test_empty_url_returns_empty_domain(self):
        """Should return empty string for empty URL."""
        from urllib.parse import urlparse

        url = ""
        try:
            domain = urlparse(url).netloc if url else ""
        except Exception:
            domain = ""

        assert domain == ""

    def test_none_url_returns_empty_domain(self):
        """Should return empty string for None URL."""
        url = None
        try:
            from urllib.parse import urlparse

            domain = urlparse(url).netloc if url else ""
        except Exception:
            domain = ""

        assert domain == ""

    def test_malformed_url_handled_gracefully(self):
        """Should handle malformed URLs gracefully."""
        from urllib.parse import urlparse

        url = "not-a-valid-url"
        try:
            domain = urlparse(url).netloc if url else ""
        except Exception:
            domain = ""

        # urlparse handles most strings without raising
        assert isinstance(domain, str)
