"""Tests for browser/agent.py -- Coverage boost.

All Playwright interactions are mocked (no real browser needed).
"""

from __future__ import annotations

import tempfile
from pathlib import Path
from unittest.mock import AsyncMock, MagicMock, patch

import pytest

from cognithor.browser.agent import BrowserAgent
from cognithor.browser.types import (
    ActionResult,
    ActionType,
    BrowserAction,
    BrowserConfig,
    BrowserWorkflow,
    ElementInfo,
    ElementType,
    FormField,
    FormInfo,
    PageState,
    WorkflowStatus,
)

# ============================================================================
# Helpers
# ============================================================================


def _mock_page(url="https://example.com", title="Example"):
    """Create a mock Playwright Page."""
    page = AsyncMock()
    page.url = url
    page.title = AsyncMock(return_value=title)
    page.goto = AsyncMock(return_value=MagicMock(status=200))
    page.click = AsyncMock()
    page.fill = AsyncMock()
    page.select_option = AsyncMock()
    page.hover = AsyncMock()
    page.wait_for_selector = AsyncMock()
    page.wait_for_load_state = AsyncMock()
    page.evaluate = AsyncMock(return_value="text content")
    page.screenshot = AsyncMock(return_value=b"\x89PNG\r\n\x1a\n" + b"\x00" * 100)
    page.go_back = AsyncMock()
    page.go_forward = AsyncMock()
    page.reload = AsyncMock()
    page.close = AsyncMock()
    page.keyboard = AsyncMock()
    page.keyboard.press = AsyncMock()
    page.mouse = AsyncMock()
    page.mouse.wheel = AsyncMock()
    return page


def _make_running_agent(page=None, config=None):
    """Create a BrowserAgent that thinks it's running, with mock page."""
    if page is None:
        page = _mock_page()
    agent = BrowserAgent(config=config or BrowserConfig())
    agent._running = True
    agent._start_time = 1000000.0
    agent._pages = [page]
    agent._active_page_idx = 0
    agent._context = AsyncMock()
    agent._context.new_page = AsyncMock(return_value=_mock_page())
    return agent


# ============================================================================
# Lifecycle Tests
# ============================================================================


class TestBrowserAgentLifecycle:
    def test_properties_initial(self):
        agent = BrowserAgent()
        assert agent.is_running is False
        assert agent.page is None
        assert agent.page_count == 0

    def test_page_property_running(self):
        agent = _make_running_agent()
        assert agent.page is not None

    def test_page_property_out_of_range(self):
        agent = BrowserAgent()
        agent._running = True
        agent._pages = []
        agent._active_page_idx = 5
        assert agent.page is None

    @pytest.mark.asyncio
    async def test_start_no_playwright(self):
        """start() returns False if Playwright not installed."""
        agent = BrowserAgent()
        with patch("cognithor.browser.agent._HAS_PLAYWRIGHT", False):
            result = await agent.start()
        assert result is False

    @pytest.mark.asyncio
    async def test_start_already_running(self):
        agent = _make_running_agent()
        with patch("cognithor.browser.agent._HAS_PLAYWRIGHT", True):
            result = await agent.start()
        assert result is True

    @pytest.mark.asyncio
    async def test_stop_not_running(self):
        agent = BrowserAgent()
        await agent.stop()  # should not raise

    @pytest.mark.asyncio
    async def test_stop_saves_session(self):
        agent = _make_running_agent()
        agent._config.persist_cookies = True
        agent._session_mgr = MagicMock()
        agent._session_mgr.save_from_page = AsyncMock()
        await agent.stop(session_id="s1")
        agent._session_mgr.save_from_page.assert_awaited_once()

    @pytest.mark.asyncio
    async def test_cleanup(self):
        agent = _make_running_agent()
        agent._playwright = AsyncMock()
        agent._browser = AsyncMock()
        await agent._cleanup()
        assert agent._running is False
        assert agent._pages == []


# ============================================================================
# Core Action Tests
# ============================================================================


