"""
Test that all strategies can be instantiated with mocked dependencies.

This is Level 2 testing - verifying constructors work with proper mocks.
"""

import pytest
from loguru import logger

from .conftest import FACTORY_STRATEGY_NAMES


class TestFactoryInstantiation:
    """Test strategy instantiation via the factory."""

    @pytest.mark.parametrize("strategy_name", FACTORY_STRATEGY_NAMES)
    def test_factory_creates_strategy(
        self,
        strategy_name: str,
        strategy_mock_llm,
        strategy_mock_search,
        strategy_settings_snapshot,
    ):
        """Test that the factory can create each strategy with mocked dependencies."""
        from local_deep_research.search_system_factory import (
            create_strategy,
        )

        try:
            strategy = create_strategy(
                strategy_name=strategy_name,
                model=strategy_mock_llm,
                search=strategy_mock_search,
                settings_snapshot=strategy_settings_snapshot,
            )

            # Basic assertions
            assert strategy is not None, (
                f"Factory returned None for {strategy_name}"
            )

            # Check required attributes exist
            assert hasattr(strategy, "analyze_topic"), (
                f"{strategy_name} missing analyze_topic method"
            )
            assert hasattr(strategy, "set_progress_callback"), (
                f"{strategy_name} missing set_progress_callback method"
            )

            # Check that basic attributes are set
            assert hasattr(strategy, "all_links_of_system")
            assert hasattr(strategy, "questions_by_iteration")

            logger.info(
                f"Successfully instantiated {strategy_name} -> {strategy.__class__.__name__}"
            )

        except Exception as e:
            logger.exception(
                f"Failed to instantiate {strategy_name}: {type(e).__name__}"
            )
            pytest.fail(
                f"Failed to instantiate {strategy_name}: {type(e).__name__}: {e}"
            )

    @pytest.mark.parametrize("strategy_name", FACTORY_STRATEGY_NAMES)
    def test_factory_creates_strategy_minimal(
        self,
        strategy_name: str,
        strategy_mock_llm,
        strategy_mock_search,
    ):
        """Test factory instantiation with minimal arguments (no settings)."""
        from local_deep_research.search_system_factory import (
            create_strategy,
        )

        try:
            strategy = create_strategy(
                strategy_name=strategy_name,
                model=strategy_mock_llm,
                search=strategy_mock_search,
            )

            assert strategy is not None

            logger.info(f"Minimal instantiation successful: {strategy_name}")

        except Exception as e:
            # Some strategies may require settings - log and note for analysis
            logger.warning(
                f"Minimal instantiation failed for {strategy_name}: {type(e).__name__}: {e}"
            )
            # Don't fail - this is informational about which strategies need settings


class TestSearchSystemInstantiation:
    """Test AdvancedSearchSystem instantiation with different strategies."""

    @pytest.mark.parametrize(
        "strategy_name",
        [
            "source-based",
            "rapid",
            "parallel",
            "iterdrag",
            "standard",
        ],
    )
    def test_search_system_with_strategy(
        self,
        strategy_name: str,
        strategy_mock_llm,
        strategy_mock_search,
        strategy_settings_snapshot,
    ):
        """Test AdvancedSearchSystem instantiation with common strategies."""
        from local_deep_research.search_system import AdvancedSearchSystem

        try:
            system = AdvancedSearchSystem(
                llm=strategy_mock_llm,
                search=strategy_mock_search,
                strategy_name=strategy_name,
                settings_snapshot=strategy_settings_snapshot,
            )

            assert system is not None
            assert system.strategy is not None
            assert hasattr(system, "analyze_topic")

            logger.info(
                f"AdvancedSearchSystem created with {strategy_name}: "
                f"{system.strategy.__class__.__name__}"
            )

        except Exception as e:
            pytest.fail(
                f"AdvancedSearchSystem failed with {strategy_name}: {e}"
            )


class TestProgressCallbackSetup:
    """Test that progress callbacks can be set on strategies."""

    @pytest.mark.parametrize(
        "strategy_name", FACTORY_STRATEGY_NAMES[:10]
    )  # Test subset
    def test_set_progress_callback(
        self,
        strategy_name: str,
        strategy_mock_llm,
        strategy_mock_search,
        strategy_settings_snapshot,
    ):
        """Test that progress callback can be set on strategies."""
        from local_deep_research.search_system_factory import (
            create_strategy,
        )
        from unittest.mock import Mock

        strategy = create_strategy(
            strategy_name=strategy_name,
            model=strategy_mock_llm,
            search=strategy_mock_search,
            settings_snapshot=strategy_settings_snapshot,
        )

        callback = Mock()
        strategy.set_progress_callback(callback)

        assert strategy.progress_callback == callback


