"""
Comprehensive tests for resource_service.
Tests CRUD operations for ResearchResource.
"""

import pytest
from unittest.mock import Mock, patch


class TestGetResourcesForResearch:
    """Tests for get_resources_for_research function."""

    def test_returns_list_of_resources(
        self, mock_db_session, mock_research_resource, sample_research_id
    ):
        """Test that function returns list of resources."""
        from local_deep_research.web.services.resource_service import (
            get_resources_for_research,
        )

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

        with patch(
            "local_deep_research.web.services.resource_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 = get_resources_for_research(sample_research_id)

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

    def test_returns_correct_fields(
        self, mock_db_session, mock_research_resource, sample_research_id
    ):
        """Test that returned resources have correct fields."""
        from local_deep_research.web.services.resource_service import (
            get_resources_for_research,
        )

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

        with patch(
            "local_deep_research.web.services.resource_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 = get_resources_for_research(sample_research_id)

            resource = result[0]
            assert "id" in resource
            assert "research_id" in resource
            assert "title" in resource
            assert "url" in resource
            assert "content_preview" in resource
            assert "source_type" in resource
            assert "metadata" in resource

    def test_returns_empty_list_when_no_resources(
        self, mock_db_session, sample_research_id
    ):
        """Test that empty list is returned when no resources exist."""
        from local_deep_research.web.services.resource_service import (
            get_resources_for_research,
        )

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

        with patch(
            "local_deep_research.web.services.resource_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 = get_resources_for_research(sample_research_id)

            assert result == []

    def test_filters_by_research_id(self, mock_db_session, sample_research_id):
        """Test that query filters by research_id."""
        from local_deep_research.web.services.resource_service import (
            get_resources_for_research,
        )

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

        with patch(
            "local_deep_research.web.services.resource_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

            get_resources_for_research(sample_research_id)

            mock_db_session.query.return_value.filter_by.assert_called_once_with(
                research_id=sample_research_id
            )

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

        resource = Mock()
        resource.id = 1
        resource.research_id = sample_research_id
        resource.title = "Test"
        resource.url = "https://test.com"
        resource.content_preview = "Preview"
        resource.source_type = "web"
        resource.resource_metadata = None

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

        with patch(
            "local_deep_research.web.services.resource_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 = get_resources_for_research(sample_research_id)

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

    def test_raises_on_database_error(
        self, mock_db_session, sample_research_id
    ):
        """Test that exceptions are raised on database error."""
        from local_deep_research.web.services.resource_service import (
            get_resources_for_research,
        )

        mock_db_session.query.side_effect = Exception("DB error")

        with patch(
            "local_deep_research.web.services.resource_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 pytest.raises(Exception):
                get_resources_for_research(sample_research_id)


class TestAddResource:
    """Tests for add_resource function."""

    def test_creates_resource(self, mock_db_session, sample_research_id):
        """Test that resource is created and added to session."""
        from local_deep_research.web.services.resource_service import (
            add_resource,
        )

        with patch(
            "local_deep_research.web.services.resource_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.resource_service.ResearchResource"
            ):
                add_resource(
                    sample_research_id, "Test Title", "https://example.com"
                )

                mock_db_session.add.assert_called_once()
                mock_db_session.commit.assert_called_once()

    def test_returns_created_resource(
        self, mock_db_session, sample_research_id
    ):
        """Test that created resource is returned."""
        from local_deep_research.web.services.resource_service import (
            add_resource,
        )

        with patch(
            "local_deep_research.web.services.resource_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.resource_service.ResearchResource"
            ) as mock_resource_class:
                mock_resource_class.return_value = Mock()
                result = add_resource(
                    sample_research_id, "Test Title", "https://example.com"
                )

                assert result is not None

    def test_uses_default_source_type(
        self, mock_db_session, sample_research_id
    ):
        """Test that default source_type is 'web'."""
        from local_deep_research.web.services.resource_service import (
            add_resource,
        )

        with patch(
            "local_deep_research.web.services.resource_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.resource_service.ResearchResource"
            ) as mock_resource_class:
                add_resource(
                    sample_research_id, "Test Title", "https://example.com"
                )

                call_kwargs = mock_resource_class.call_args[1]
                assert call_kwargs["source_type"] == "web"

    def test_accepts_custom_source_type(
        self, mock_db_session, sample_research_id
    ):
        """Test that custom source_type is accepted."""
        from local_deep_research.web.services.resource_service import (
            add_resource,
        )

        with patch(
            "local_deep_research.web.services.resource_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.resource_service.ResearchResource"
            ) as mock_resource_class:
                add_resource(
                    sample_research_id,
                    "Test Title",
                    "https://example.com",
                    source_type="pdf",
                )

                call_kwargs = mock_resource_class.call_args[1]
                assert call_kwargs["source_type"] == "pdf"

    def test_accepts_metadata(self, mock_db_session, sample_research_id):
        """Test that metadata is passed to resource."""
        from local_deep_research.web.services.resource_service import (
            add_resource,
        )

        metadata = {"key": "value", "extra": "data"}

        with patch(
            "local_deep_research.web.services.resource_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.resource_service.ResearchResource"
            ) as mock_resource_class:
                add_resource(
                    sample_research_id,
                    "Test Title",
                    "https://example.com",
                    metadata=metadata,
                )

                call_kwargs = mock_resource_class.call_args[1]
                assert call_kwargs["resource_metadata"] == metadata

    def test_raises_on_database_error(
        self, mock_db_session, sample_research_id
    ):
        """Test that exceptions are raised on database error."""
        from local_deep_research.web.services.resource_service import (
            add_resource,
        )

        mock_db_session.add.side_effect = Exception("DB error")

        with patch(
            "local_deep_research.web.services.resource_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 pytest.raises(Exception):
                add_resource(
                    sample_research_id, "Test Title", "https://example.com"
                )


class TestDeleteResource:
    """Tests for delete_resource function."""

    def test_deletes_existing_resource(self, mock_db_session):
        """Test that existing resource is deleted."""
        from local_deep_research.web.services.resource_service import (
            delete_resource,
        )

        mock_resource = Mock()
        mock_db_session.query.return_value.filter_by.return_value.first.return_value = mock_resource

        with patch(
            "local_deep_research.web.services.resource_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 = delete_resource(1)

            mock_db_session.delete.assert_called_once_with(mock_resource)
            mock_db_session.commit.assert_called_once()
            assert result is True

    def test_raises_value_error_when_not_found(self, mock_db_session):
        """Test that ValueError is raised when resource not found."""
        from local_deep_research.web.services.resource_service import (
            delete_resource,
        )

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

        with patch(
            "local_deep_research.web.services.resource_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 pytest.raises(ValueError) as exc_info:
                delete_resource(999)

            assert "not found" in str(exc_info.value)

    def test_logs_successful_deletion(self, mock_db_session):
        """Test that successful deletion is logged."""
        from local_deep_research.web.services.resource_service import (
            delete_resource,
        )

        mock_resource = Mock()
        mock_db_session.query.return_value.filter_by.return_value.first.return_value = mock_resource

        with patch(
            "local_deep_research.web.services.resource_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.resource_service.logger"
            ) as mock_logger:
                delete_resource(1)

                mock_logger.info.assert_called_once()

    def test_raises_on_database_error(self, mock_db_session):
        """Test that database errors are propagated."""
        from local_deep_research.web.services.resource_service import (
            delete_resource,
        )

        mock_db_session.query.side_effect = Exception("DB error")

        with patch(
            "local_deep_research.web.services.resource_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 pytest.raises(Exception):
                delete_resource(1)