class TestBrowserAgentActions:
    def test_ensure_running_raises(self):
        agent = BrowserAgent()
        with pytest.raises(RuntimeError, match="not started"):
            agent._ensure_running()

    @pytest.mark.asyncio
    async def test_navigate_success(self):
        agent = _make_running_agent()
        agent._analyzer = MagicMock()
        agent._analyzer.analyze = AsyncMock(
            return_value=PageState(url="https://example.com", is_loaded=True)
        )
        agent._analyzer.detect_cookie_banner = AsyncMock(return_value={"found": False})

        state = await agent.navigate("https://example.com")
        assert state.url == "https://example.com"
        assert state.status_code == 200
        assert agent._action_count == 1

    @pytest.mark.asyncio
    async def test_navigate_error(self):
        page = _mock_page()
        page.goto = AsyncMock(side_effect=Exception("Nav error"))
        agent = _make_running_agent(page=page)

        state = await agent.navigate("https://bad.com")
        assert len(state.errors) > 0
        assert "Nav error" in state.errors[0]

    @pytest.mark.asyncio
    async def test_navigate_no_cookie_dismiss(self):
        agent = _make_running_agent()
        agent._analyzer = MagicMock()
        agent._analyzer.analyze = AsyncMock(return_value=PageState())
        state = await agent.navigate("https://example.com", auto_dismiss_cookies=False)
        assert state is not None

    @pytest.mark.asyncio
    async def test_click_success(self):
        page = _mock_page()
        page.url = "https://example.com"
        agent = _make_running_agent(page=page)
        result = await agent.click("#btn")
        assert result.success is True
        assert agent._action_count == 1

    @pytest.mark.asyncio
    async def test_click_error(self):
        page = _mock_page()
        page.click = AsyncMock(side_effect=Exception("Click fail"))
        agent = _make_running_agent(page=page)
        result = await agent.click("#btn")
        assert result.success is False
        assert agent._error_count == 1

    @pytest.mark.asyncio
    async def test_fill_success(self):
        agent = _make_running_agent()
        result = await agent.fill("#input", "value")
        assert result.success is True

    @pytest.mark.asyncio
    async def test_fill_error(self):
        page = _mock_page()
        page.fill = AsyncMock(side_effect=Exception("Fill error"))
        agent = _make_running_agent(page=page)
        result = await agent.fill("#input", "value")
        assert result.success is False

    @pytest.mark.asyncio
    async def test_fill_no_clear(self):
        agent = _make_running_agent()
        result = await agent.fill("#input", "value", clear=False)
        assert result.success is True

    @pytest.mark.asyncio
    async def test_select_success(self):
        agent = _make_running_agent()
        result = await agent.select("#sel", "option1")
        assert result.success is True

    @pytest.mark.asyncio
    async def test_select_error(self):
        page = _mock_page()
        page.select_option = AsyncMock(side_effect=Exception("Select error"))
        agent = _make_running_agent(page=page)
        result = await agent.select("#sel", "option1")
        assert result.success is False

    @pytest.mark.asyncio
    async def test_hover_success(self):
        agent = _make_running_agent()
        result = await agent.hover("#el")
        assert result.success is True

    @pytest.mark.asyncio
    async def test_hover_error(self):
        page = _mock_page()
        page.hover = AsyncMock(side_effect=Exception("Hover error"))
        agent = _make_running_agent(page=page)
        result = await agent.hover("#el")
        assert result.success is False

    @pytest.mark.asyncio
    async def test_press_key_success(self):
        agent = _make_running_agent()
        result = await agent.press_key("Enter")
        assert result.success is True

    @pytest.mark.asyncio
    async def test_press_key_error(self):
        page = _mock_page()
        page.keyboard.press = AsyncMock(side_effect=Exception("Key error"))
        agent = _make_running_agent(page=page)
        result = await agent.press_key("Enter")
        assert result.success is False

    @pytest.mark.asyncio
    async def test_scroll_down(self):
        agent = _make_running_agent()
        with patch("asyncio.sleep", new_callable=AsyncMock):
            result = await agent.scroll("down", 500)
        assert result.success is True

    @pytest.mark.asyncio
    async def test_scroll_up(self):
        agent = _make_running_agent()
        with patch("asyncio.sleep", new_callable=AsyncMock):
            result = await agent.scroll("up", 300)
        assert result.success is True

    @pytest.mark.asyncio
    async def test_scroll_error(self):
        page = _mock_page()
        page.mouse.wheel = AsyncMock(side_effect=Exception("Scroll error"))
        agent = _make_running_agent(page=page)
        result = await agent.scroll()
        assert result.success is False

    @pytest.mark.asyncio
    async def test_wait_for_success(self):
        agent = _make_running_agent()
        result = await agent.wait_for("#el")
        assert result.success is True

    @pytest.mark.asyncio
    async def test_wait_for_error(self):
        page = _mock_page()
        page.wait_for_selector = AsyncMock(side_effect=Exception("Timeout"))
        agent = _make_running_agent(page=page)
        result = await agent.wait_for("#el")
        assert result.success is False

    @pytest.mark.asyncio
    async def test_execute_js_success(self):
        agent = _make_running_agent()
        result = await agent.execute_js("return 42")
        assert result.success is True

    @pytest.mark.asyncio
    async def test_execute_js_error(self):
        page = _mock_page()
        page.evaluate = AsyncMock(side_effect=Exception("JS error"))
        agent = _make_running_agent(page=page)
        result = await agent.execute_js("bad()")
        assert result.success is False


