"""Tests for FollowUpResearchService."""

from unittest.mock import Mock, MagicMock, patch

from local_deep_research.followup_research.service import (
    FollowUpResearchService,
)
from local_deep_research.followup_research.models import FollowUpRequest


class TestFollowUpResearchServiceInit:
    """Tests for FollowUpResearchService initialization."""

    def test_init_with_username(self):
        """Initialize service with username."""
        service = FollowUpResearchService(username="testuser")
        assert service.username == "testuser"

    def test_init_without_username(self):
        """Initialize service without username (default None)."""
        service = FollowUpResearchService()
        assert service.username is None

    def test_init_with_empty_username(self):
        """Initialize service with empty username."""
        service = FollowUpResearchService(username="")
        assert service.username == ""


class TestLoadParentResearch:
    """Tests for load_parent_research method."""

    @patch("local_deep_research.followup_research.service.get_user_db_session")
    @patch(
        "local_deep_research.followup_research.service.ResearchSourcesService"
    )
    def test_load_parent_research_success(
        self, mock_sources_service_class, mock_get_session
    ):
        """Successfully load parent research data."""
        # Setup mock session
        mock_session = MagicMock()
        mock_get_session.return_value.__enter__ = Mock(
            return_value=mock_session
        )
        mock_get_session.return_value.__exit__ = Mock(return_value=False)

        # Setup mock research
        mock_research = Mock()
        mock_research.id = "parent-123"
        mock_research.query = "Original query"
        mock_research.report_content = "Report content"
        mock_research.research_meta = {
            "formatted_findings": "Findings text",
            "strategy_name": "iterative",
        }
        mock_session.query.return_value.filter_by.return_value.first.return_value = mock_research

        # Setup mock sources service
        mock_sources_service = Mock()
        mock_sources_service.get_research_sources.return_value = [
            {"title": "Source 1", "url": "https://example.com/1"}
        ]
        mock_sources_service_class.return_value = mock_sources_service

        service = FollowUpResearchService(username="testuser")
        result = service.load_parent_research("parent-123")

        assert result["research_id"] == "parent-123"
        assert result["query"] == "Original query"
        assert result["report_content"] == "Report content"
        assert result["strategy"] == "iterative"
        assert len(result["resources"]) == 1

    @patch("local_deep_research.followup_research.service.get_user_db_session")
    def test_load_parent_research_not_found(self, mock_get_session):
        """Return empty dict when parent research not found."""
        mock_session = MagicMock()
        mock_get_session.return_value.__enter__ = Mock(
            return_value=mock_session
        )
        mock_get_session.return_value.__exit__ = Mock(return_value=False)

        mock_session.query.return_value.filter_by.return_value.first.return_value = None

        service = FollowUpResearchService(username="testuser")
        result = service.load_parent_research("nonexistent-id")

        assert result == {}

    @patch("local_deep_research.followup_research.service.get_user_db_session")
    def test_load_parent_research_exception(self, mock_get_session):
        """Return empty dict on exception."""
        mock_get_session.return_value.__enter__ = Mock(
            side_effect=Exception("Database error")
        )

        service = FollowUpResearchService(username="testuser")
        result = service.load_parent_research("parent-123")

        assert result == {}

    @patch("local_deep_research.followup_research.service.get_user_db_session")
    @patch(
        "local_deep_research.followup_research.service.ResearchSourcesService"
    )
    def test_load_parent_research_no_sources_in_db(
        self, mock_sources_service_class, mock_get_session
    ):
        """Load sources from research_meta when not in database."""
        mock_session = MagicMock()
        mock_get_session.return_value.__enter__ = Mock(
            return_value=mock_session
        )
        mock_get_session.return_value.__exit__ = Mock(return_value=False)

        mock_research = Mock()
        mock_research.id = "parent-123"
        mock_research.query = "Query"
        mock_research.report_content = "Report"
        mock_research.research_meta = {
            "all_links_of_system": [
                {"title": "Meta Source", "link": "https://meta.com"}
            ],
            "formatted_findings": "",
            "strategy_name": "",
        }
        mock_session.query.return_value.filter_by.return_value.first.return_value = mock_research

        # First call returns empty, second call returns saved sources
        mock_sources_service = Mock()
        mock_sources_service.get_research_sources.side_effect = [
            [],  # First call - no sources in DB
            [
                {"title": "Meta Source", "url": "https://meta.com"}
            ],  # After saving
        ]
        mock_sources_service.save_research_sources.return_value = 1
        mock_sources_service_class.return_value = mock_sources_service

        service = FollowUpResearchService(username="testuser")
        service.load_parent_research("parent-123")

        # Should have saved and retrieved sources from meta
        mock_sources_service.save_research_sources.assert_called_once()

    @patch("local_deep_research.followup_research.service.get_user_db_session")
    @patch(
        "local_deep_research.followup_research.service.ResearchSourcesService"
    )
    def test_load_parent_research_null_meta(
        self, mock_sources_service_class, mock_get_session
    ):
        """Handle null research_meta gracefully."""
        mock_session = MagicMock()
        mock_get_session.return_value.__enter__ = Mock(
            return_value=mock_session
        )
        mock_get_session.return_value.__exit__ = Mock(return_value=False)

        mock_research = Mock()
        mock_research.id = "parent-123"
        mock_research.query = "Query"
        mock_research.report_content = "Report"
        mock_research.research_meta = None
        mock_session.query.return_value.filter_by.return_value.first.return_value = mock_research

        mock_sources_service = Mock()
        mock_sources_service.get_research_sources.return_value = []
        mock_sources_service_class.return_value = mock_sources_service

        service = FollowUpResearchService(username="testuser")
        result = service.load_parent_research("parent-123")

        assert result["formatted_findings"] == ""
        assert result["strategy"] == ""


