"""
Comprehensive tests for ResearchSourcesService.
Tests saving, retrieving, and updating research sources.
"""

from unittest.mock import Mock, patch


class TestSaveResearchSources:
    """Tests for save_research_sources method."""

    def test_returns_zero_for_empty_sources(self, sample_research_id):
        """Test that zero is returned for empty sources list."""
        from local_deep_research.web.services.research_sources_service import (
            ResearchSourcesService,
        )

        result = ResearchSourcesService.save_research_sources(
            sample_research_id, []
        )

        assert result == 0

    def test_returns_zero_for_none_sources(self, sample_research_id):
        """Test that zero is returned for None sources."""
        from local_deep_research.web.services.research_sources_service import (
            ResearchSourcesService,
        )

        result = ResearchSourcesService.save_research_sources(
            sample_research_id, None
        )

        assert result == 0

    def test_skips_if_resources_already_exist(
        self, mock_db_session, sample_research_id, sample_sources
    ):
        """Test that saving is skipped if resources already exist."""
        from local_deep_research.web.services.research_sources_service import (
            ResearchSourcesService,
        )

        mock_db_session.query.return_value.filter_by.return_value.count.return_value = 5

        with patch(
            "local_deep_research.web.services.research_sources_service.get_user_db_session"
        ) as mock_get_session:
            mock_get_session.return_value.__enter__.return_value = (
                mock_db_session
            )
            mock_get_session.return_value.__exit__.return_value = False

            result = ResearchSourcesService.save_research_sources(
                sample_research_id, sample_sources
            )

            assert result == 5
            mock_db_session.add.assert_not_called()

    def test_saves_sources_when_none_exist(
        self, mock_db_session, sample_research_id, sample_sources
    ):
        """Test that sources are saved when none exist."""
        from local_deep_research.web.services.research_sources_service import (
            ResearchSourcesService,
        )

        mock_db_session.query.return_value.filter_by.return_value.count.return_value = 0

        with patch(
            "local_deep_research.web.services.research_sources_service.get_user_db_session"
        ) as mock_get_session:
            mock_get_session.return_value.__enter__.return_value = (
                mock_db_session
            )
            mock_get_session.return_value.__exit__.return_value = False

            with patch(
                "local_deep_research.web.services.research_sources_service.ResearchResource"
            ):
                result = ResearchSourcesService.save_research_sources(
                    sample_research_id, sample_sources
                )

                assert result > 0
                mock_db_session.commit.assert_called_once()

    def test_extracts_url_from_url_key(
        self, mock_db_session, sample_research_id
    ):
        """Test extraction of URL from 'url' key."""
        from local_deep_research.web.services.research_sources_service import (
            ResearchSourcesService,
        )

        sources = [{"url": "https://example.com", "title": "Test"}]
        mock_db_session.query.return_value.filter_by.return_value.count.return_value = 0

        with patch(
            "local_deep_research.web.services.research_sources_service.get_user_db_session"
        ) as mock_get_session:
            mock_get_session.return_value.__enter__.return_value = (
                mock_db_session
            )
            mock_get_session.return_value.__exit__.return_value = False

            with patch(
                "local_deep_research.web.services.research_sources_service.ResearchResource"
            ) as mock_resource:
                ResearchSourcesService.save_research_sources(
                    sample_research_id, sources
                )

                call_kwargs = mock_resource.call_args[1]
                assert call_kwargs["url"] == "https://example.com"

    def test_extracts_url_from_link_key(
        self, mock_db_session, sample_research_id
    ):
        """Test extraction of URL from 'link' key."""
        from local_deep_research.web.services.research_sources_service import (
            ResearchSourcesService,
        )

        sources = [{"link": "https://example.com", "title": "Test"}]
        mock_db_session.query.return_value.filter_by.return_value.count.return_value = 0

        with patch(
            "local_deep_research.web.services.research_sources_service.get_user_db_session"
        ) as mock_get_session:
            mock_get_session.return_value.__enter__.return_value = (
                mock_db_session
            )
            mock_get_session.return_value.__exit__.return_value = False

            with patch(
                "local_deep_research.web.services.research_sources_service.ResearchResource"
            ) as mock_resource:
                ResearchSourcesService.save_research_sources(
                    sample_research_id, sources
                )

                call_kwargs = mock_resource.call_args[1]
                assert call_kwargs["url"] == "https://example.com"

    def test_extracts_title_from_title_key(
        self, mock_db_session, sample_research_id
    ):
        """Test extraction of title from 'title' key."""
        from local_deep_research.web.services.research_sources_service import (
            ResearchSourcesService,
        )

        sources = [{"url": "https://example.com", "title": "My Title"}]
        mock_db_session.query.return_value.filter_by.return_value.count.return_value = 0

        with patch(
            "local_deep_research.web.services.research_sources_service.get_user_db_session"
        ) as mock_get_session:
            mock_get_session.return_value.__enter__.return_value = (
                mock_db_session
            )
            mock_get_session.return_value.__exit__.return_value = False

            with patch(
                "local_deep_research.web.services.research_sources_service.ResearchResource"
            ) as mock_resource:
                ResearchSourcesService.save_research_sources(
                    sample_research_id, sources
                )

                call_kwargs = mock_resource.call_args[1]
                assert call_kwargs["title"] == "My Title"

    def test_extracts_title_from_name_key(
        self, mock_db_session, sample_research_id
    ):
        """Test extraction of title from 'name' key."""
        from local_deep_research.web.services.research_sources_service import (
            ResearchSourcesService,
        )

        sources = [{"url": "https://example.com", "name": "My Name"}]
        mock_db_session.query.return_value.filter_by.return_value.count.return_value = 0

        with patch(
            "local_deep_research.web.services.research_sources_service.get_user_db_session"
        ) as mock_get_session:
            mock_get_session.return_value.__enter__.return_value = (
                mock_db_session
            )
            mock_get_session.return_value.__exit__.return_value = False

            with patch(
                "local_deep_research.web.services.research_sources_service.ResearchResource"
            ) as mock_resource:
                ResearchSourcesService.save_research_sources(
                    sample_research_id, sources
                )

                call_kwargs = mock_resource.call_args[1]
                assert call_kwargs["title"] == "My Name"

    def test_uses_untitled_for_missing_title(
        self, mock_db_session, sample_research_id
    ):
        """Test that 'Untitled' is used when title is missing."""
        from local_deep_research.web.services.research_sources_service import (
            ResearchSourcesService,
        )

        sources = [{"url": "https://example.com"}]
        mock_db_session.query.return_value.filter_by.return_value.count.return_value = 0

        with patch(
            "local_deep_research.web.services.research_sources_service.get_user_db_session"
        ) as mock_get_session:
            mock_get_session.return_value.__enter__.return_value = (
                mock_db_session
            )
            mock_get_session.return_value.__exit__.return_value = False

            with patch(
                "local_deep_research.web.services.research_sources_service.ResearchResource"
            ) as mock_resource:
                ResearchSourcesService.save_research_sources(
                    sample_research_id, sources
                )

                call_kwargs = mock_resource.call_args[1]
                assert call_kwargs["title"] == "Untitled"

    def test_skips_sources_without_url(
        self, mock_db_session, sample_research_id
    ):
        """Test that sources without URL are skipped."""
        from local_deep_research.web.services.research_sources_service import (
            ResearchSourcesService,
        )

        sources = [
            {"title": "No URL"},
            {"url": "https://example.com", "title": "Has URL"},
        ]
        mock_db_session.query.return_value.filter_by.return_value.count.return_value = 0

        with patch(
            "local_deep_research.web.services.research_sources_service.get_user_db_session"
        ) as mock_get_session:
            mock_get_session.return_value.__enter__.return_value = (
                mock_db_session
            )
            mock_get_session.return_value.__exit__.return_value = False

            with patch(
                "local_deep_research.web.services.research_sources_service.ResearchResource"
            ):
                result = ResearchSourcesService.save_research_sources(
                    sample_research_id, sources
                )

                # Only one source should be saved
                assert result == 1

    def test_truncates_long_snippets(self, mock_db_session, sample_research_id):
        """Test that snippets are truncated to 1000 characters."""
        from local_deep_research.web.services.research_sources_service import (
            ResearchSourcesService,
        )

        long_snippet = "x" * 2000
        sources = [
            {
                "url": "https://example.com",
                "title": "Test",
                "snippet": long_snippet,
            }
        ]
        mock_db_session.query.return_value.filter_by.return_value.count.return_value = 0

        with patch(
            "local_deep_research.web.services.research_sources_service.get_user_db_session"
        ) as mock_get_session:
            mock_get_session.return_value.__enter__.return_value = (
                mock_db_session
            )
            mock_get_session.return_value.__exit__.return_value = False

            with patch(
                "local_deep_research.web.services.research_sources_service.ResearchResource"
            ) as mock_resource:
                ResearchSourcesService.save_research_sources(
                    sample_research_id, sources
                )

                call_kwargs = mock_resource.call_args[1]
                assert len(call_kwargs["content_preview"]) == 1000

    def test_extracts_snippet_from_multiple_keys(
        self, mock_db_session, sample_research_id
    ):
        """Test extraction of snippet from various keys."""
        from local_deep_research.web.services.research_sources_service import (
            ResearchSourcesService,
        )

        # Test content_preview key
        sources = [
            {"url": "https://example.com", "content_preview": "Preview text"}
        ]
        mock_db_session.query.return_value.filter_by.return_value.count.return_value = 0

        with patch(
            "local_deep_research.web.services.research_sources_service.get_user_db_session"
        ) as mock_get_session:
            mock_get_session.return_value.__enter__.return_value = (
                mock_db_session
            )
            mock_get_session.return_value.__exit__.return_value = False

            with patch(
                "local_deep_research.web.services.research_sources_service.ResearchResource"
            ) as mock_resource:
                ResearchSourcesService.save_research_sources(
                    sample_research_id, sources
                )

                call_kwargs = mock_resource.call_args[1]
                assert call_kwargs["content_preview"] == "Preview text"