# ============================================================================
# Screenshot Tests
# ============================================================================


class TestBrowserAgentScreenshot:
    @pytest.mark.asyncio
    async def test_screenshot_success(self):
        agent = _make_running_agent()
        result = await agent.screenshot()
        assert result.success is True
        assert result.screenshot_b64 != ""

    @pytest.mark.asyncio
    async def test_screenshot_with_path(self):
        agent = _make_running_agent()
        result = await agent.screenshot(
            path=str(Path(tempfile.gettempdir()) / "test.png"), full_page=True
        )
        assert result.success is True

    @pytest.mark.asyncio
    async def test_screenshot_error(self):
        page = _mock_page()
        page.screenshot = AsyncMock(side_effect=Exception("Screenshot error"))
        agent = _make_running_agent(page=page)
        result = await agent.screenshot()
        assert result.success is False


# ============================================================================
# Tab Management Tests
# ============================================================================


class TestBrowserAgentTabs:
    @pytest.mark.asyncio
    async def test_new_tab(self):
        agent = _make_running_agent()
        result = await agent.new_tab("https://new.com")
        assert result.success is True
        assert agent.page_count == 2

    @pytest.mark.asyncio
    async def test_new_tab_max_reached(self):
        config = BrowserConfig(max_pages=1)
        agent = _make_running_agent(config=config)
        result = await agent.new_tab()
        assert result.success is False
        assert "Max tabs" in result.error

    @pytest.mark.asyncio
    async def test_new_tab_error(self):
        agent = _make_running_agent()
        agent._context.new_page = AsyncMock(side_effect=Exception("Tab error"))
        result = await agent.new_tab()
        assert result.success is False

    @pytest.mark.asyncio
    async def test_close_tab(self):
        page1 = _mock_page()
        page2 = _mock_page()
        agent = _make_running_agent(page=page1)
        agent._pages = [page1, page2]
        result = await agent.close_tab(1)
        assert result.success is True
        assert agent.page_count == 1

    @pytest.mark.asyncio
    async def test_close_last_tab_fails(self):
        agent = _make_running_agent()
        result = await agent.close_tab()
        assert result.success is False
        assert "last tab" in result.error.lower()

    @pytest.mark.asyncio
    async def test_close_tab_invalid_index(self):
        page1 = _mock_page()
        page2 = _mock_page()
        agent = _make_running_agent(page=page1)
        agent._pages = [page1, page2]
        result = await agent.close_tab(99)
        assert result.success is False

    def test_switch_tab(self):
        agent = _make_running_agent()
        agent._pages = [_mock_page(), _mock_page()]
        result = agent.switch_tab(1)
        assert result.success is True
        assert agent._active_page_idx == 1

    def test_switch_tab_invalid(self):
        agent = _make_running_agent()
        result = agent.switch_tab(99)
        assert result.success is False


# ============================================================================
# Content Extraction Tests
# ============================================================================


