"""
Factory for creating search strategies.
This module provides a centralized way to create search strategies
to avoid code duplication.
"""

from loguru import logger
from typing import Optional, Dict, Any, List
from langchain_core.language_models import BaseChatModel

from .utilities.type_utils import unwrap_setting

# Re-export from constants so existing importers don't break
from .constants import (  # noqa: F401
    AVAILABLE_STRATEGIES,
    get_available_strategies,
)


def _get_setting(
    settings_snapshot: Optional[Dict], key: str, default: Any
) -> Any:
    """Get a setting value from the snapshot, handling nested dict structure."""
    if not settings_snapshot or key not in settings_snapshot:
        return default
    value = settings_snapshot[key]
    return unwrap_setting(value)


def create_strategy(
    strategy_name: str,
    model: BaseChatModel,
    search: Any,
    all_links_of_system: Optional[List[Dict]] = None,
    settings_snapshot: Optional[Dict] = None,
    research_context: Optional[Dict] = None,
    **kwargs,
):
    """
    Create a search strategy by name.

    Args:
        strategy_name: Name of the strategy to create
        model: Language model to use
        search: Search engine instance
        all_links_of_system: List of existing links
        settings_snapshot: Settings snapshot
        research_context: Research context for special strategies
        **kwargs: Additional strategy-specific parameters

    Returns:
        Strategy instance
    """
    if all_links_of_system is None:
        all_links_of_system = []

    strategy_name_lower = strategy_name.lower()

    # Source-based strategy
    if strategy_name_lower in [
        "source-based",
        "source_based",
        "source_based_search",
    ]:
        from .advanced_search_system.strategies.source_based_strategy import (
            SourceBasedSearchStrategy,
        )

        return SourceBasedSearchStrategy(
            model=model,
            search=search,
            include_text_content=kwargs.get("include_text_content", True),
            use_cross_engine_filter=kwargs.get("use_cross_engine_filter", True),
            all_links_of_system=all_links_of_system,
            use_atomic_facts=kwargs.get("use_atomic_facts", False),
            settings_snapshot=settings_snapshot,
            search_original_query=kwargs.get("search_original_query", True),
        )

    # Focused iteration strategy
    if strategy_name_lower in ["focused-iteration", "focused_iteration"]:
        from .advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        # Read focused_iteration settings with kwargs override
        # adaptive_questions is stored as 0/1 integer, convert to bool
        enable_adaptive = bool(
            kwargs.get(
                "enable_adaptive_questions",
                _get_setting(
                    settings_snapshot, "focused_iteration.adaptive_questions", 0
                ),
            )
        )
        knowledge_limit = kwargs.get(
            "knowledge_summary_limit",
            _get_setting(
                settings_snapshot,
                "focused_iteration.knowledge_summary_limit",
                10,
            ),
        )
        snippet_truncate = kwargs.get(
            "knowledge_snippet_truncate",
            _get_setting(
                settings_snapshot, "focused_iteration.snippet_truncate", 200
            ),
        )
        question_gen_type = kwargs.get(
            "question_generator",
            _get_setting(
                settings_snapshot,
                "focused_iteration.question_generator",
                "browsecomp",
            ),
        )
        prompt_knowledge_truncate = kwargs.get(
            "prompt_knowledge_truncate",
            _get_setting(
                settings_snapshot,
                "focused_iteration.prompt_knowledge_truncate",
                1500,
            ),
        )
        previous_searches_limit = kwargs.get(
            "previous_searches_limit",
            _get_setting(
                settings_snapshot,
                "focused_iteration.previous_searches_limit",
                10,
            ),
        )
        # Convert 0 to None for "unlimited"
        if knowledge_limit == 0:
            knowledge_limit = None
        if snippet_truncate == 0:
            snippet_truncate = None
        if prompt_knowledge_truncate == 0:
            prompt_knowledge_truncate = None
        if previous_searches_limit == 0:
            previous_searches_limit = None

        strategy = FocusedIterationStrategy(
            model=model,
            search=search,
            all_links_of_system=all_links_of_system,
            max_iterations=kwargs.get("max_iterations", 8),
            questions_per_iteration=kwargs.get("questions_per_iteration", 5),
            settings_snapshot=settings_snapshot,
            # Options read from settings (with kwargs override)
            enable_adaptive_questions=enable_adaptive,
            enable_early_termination=kwargs.get(
                "enable_early_termination", False
            ),
            knowledge_summary_limit=knowledge_limit,
            knowledge_snippet_truncate=snippet_truncate,
            prompt_knowledge_truncate=prompt_knowledge_truncate,
            previous_searches_limit=previous_searches_limit,
        )

        # Override question generator if flexible is selected
        if question_gen_type == "flexible":
            from .advanced_search_system.questions.flexible_browsecomp_question import (
                FlexibleBrowseCompQuestionGenerator,
            )

            # Pass truncation settings to flexible generator
            strategy.question_generator = FlexibleBrowseCompQuestionGenerator(
                model,
                knowledge_truncate_length=prompt_knowledge_truncate,
                previous_searches_limit=previous_searches_limit,
            )

        return strategy

    # Focused iteration strategy with standard citation handler
    if strategy_name_lower in [
        "focused-iteration-standard",
        "focused_iteration_standard",
    ]:
        from .advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )
        from .citation_handler import CitationHandler

        # Use standard citation handler (same question generator as regular focused-iteration)
        standard_citation_handler = CitationHandler(
            model, handler_type="standard", settings_snapshot=settings_snapshot
        )

        # Read focused_iteration settings with kwargs override
        # adaptive_questions is stored as 0/1 integer, convert to bool
        enable_adaptive = bool(
            kwargs.get(
                "enable_adaptive_questions",
                _get_setting(
                    settings_snapshot, "focused_iteration.adaptive_questions", 0
                ),
            )
        )
        knowledge_limit = kwargs.get(
            "knowledge_summary_limit",
            _get_setting(
                settings_snapshot,
                "focused_iteration.knowledge_summary_limit",
                10,
            ),
        )
        snippet_truncate = kwargs.get(
            "knowledge_snippet_truncate",
            _get_setting(
                settings_snapshot, "focused_iteration.snippet_truncate", 200
            ),
        )
        question_gen_type = kwargs.get(
            "question_generator",
            _get_setting(
                settings_snapshot,
                "focused_iteration.question_generator",
                "browsecomp",
            ),
        )
        prompt_knowledge_truncate = kwargs.get(
            "prompt_knowledge_truncate",
            _get_setting(
                settings_snapshot,
                "focused_iteration.prompt_knowledge_truncate",
                1500,
            ),
        )
        previous_searches_limit = kwargs.get(
            "previous_searches_limit",
            _get_setting(
                settings_snapshot,
                "focused_iteration.previous_searches_limit",
                10,
            ),
        )
        # Convert 0 to None for "unlimited"
        if knowledge_limit == 0:
            knowledge_limit = None
        if snippet_truncate == 0:
            snippet_truncate = None
        if prompt_knowledge_truncate == 0:
            prompt_knowledge_truncate = None
        if previous_searches_limit == 0:
            previous_searches_limit = None

        strategy = FocusedIterationStrategy(
            model=model,
            search=search,
            citation_handler=standard_citation_handler,
            all_links_of_system=all_links_of_system,
            max_iterations=kwargs.get("max_iterations", 8),
            questions_per_iteration=kwargs.get("questions_per_iteration", 5),
            use_browsecomp_optimization=True,  # Keep BrowseComp features
            settings_snapshot=settings_snapshot,
            # Options read from settings (with kwargs override)
            enable_adaptive_questions=enable_adaptive,
            enable_early_termination=kwargs.get(
                "enable_early_termination", False
            ),
            knowledge_summary_limit=knowledge_limit,
            knowledge_snippet_truncate=snippet_truncate,
            prompt_knowledge_truncate=prompt_knowledge_truncate,
            previous_searches_limit=previous_searches_limit,
        )

        # Override question generator if flexible is selected
        if question_gen_type == "flexible":
            from .advanced_search_system.questions.flexible_browsecomp_question import (
                FlexibleBrowseCompQuestionGenerator,
            )

            # Pass truncation settings to flexible generator
            strategy.question_generator = FlexibleBrowseCompQuestionGenerator(
                model,
                knowledge_truncate_length=prompt_knowledge_truncate,
                previous_searches_limit=previous_searches_limit,
            )

        return strategy

    # News aggregation strategy (used internally by the news subsystem)
    if strategy_name_lower in [
        "news",
        "news_aggregation",
        "news-aggregation",
    ]:
        from .advanced_search_system.strategies.news_strategy import (
            NewsAggregationStrategy,
        )

        return NewsAggregationStrategy(
            model=model,
            search=search,
            all_links_of_system=all_links_of_system,
        )

    # Topic organization strategy
    if strategy_name_lower in [
        "topic-organization",
        "topic_organization",
        "topic",
    ]:
        from .advanced_search_system.strategies.topic_organization_strategy import (
            TopicOrganizationStrategy,
        )

        return TopicOrganizationStrategy(
            model=model,
            search=search,
            all_links_of_system=all_links_of_system,
            settings_snapshot=settings_snapshot,
            min_sources_per_topic=1,  # Allow single-source topics
            use_cross_engine_filter=kwargs.get("use_cross_engine_filter", True),
            filter_reorder=kwargs.get("filter_reorder", True),
            filter_reindex=kwargs.get("filter_reindex", True),
            cross_engine_max_results=kwargs.get(  # type: ignore[arg-type]
                "cross_engine_max_results", None
            ),
            search_original_query=kwargs.get("search_original_query", True),
            max_topics=kwargs.get("max_topics", 5),
            similarity_threshold=kwargs.get("similarity_threshold", 0.3),
            use_focused_iteration=kwargs.get("use_focused_iteration", False),
            enable_refinement=kwargs.get(
                "enable_refinement", False
            ),  # Disable refinement iterations for now
            max_refinement_iterations=kwargs.get(
                "max_refinement_iterations",
                1,  # Set to 1 iteration for faster results
            ),
            generate_text=kwargs.get("generate_text", True),
        )

    # LangGraph agent strategy (parallel subagent research).
    # ``mcp`` / ``agentic`` were removed (#4548); they remain here as
    # deprecated aliases so existing saved settings, queued runs, and API
    # callers route to the closest successor (langgraph-agent) instead of
    # the source-based fallback below.
    if strategy_name_lower in [
        "langgraph-agent",
        "langgraph_agent",
        "mcp",
        "agentic",
    ]:
        if strategy_name_lower in ("mcp", "agentic"):
            logger.warning(
                f"Strategy {strategy_name!r} was removed (#4548); "
                "using 'langgraph-agent' instead."
            )
        from .advanced_search_system.strategies.langgraph_agent_strategy import (
            LangGraphAgentStrategy,
        )

        return LangGraphAgentStrategy(
            model=model,
            search=search,
            max_iterations=kwargs.get(
                "max_iterations",
                _get_setting(
                    settings_snapshot, "langgraph_agent.max_iterations", 50
                ),
            ),
            max_sub_iterations=kwargs.get(
                "max_sub_iterations",
                _get_setting(
                    settings_snapshot, "langgraph_agent.max_sub_iterations", 8
                ),
            ),
            include_sub_research=kwargs.get(
                "include_sub_research",
                _get_setting(
                    settings_snapshot,
                    "langgraph_agent.include_sub_research",
                    True,
                ),
            ),
            programmatic_mode=kwargs.get("programmatic_mode", False),
            all_links_of_system=all_links_of_system,
            settings_snapshot=settings_snapshot,
        )

    # Default to source-based if unknown
    logger.warning(
        f"Unknown strategy: {strategy_name}, defaulting to source-based"
    )
    from .advanced_search_system.strategies.source_based_strategy import (
        SourceBasedSearchStrategy,
    )

    return SourceBasedSearchStrategy(
        model=model,
        search=search,
        include_text_content=True,
        use_cross_engine_filter=True,
        all_links_of_system=all_links_of_system,
        use_atomic_facts=False,
        settings_snapshot=settings_snapshot,
        search_original_query=kwargs.get("search_original_query", True),
    )