class TestStrategyDefaultValues:
    """Test that strategies have sensible default values."""

    @pytest.mark.parametrize(
        "strategy_name", FACTORY_STRATEGY_NAMES[:10]
    )  # Test subset
    def test_default_all_links_empty(
        self,
        strategy_name: str,
        strategy_mock_llm,
        strategy_mock_search,
        strategy_settings_snapshot,
    ):
        """Test that all_links_of_system starts empty by default."""
        from local_deep_research.search_system_factory import (
            create_strategy,
        )

        strategy = create_strategy(
            strategy_name=strategy_name,
            model=strategy_mock_llm,
            search=strategy_mock_search,
            settings_snapshot=strategy_settings_snapshot,
        )

        # Should be an empty list (not None, not shared reference)
        assert strategy.all_links_of_system is not None
        assert isinstance(strategy.all_links_of_system, list)

    @pytest.mark.parametrize(
        "strategy_name", FACTORY_STRATEGY_NAMES[:10]
    )  # Test subset
    def test_default_questions_by_iteration_empty(
        self,
        strategy_name: str,
        strategy_mock_llm,
        strategy_mock_search,
        strategy_settings_snapshot,
    ):
        """Test that questions_by_iteration starts empty by default."""
        from local_deep_research.search_system_factory import (
            create_strategy,
        )

        strategy = create_strategy(
            strategy_name=strategy_name,
            model=strategy_mock_llm,
            search=strategy_mock_search,
            settings_snapshot=strategy_settings_snapshot,
        )

        # Should be an empty dict (not None, not shared reference)
        assert strategy.questions_by_iteration is not None
        assert isinstance(strategy.questions_by_iteration, dict)


class TestOutputInstructionsPropagation:
    """Regression test: settings_snapshot with output_instructions reaches citation handler."""

    STRATEGIES_WITH_CITATION_HANDLER = [
        "source-based",
        "standard",
        "parallel",
        "rapid",
        "iterdrag",
        "focused-iteration",
    ]

    @pytest.mark.parametrize("strategy_name", STRATEGIES_WITH_CITATION_HANDLER)
    def test_output_instructions_reach_citation_handler(
        self,
        strategy_name: str,
        strategy_mock_llm,
        strategy_mock_search,
        strategy_settings_snapshot,
    ):
        """Test that general.output_instructions in the snapshot is visible to the citation handler."""
        from local_deep_research.search_system_factory import create_strategy

        snapshot = {
            **strategy_settings_snapshot,
            "general.output_instructions": "Respond in Spanish",
        }

        strategy = create_strategy(
            strategy_name=strategy_name,
            model=strategy_mock_llm,
            search=strategy_mock_search,
            settings_snapshot=snapshot,
        )

        handler = strategy.citation_handler
        # The internal handler should produce the output instruction prefix
        prefix = handler._handler._get_output_instruction_prefix()
        assert "Respond in Spanish" in prefix

    @pytest.mark.parametrize("strategy_name", STRATEGIES_WITH_CITATION_HANDLER)
    def test_no_output_instructions_produces_empty_prefix(
        self,
        strategy_name: str,
        strategy_mock_llm,
        strategy_mock_search,
        strategy_settings_snapshot,
    ):
        """Test that omitting output_instructions yields an empty prefix."""
        from local_deep_research.search_system_factory import create_strategy

        strategy = create_strategy(
            strategy_name=strategy_name,
            model=strategy_mock_llm,
            search=strategy_mock_search,
            settings_snapshot=strategy_settings_snapshot,
        )

        handler = strategy.citation_handler
        prefix = handler._handler._get_output_instruction_prefix()
        assert prefix == ""


class TestSharedLinksIsolation:
    """Test that all_links_of_system is properly isolated between instances."""

    def test_separate_instances_have_separate_links(
        self,
        strategy_mock_llm,
        strategy_mock_search,
        strategy_settings_snapshot,
    ):
        """Test that two strategy instances don't share links list."""
        from local_deep_research.search_system_factory import (
            create_strategy,
        )

        strategy1 = create_strategy(
            strategy_name="source-based",
            model=strategy_mock_llm,
            search=strategy_mock_search,
            settings_snapshot=strategy_settings_snapshot,
        )

        strategy2 = create_strategy(
            strategy_name="source-based",
            model=strategy_mock_llm,
            search=strategy_mock_search,
            settings_snapshot=strategy_settings_snapshot,
        )

        # Modify one
        strategy1.all_links_of_system.append({"test": "link"})

        # Other should be unaffected
        assert len(strategy2.all_links_of_system) == 0
        assert (
            strategy1.all_links_of_system is not strategy2.all_links_of_system
        )