class TestBrowserAgentExtraction:
    @pytest.mark.asyncio
    async def test_analyze_page(self):
        agent = _make_running_agent()
        agent._analyzer = MagicMock()
        agent._analyzer.analyze = AsyncMock(return_value=PageState(url="https://example.com"))
        state = await agent.analyze_page()
        assert state.url == "https://example.com"

    @pytest.mark.asyncio
    async def test_extract_text(self):
        agent = _make_running_agent()
        text = await agent.extract_text("body")
        assert isinstance(text, str)

    @pytest.mark.asyncio
    async def test_extract_text_error(self):
        page = _mock_page()
        page.evaluate = AsyncMock(side_effect=Exception("Error"))
        agent = _make_running_agent(page=page)
        text = await agent.extract_text()
        assert text == ""

    @pytest.mark.asyncio
    async def test_extract_tables(self):
        agent = _make_running_agent()
        agent._analyzer = MagicMock()
        agent._analyzer.analyze = AsyncMock(return_value=PageState(tables=[{"col": "val"}]))
        tables = await agent.extract_tables()
        assert len(tables) == 1

    @pytest.mark.asyncio
    async def test_extract_links(self):
        agent = _make_running_agent()
        link = ElementInfo(
            selector="a", element_type=ElementType.LINK, text="Link", href="https://example.com"
        )
        agent._analyzer = MagicMock()
        agent._analyzer.analyze = AsyncMock(return_value=PageState(links=[link]))
        links = await agent.extract_links()
        assert len(links) == 1
        assert links[0]["href"] == "https://example.com"

    @pytest.mark.asyncio
    async def test_find_and_click_found(self):
        agent = _make_running_agent()
        element = ElementInfo(selector="#btn", element_type=ElementType.BUTTON)
        agent._analyzer = MagicMock()
        agent._analyzer.find_element = AsyncMock(return_value=element)
        result = await agent.find_and_click("Submit button")
        assert result.success is True

    @pytest.mark.asyncio
    async def test_find_and_click_not_found(self):
        agent = _make_running_agent()
        agent._analyzer = MagicMock()
        agent._analyzer.find_element = AsyncMock(return_value=None)
        result = await agent.find_and_click("Nonexistent")
        assert result.success is False
        assert "not found" in result.error.lower()


# ============================================================================
# Form Automation Tests
# ============================================================================


class TestBrowserAgentForms:
    @pytest.mark.asyncio
    async def test_fill_form(self):
        agent = _make_running_agent()
        form = FormInfo(
            fields=[
                FormField(name="email", field_type="email", selector="#email"),
                FormField(name="country", field_type="select", selector="#country"),
            ],
            submit_selector="#submit",
        )
        agent._analyzer = MagicMock()
        agent._analyzer.analyze = AsyncMock(return_value=PageState(forms=[form]))
        results = await agent.fill_form({"email": "a@b.com", "country": "DE"}, submit=True)
        assert len(results) >= 2  # fill + select + click

    @pytest.mark.asyncio
    async def test_fill_form_no_match(self):
        agent = _make_running_agent()
        form = FormInfo(fields=[FormField(name="other", field_type="text", selector="#x")])
        agent._analyzer = MagicMock()
        agent._analyzer.analyze = AsyncMock(return_value=PageState(forms=[form]))
        results = await agent.fill_form({"email": "a@b.com"})
        assert len(results) == 0


# ============================================================================
# Workflow Execution Tests
# ============================================================================