class TestPrepareResearchContext:
    """Tests for prepare_research_context method."""

    @patch.object(FollowUpResearchService, "load_parent_research")
    def test_prepare_context_success(self, mock_load_parent):
        """Prepare research context with parent data."""
        mock_load_parent.return_value = {
            "research_id": "parent-123",
            "query": "Original query",
            "report_content": "Report content",
            "formatted_findings": "Findings",
            "resources": [{"title": "Source", "url": "https://example.com"}],
            "all_links_of_system": [
                {"title": "Source", "url": "https://example.com"}
            ],
        }

        service = FollowUpResearchService(username="testuser")
        result = service.prepare_research_context("parent-123")

        assert result["parent_research_id"] == "parent-123"
        assert result["original_query"] == "Original query"
        assert result["report_content"] == "Report content"
        assert result["past_findings"] == "Findings"
        assert len(result["resources"]) == 1

    @patch.object(FollowUpResearchService, "load_parent_research")
    def test_prepare_context_no_parent(self, mock_load_parent):
        """Return empty context when parent not found."""
        mock_load_parent.return_value = {}

        service = FollowUpResearchService(username="testuser")
        result = service.prepare_research_context("nonexistent")

        assert result == {}

    @patch.object(FollowUpResearchService, "load_parent_research")
    def test_prepare_context_missing_fields(self, mock_load_parent):
        """Handle missing fields in parent data."""
        mock_load_parent.return_value = {
            "research_id": "parent-123",
            # Missing other fields
        }

        service = FollowUpResearchService(username="testuser")
        result = service.prepare_research_context("parent-123")

        # Should use .get() with defaults
        assert result["past_links"] == []
        assert result["past_findings"] == ""
        assert result["report_content"] == ""


class TestPerformFollowup:
    """Tests for perform_followup method."""

    @patch.object(FollowUpResearchService, "prepare_research_context")
    def test_perform_followup_success(self, mock_prepare_context):
        """Perform follow-up with valid parent context."""
        mock_prepare_context.return_value = {
            "parent_research_id": "parent-123",
            "past_links": [{"title": "Link", "url": "https://example.com"}],
            "past_findings": "Previous findings",
            "report_content": "Report",
            "resources": [{"title": "Link", "url": "https://example.com"}],
            "all_links_of_system": [
                {"title": "Link", "url": "https://example.com"}
            ],
            "original_query": "Original query",
        }

        request = FollowUpRequest(
            parent_research_id="parent-123",
            question="Follow-up question?",
            strategy="iterative",
            max_iterations=3,
            questions_per_iteration=5,
        )

        service = FollowUpResearchService(username="testuser")
        result = service.perform_followup(request)

        assert result["query"] == "Follow-up question?"
        assert result["strategy"] == "contextual-followup"
        assert result["delegate_strategy"] == "iterative"
        assert result["max_iterations"] == 3
        assert result["questions_per_iteration"] == 5
        assert result["parent_research_id"] == "parent-123"
        assert "research_context" in result

    @patch.object(FollowUpResearchService, "prepare_research_context")
    def test_perform_followup_no_parent_context(self, mock_prepare_context):
        """Perform follow-up with empty parent context (creates default)."""
        mock_prepare_context.return_value = {}

        request = FollowUpRequest(
            parent_research_id="missing-parent",
            question="Follow-up without parent",
        )

        service = FollowUpResearchService(username="testuser")
        result = service.perform_followup(request)

        # Should create empty context
        assert result["query"] == "Follow-up without parent"
        assert result["research_context"]["past_links"] == []
        assert result["research_context"]["past_findings"] == ""
        assert result["research_context"]["report_content"] == ""

    @patch.object(FollowUpResearchService, "prepare_research_context")
    def test_perform_followup_default_strategy(self, mock_prepare_context):
        """Use default strategy when not specified."""
        mock_prepare_context.return_value = {
            "parent_research_id": "parent",
            "past_links": [],
            "past_findings": "",
            "report_content": "",
            "resources": [],
            "all_links_of_system": [],
            "original_query": "",
        }

        request = FollowUpRequest(
            parent_research_id="parent",
            question="Question",
            # strategy defaults to "source-based"
        )

        service = FollowUpResearchService(username="testuser")
        result = service.perform_followup(request)

        assert result["delegate_strategy"] == "source-based"
        assert result["max_iterations"] == 1
        assert result["questions_per_iteration"] == 3