class TestGetResearchSources:
    """Tests for get_research_sources method."""

    def test_returns_list_of_sources(self, mock_db_session, sample_research_id):
        """Test that list of sources is returned."""
        from local_deep_research.web.services.research_sources_service import (
            ResearchSourcesService,
        )

        mock_resource = Mock()
        mock_resource.id = 1
        mock_resource.url = "https://example.com"
        mock_resource.title = "Test"
        mock_resource.content_preview = "Preview"
        mock_resource.source_type = "web"
        mock_resource.resource_metadata = {}
        mock_resource.created_at = "2024-01-01"

        mock_db_session.query.return_value.filter_by.return_value.order_by.return_value.all.return_value = [
            mock_resource
        ]

        with patch(
            "local_deep_research.web.services.research_sources_service.get_user_db_session"
        ) as mock_get_session:
            mock_get_session.return_value.__enter__.return_value = (
                mock_db_session
            )
            mock_get_session.return_value.__exit__.return_value = False

            result = ResearchSourcesService.get_research_sources(
                sample_research_id
            )

            assert isinstance(result, list)
            assert len(result) == 1

    def test_returns_correct_fields(self, mock_db_session, sample_research_id):
        """Test that sources have correct fields."""
        from local_deep_research.web.services.research_sources_service import (
            ResearchSourcesService,
        )

        mock_resource = Mock()
        mock_resource.id = 1
        mock_resource.url = "https://example.com"
        mock_resource.title = "Test Title"
        mock_resource.content_preview = "Preview text"
        mock_resource.source_type = "web"
        mock_resource.resource_metadata = {"key": "value"}
        mock_resource.created_at = "2024-01-01"

        mock_db_session.query.return_value.filter_by.return_value.order_by.return_value.all.return_value = [
            mock_resource
        ]

        with patch(
            "local_deep_research.web.services.research_sources_service.get_user_db_session"
        ) as mock_get_session:
            mock_get_session.return_value.__enter__.return_value = (
                mock_db_session
            )
            mock_get_session.return_value.__exit__.return_value = False

            result = ResearchSourcesService.get_research_sources(
                sample_research_id
            )

            source = result[0]
            assert source["id"] == 1
            assert source["url"] == "https://example.com"
            assert source["title"] == "Test Title"
            assert source["snippet"] == "Preview text"
            assert source["content_preview"] == "Preview text"
            assert source["source_type"] == "web"
            assert source["metadata"] == {"key": "value"}

    def test_returns_empty_list_when_no_sources(
        self, mock_db_session, sample_research_id
    ):
        """Test that empty list is returned when no sources exist."""
        from local_deep_research.web.services.research_sources_service import (
            ResearchSourcesService,
        )

        mock_db_session.query.return_value.filter_by.return_value.order_by.return_value.all.return_value = []

        with patch(
            "local_deep_research.web.services.research_sources_service.get_user_db_session"
        ) as mock_get_session:
            mock_get_session.return_value.__enter__.return_value = (
                mock_db_session
            )
            mock_get_session.return_value.__exit__.return_value = False

            result = ResearchSourcesService.get_research_sources(
                sample_research_id
            )

            assert result == []

    def test_handles_null_metadata(self, mock_db_session, sample_research_id):
        """Test handling of null resource_metadata."""
        from local_deep_research.web.services.research_sources_service import (
            ResearchSourcesService,
        )

        mock_resource = Mock()
        mock_resource.id = 1
        mock_resource.url = "https://example.com"
        mock_resource.title = "Test"
        mock_resource.content_preview = "Preview"
        mock_resource.source_type = "web"
        mock_resource.resource_metadata = None
        mock_resource.created_at = "2024-01-01"

        mock_db_session.query.return_value.filter_by.return_value.order_by.return_value.all.return_value = [
            mock_resource
        ]

        with patch(
            "local_deep_research.web.services.research_sources_service.get_user_db_session"
        ) as mock_get_session:
            mock_get_session.return_value.__enter__.return_value = (
                mock_db_session
            )
            mock_get_session.return_value.__exit__.return_value = False

            result = ResearchSourcesService.get_research_sources(
                sample_research_id
            )

            assert result[0]["metadata"] == {}

    def test_passes_username_to_session(
        self, mock_db_session, sample_research_id
    ):
        """Test that username is passed to get_user_db_session."""
        from local_deep_research.web.services.research_sources_service import (
            ResearchSourcesService,
        )

        mock_db_session.query.return_value.filter_by.return_value.order_by.return_value.all.return_value = []

        with patch(
            "local_deep_research.web.services.research_sources_service.get_user_db_session"
        ) as mock_get_session:
            mock_get_session.return_value.__enter__.return_value = (
                mock_db_session
            )
            mock_get_session.return_value.__exit__.return_value = False

            ResearchSourcesService.get_research_sources(
                sample_research_id, username="testuser"
            )

            mock_get_session.assert_called_once_with("testuser")