class TestBrowserAgentWorkflow:
    @pytest.mark.asyncio
    async def test_execute_workflow_success(self):
        agent = _make_running_agent()
        agent._analyzer = MagicMock()
        agent._analyzer.analyze = AsyncMock(return_value=PageState())
        agent._analyzer.detect_cookie_banner = AsyncMock(return_value={"found": False})

        workflow = BrowserWorkflow(
            name="test",
            steps=[
                BrowserAction(
                    action_type=ActionType.NAVIGATE, params={"url": "https://example.com"}
                ),
                BrowserAction(action_type=ActionType.CLICK, params={"selector": "#btn"}),
            ],
        )
        result = await agent.execute_workflow(workflow)
        assert result.status == WorkflowStatus.COMPLETED
        assert len(result.results) == 2

    @pytest.mark.asyncio
    async def test_execute_workflow_failure_with_retry(self):
        page = _mock_page()
        call_count = 0

        async def failing_click(*args, **kwargs):
            nonlocal call_count
            call_count += 1
            raise Exception("Click fail")

        page.click = failing_click
        agent = _make_running_agent(page=page)
        agent._config.screenshot_on_error = False

        workflow = BrowserWorkflow(
            name="fail_test",
            steps=[BrowserAction(action_type=ActionType.CLICK, params={"selector": "#btn"})],
            max_retries=1,
        )
        result = await agent.execute_workflow(workflow)
        assert result.status == WorkflowStatus.FAILED

    @pytest.mark.asyncio
    async def test_execute_action_all_types(self):
        """Test all ActionType branches in _execute_action."""
        agent = _make_running_agent()
        agent._analyzer = MagicMock()
        agent._analyzer.analyze = AsyncMock(return_value=PageState())
        agent._analyzer.detect_cookie_banner = AsyncMock(return_value={"found": False})

        action_types_and_params = [
            (ActionType.NAVIGATE, {"url": "https://example.com"}),
            (ActionType.CLICK, {"selector": "#btn"}),
            (ActionType.FILL, {"selector": "#input", "value": "text"}),
            (ActionType.SELECT, {"selector": "#sel", "value": "opt"}),
            (ActionType.SCROLL, {"direction": "down", "amount": 200}),
            (ActionType.SCREENSHOT, {"full_page": False}),
            (ActionType.WAIT_FOR, {"selector": "#el"}),
            (ActionType.EXECUTE_JS, {"script": "return 1"}),
            (ActionType.GO_BACK, {}),
            (ActionType.GO_FORWARD, {}),
            (ActionType.REFRESH, {}),
            (ActionType.SWITCH_TAB, {"index": 0}),
            (ActionType.HOVER, {"selector": "#el"}),
            (ActionType.PRESS_KEY, {"key": "Enter"}),
            (ActionType.EXTRACT_TEXT, {"selector": "body"}),
            (ActionType.EXTRACT_TABLE, {}),
            (ActionType.EXTRACT_LINKS, {}),
        ]

        with patch("asyncio.sleep", new_callable=AsyncMock):
            for at, params in action_types_and_params:
                action = BrowserAction(action_type=at, params=params)
                result = await agent._execute_action(action)
                # Just verify it doesn't crash
                assert isinstance(result, ActionResult), f"Failed for {at}"

    @pytest.mark.asyncio
    async def test_execute_action_wait(self):
        agent = _make_running_agent()
        action = BrowserAction(action_type=ActionType.WAIT, params={"seconds": 0.01})
        with patch("asyncio.sleep", new_callable=AsyncMock):
            result = await agent._execute_action(action)
        assert result.success is True

    @pytest.mark.asyncio
    async def test_execute_action_new_tab(self):
        agent = _make_running_agent()
        action = BrowserAction(action_type=ActionType.NEW_TAB, params={"url": "https://new.com"})
        result = await agent._execute_action(action)
        assert result.success is True

    @pytest.mark.asyncio
    async def test_execute_action_close_tab(self):
        agent = _make_running_agent()
        agent._pages = [_mock_page(), _mock_page()]
        action = BrowserAction(action_type=ActionType.CLOSE_TAB, params={"index": 1})
        result = await agent._execute_action(action)
        assert result.success is True

    @pytest.mark.asyncio
    async def test_execute_action_exception(self):
        page = _mock_page()
        page.go_back = AsyncMock(side_effect=Exception("go_back error"))
        agent = _make_running_agent(page=page)
        action = BrowserAction(action_type=ActionType.GO_BACK, params={})
        result = await agent._execute_action(action)
        assert result.success is False

    def test_get_workflow(self):
        agent = BrowserAgent()
        assert agent.get_workflow("nope") is None


# ============================================================================
# Cookie Banner Tests
# ============================================================================


class TestBrowserAgentCookies:
    @pytest.mark.asyncio
    async def test_dismiss_cookies_found(self):
        agent = _make_running_agent()
        agent._analyzer = MagicMock()
        agent._analyzer.detect_cookie_banner = AsyncMock(
            return_value={"found": True, "acceptSelector": "#accept"}
        )
        with patch("asyncio.sleep", new_callable=AsyncMock):
            result = await agent._try_dismiss_cookies()
        assert result is True

    @pytest.mark.asyncio
    async def test_dismiss_cookies_not_found(self):
        agent = _make_running_agent()
        agent._analyzer = MagicMock()
        agent._analyzer.detect_cookie_banner = AsyncMock(return_value={"found": False})
        result = await agent._try_dismiss_cookies()
        assert result is False

    @pytest.mark.asyncio
    async def test_dismiss_cookies_exception(self):
        agent = _make_running_agent()
        agent._analyzer = MagicMock()
        agent._analyzer.detect_cookie_banner = AsyncMock(side_effect=Exception("Error"))
        result = await agent._try_dismiss_cookies()
        assert result is False


# ============================================================================
# Vision Integration Tests
# ============================================================================


