"""Per-collection "Available to the research agent" flag (agent_enabled).

A collection a user marked as NOT available to the research agent must be
excluded from the LangGraph agent's specialized tool list — its name never
reaches ``create_agent()`` — while agent-enabled collections survive. This is a
usability switch, independent of egress scope: it filters even a collection
that egress would otherwise allow.

Drives the REAL ``LangGraphAgentStrategy._build_tools`` loop; only the leaf tool
factories and engine discovery are mocked, so the assertion is the filter
decision (which collection names survive into the built tool list).
"""

from __future__ import annotations

from contextlib import contextmanager
from types import SimpleNamespace
from unittest.mock import MagicMock, patch

import pytest

import local_deep_research.advanced_search_system.strategies.langgraph_agent_strategy as strat  # noqa: E501
from local_deep_research.security import clear_active_context


@pytest.fixture(autouse=True)
def _clear_ctx():
    clear_active_context()
    yield
    clear_active_context()


@contextmanager
def _patched_factories():
    spec = MagicMock(
        side_effect=lambda name, *a, **k: SimpleNamespace(name=name)
    )
    with (
        patch.object(
            strat,
            "_make_web_search_tool",
            lambda *a, **k: SimpleNamespace(name="web_search"),
        ),
        patch.object(strat, "build_fetch_tool", lambda *a, **k: None),
        patch.object(strat, "_make_specialized_search_tool", spec),
    ):
        yield spec


def _build(available):
    # BOTH scope: a collection is allowed by egress regardless of is_public, so
    # the ONLY differentiator left is the agent_enabled flag.
    snapshot = {"policy.egress_scope": "both", "search.fetch.mode": "disabled"}
    reg = MagicMock()
    reg.get_metadata.return_value = None
    with _patched_factories() as spec:
        with (
            patch(
                "local_deep_research.web_search_engines.search_engines_config"
                ".get_available_engines",
                return_value=available,
            ),
            patch(
                "local_deep_research.web_search_engines.retriever_registry"
                ".retriever_registry",
                reg,
            ),
        ):
            strategy = strat.LangGraphAgentStrategy(
                model=MagicMock(),
                search=MagicMock(),
                citation_handler=object(),
                include_sub_research=False,
                settings_snapshot=snapshot,
            )
            tools = strategy._build_tools("overall query")
    return {t.name for t in tools}, spec


def _coll(is_public=True, agent_enabled=True):
    cfg = {
        "is_public": is_public,
        "is_local": not is_public,
        "is_retriever": False,
        "description": "a collection",
        "strengths": [],
    }
    if agent_enabled is not None:
        cfg["agent_enabled"] = agent_enabled
    return cfg


def test_agent_disabled_collection_excluded_from_tools():
    names, spec = _build(
        {
            "collection_enabled": _coll(agent_enabled=True),
            "collection_disabled": _coll(agent_enabled=False),
        }
    )
    assert "collection_enabled" in names
    assert "collection_disabled" not in names
    # The disabled collection's tool factory was never even called (the loop
    # `continue`s before construction).
    built = {c.args[0] for c in spec.call_args_list}
    assert "collection_disabled" not in built
    assert "collection_enabled" in built


def test_missing_flag_defaults_to_available():
    names, _ = _build({"collection_x": _coll(agent_enabled=None)})
    assert "collection_x" in names


def test_agent_flag_independent_of_egress():
    # A PUBLIC collection that egress would allow under BOTH is still excluded
    # when agent_enabled is False — proving the switch is orthogonal to scope.
    names, _ = _build(
        {"collection_pub_disabled": _coll(is_public=True, agent_enabled=False)}
    )
    assert "collection_pub_disabled" not in names