class TestUpdateResearchWithSources:
    """Tests for update_research_with_sources method."""

    def test_calls_save_research_sources(
        self, mock_db_session, sample_research_id, sample_sources
    ):
        """Test that save_research_sources is called."""
        from local_deep_research.web.services.research_sources_service import (
            ResearchSourcesService,
        )

        mock_research = Mock()
        mock_research.research_meta = None

        mock_db_session.query.return_value.filter_by.return_value.first.return_value = mock_research

        with patch(
            "local_deep_research.web.services.research_sources_service.get_user_db_session"
        ) as mock_get_session:
            mock_get_session.return_value.__enter__.return_value = (
                mock_db_session
            )
            mock_get_session.return_value.__exit__.return_value = False

            with patch.object(
                ResearchSourcesService,
                "save_research_sources",
                return_value=3,
            ) as mock_save:
                ResearchSourcesService.update_research_with_sources(
                    sample_research_id, sample_sources
                )

                mock_save.assert_called_once()

    def test_updates_research_metadata(
        self, mock_db_session, sample_research_id, sample_sources
    ):
        """Test that research metadata is updated with source count."""
        from local_deep_research.web.services.research_sources_service import (
            ResearchSourcesService,
        )

        mock_research = Mock()
        mock_research.research_meta = {}

        mock_db_session.query.return_value.filter_by.return_value.first.return_value = mock_research

        with patch(
            "local_deep_research.web.services.research_sources_service.get_user_db_session"
        ) as mock_get_session:
            mock_get_session.return_value.__enter__.return_value = (
                mock_db_session
            )
            mock_get_session.return_value.__exit__.return_value = False

            with patch.object(
                ResearchSourcesService,
                "save_research_sources",
                return_value=3,
            ):
                ResearchSourcesService.update_research_with_sources(
                    sample_research_id, sample_sources
                )

                assert mock_research.research_meta["sources_count"] == 3
                assert mock_research.research_meta["has_sources"] is True

    def test_returns_true_on_success(
        self, mock_db_session, sample_research_id, sample_sources
    ):
        """Test that True is returned on success."""
        from local_deep_research.web.services.research_sources_service import (
            ResearchSourcesService,
        )

        mock_research = Mock()
        mock_research.research_meta = {}

        mock_db_session.query.return_value.filter_by.return_value.first.return_value = mock_research

        with patch(
            "local_deep_research.web.services.research_sources_service.get_user_db_session"
        ) as mock_get_session:
            mock_get_session.return_value.__enter__.return_value = (
                mock_db_session
            )
            mock_get_session.return_value.__exit__.return_value = False

            with patch.object(
                ResearchSourcesService,
                "save_research_sources",
                return_value=3,
            ):
                result = ResearchSourcesService.update_research_with_sources(
                    sample_research_id, sample_sources
                )

                assert result is True

    def test_returns_false_when_research_not_found(
        self, mock_db_session, sample_research_id, sample_sources
    ):
        """Test that False is returned when research not found."""
        from local_deep_research.web.services.research_sources_service import (
            ResearchSourcesService,
        )

        mock_db_session.query.return_value.filter_by.return_value.first.return_value = None

        with patch(
            "local_deep_research.web.services.research_sources_service.get_user_db_session"
        ) as mock_get_session:
            mock_get_session.return_value.__enter__.return_value = (
                mock_db_session
            )
            mock_get_session.return_value.__exit__.return_value = False

            with patch.object(
                ResearchSourcesService,
                "save_research_sources",
                return_value=3,
            ):
                result = ResearchSourcesService.update_research_with_sources(
                    sample_research_id, sample_sources
                )

                assert result is False

    def test_returns_false_on_exception(
        self, mock_db_session, sample_research_id, sample_sources
    ):
        """Test that False is returned on exception."""
        from local_deep_research.web.services.research_sources_service import (
            ResearchSourcesService,
        )

        with patch(
            "local_deep_research.web.services.research_sources_service.get_user_db_session"
        ) as mock_get_session:
            mock_get_session.return_value.__enter__.return_value = (
                mock_db_session
            )
            mock_get_session.return_value.__exit__.return_value = False

            with patch.object(
                ResearchSourcesService,
                "save_research_sources",
                side_effect=Exception("DB error"),
            ):
                result = ResearchSourcesService.update_research_with_sources(
                    sample_research_id, sample_sources
                )

                assert result is False

    def test_initializes_null_metadata(
        self, mock_db_session, sample_research_id, sample_sources
    ):
        """Test that null research_meta is initialized."""
        from local_deep_research.web.services.research_sources_service import (
            ResearchSourcesService,
        )

        mock_research = Mock()
        mock_research.research_meta = None

        mock_db_session.query.return_value.filter_by.return_value.first.return_value = mock_research

        with patch(
            "local_deep_research.web.services.research_sources_service.get_user_db_session"
        ) as mock_get_session:
            mock_get_session.return_value.__enter__.return_value = (
                mock_db_session
            )
            mock_get_session.return_value.__exit__.return_value = False

            with patch.object(
                ResearchSourcesService,
                "save_research_sources",
                return_value=2,
            ):
                ResearchSourcesService.update_research_with_sources(
                    sample_research_id, sample_sources
                )

                assert mock_research.research_meta is not None
                assert isinstance(mock_research.research_meta, dict)