class TestBrowserAgentVision:
    @pytest.mark.asyncio
    async def test_extract_page_content(self):
        agent = _make_running_agent()
        content = await agent._extract_page_content()
        assert isinstance(content, str)

    @pytest.mark.asyncio
    async def test_extract_page_content_error(self):
        page = _mock_page()
        page.evaluate = AsyncMock(side_effect=Exception("Error"))
        agent = _make_running_agent(page=page)
        content = await agent._extract_page_content()
        assert content == ""

    @pytest.mark.asyncio
    async def test_analyze_page_with_vision_no_vision(self):
        agent = _make_running_agent()
        agent._analyzer = MagicMock()
        agent._analyzer.analyze = AsyncMock(return_value=PageState(url="https://example.com"))
        result = await agent.analyze_page_with_vision()
        assert "dom" in result
        assert result["vision"] == ""

    @pytest.mark.asyncio
    async def test_analyze_page_with_vision_enabled(self):
        agent = _make_running_agent()
        agent._analyzer = MagicMock()
        agent._analyzer.analyze = AsyncMock(return_value=PageState(url="https://example.com"))

        vision = MagicMock()
        vision.is_enabled = True
        vision_result = MagicMock()
        vision_result.success = True
        vision_result.description = "I see a page"
        vision.analyze_screenshot = AsyncMock(return_value=vision_result)
        agent._vision = vision

        result = await agent.analyze_page_with_vision("What do you see?")
        assert result["vision"] == "I see a page"
        assert "Vision-Analyse" in result["combined"]

    @pytest.mark.asyncio
    async def test_analyze_page_with_vision_error(self):
        agent = _make_running_agent()
        agent._analyzer = MagicMock()
        agent._analyzer.analyze = AsyncMock(return_value=PageState())

        vision = MagicMock()
        vision.is_enabled = True
        vision.analyze_screenshot = AsyncMock(side_effect=Exception("Vision error"))
        agent._vision = vision

        result = await agent.analyze_page_with_vision()
        assert result["vision"] == ""

    @pytest.mark.asyncio
    async def test_find_and_click_with_vision_text_match(self):
        """If text-based find_and_click succeeds, no vision fallback."""
        agent = _make_running_agent()
        element = ElementInfo(selector="#btn", element_type=ElementType.BUTTON)
        agent._analyzer = MagicMock()
        agent._analyzer.find_element = AsyncMock(return_value=element)
        result = await agent.find_and_click_with_vision("Submit")
        assert result.success is True

    @pytest.mark.asyncio
    async def test_find_and_click_with_vision_fallback(self):
        """Text match fails, vision provides hint.

        Note: The agent code does result.data["vision_hint"] = ... which will
        fail if data is None. The agent catches this in a try/except, so the
        vision_hint won't be set. We just verify the code path runs.
        """
        agent = _make_running_agent()
        agent._analyzer = MagicMock()
        agent._analyzer.find_element = AsyncMock(return_value=None)

        vision = MagicMock()
        vision.is_enabled = True
        vision_result = MagicMock()
        vision_result.success = True
        vision_result.description = "Button is at top-right"
        vision.find_element_by_vision = AsyncMock(return_value=vision_result)
        agent._vision = vision

        result = await agent.find_and_click_with_vision("Submit")
        assert result.success is False  # still failed to click

    @pytest.mark.asyncio
    async def test_find_and_click_with_vision_fallback_with_data(self):
        """Text match fails, vision provides hint -- data is a dict."""
        agent = _make_running_agent()
        agent._analyzer = MagicMock()
        agent._analyzer.find_element = AsyncMock(return_value=None)
        # Patch find_and_click to return ActionResult with data={}

        async def patched_find_and_click(desc):
            return ActionResult(action_id="", success=False, error="Not found", data={})

        agent.find_and_click = patched_find_and_click

        vision = MagicMock()
        vision.is_enabled = True
        vision_result = MagicMock()
        vision_result.success = True
        vision_result.description = "Button is at top-right"
        vision.find_element_by_vision = AsyncMock(return_value=vision_result)
        agent._vision = vision

        result = await agent.find_and_click_with_vision("Submit")
        assert result.success is False
        assert result.data.get("vision_hint") == "Button is at top-right"


# ============================================================================
# Stats Tests
# ============================================================================


class TestBrowserAgentStats:
    def test_stats(self):
        agent = _make_running_agent()
        agent._analyzer = MagicMock()
        agent._analyzer.stats.return_value = {}
        agent._session_mgr = MagicMock()
        agent._session_mgr.stats.return_value = {}
        s = agent.stats()
        assert s["running"] is True
        assert "total_actions" in s

    def test_stats_with_vision(self):
        agent = _make_running_agent()
        agent._analyzer = MagicMock()
        agent._analyzer.stats.return_value = {}
        agent._session_mgr = MagicMock()
        agent._session_mgr.stats.return_value = {}
        vision = MagicMock()
        vision.stats.return_value = {"enabled": True}
        agent._vision = vision
        s = agent.stats()
        assert "vision" in s
