"""
Guard-path tests for playwright_html that don't require a real browser.

Targets pure-logic branches in
src/local_deep_research/research_library/downloaders/playwright_html.py:
- Lines 57-62: _run_async ThreadPoolExecutor fallback when an event loop
  is already running.
- Lines 117-119: Crawl4AI ImportError path in _fetch_with_crawl4ai.
- Lines 267-269: playwright ImportError path in _fetch_with_playwright.

The existing test_playwright_html.py covers inheritance and SPA-signal
detection but not these ImportError / event-loop fallback guards.
"""

import asyncio
import builtins
from unittest.mock import MagicMock, patch


from local_deep_research.research_library.downloaders.playwright_html import (
    PlaywrightHTMLDownloader,
    _run_async,
)


class TestRunAsyncEventLoopHandling:
    """_run_async detects whether an event loop is already running and
    chooses between asyncio.run() and a ThreadPoolExecutor fallback."""

    def test_no_running_loop_uses_asyncio_run(self):
        async def make_value():
            return 7

        # Called from plain sync code — no running loop — takes the
        # asyncio.run() branch.
        result = _run_async(make_value())
        assert result == 7

    def test_fallback_via_threadpool_inside_active_loop(self):
        async def make_value():
            await asyncio.sleep(0)
            return 42

        async def driver():
            # Inside a running loop: _run_async must spawn a thread that
            # calls asyncio.run on its own loop.
            return _run_async(make_value(), timeout=5)

        result = asyncio.run(driver())
        assert result == 42


def _import_blocker(blocked_prefix):
    """Return a fake __import__ that raises ImportError for blocked_prefix."""
    original_import = builtins.__import__

    def fake_import(name, globals=None, locals=None, fromlist=(), level=0):
        if name == blocked_prefix or name.startswith(f"{blocked_prefix}."):
            raise ImportError(f"simulated: {blocked_prefix} not installed")
        return original_import(name, globals, locals, fromlist, level)

    return fake_import


class TestCrawl4aiImportErrorGuard:
    """When crawl4ai is not installed, _fetch_with_crawl4ai returns None."""

    def test_returns_none_when_crawl4ai_missing(self):
        dl = PlaywrightHTMLDownloader(timeout=5)
        try:
            with patch.object(
                builtins, "__import__", side_effect=_import_blocker("crawl4ai")
            ):
                result = dl._fetch_with_crawl4ai("https://example.com")
            assert result is None
        finally:
            dl.close()


class TestPlaywrightImportErrorGuard:
    """When playwright is not installed, _fetch_with_playwright returns None
    and logs a warning rather than raising."""

    def test_returns_none_when_playwright_missing(self):
        dl = PlaywrightHTMLDownloader(timeout=5)
        try:
            # Mock rate_tracker to avoid needing the full rate-limit setup.
            dl.rate_tracker = MagicMock()
            dl.rate_tracker.apply_rate_limit.return_value = 0
            with patch.object(
                builtins,
                "__import__",
                side_effect=_import_blocker("playwright"),
            ):
                result = dl._fetch_with_playwright("https://example.com")
            assert result is None
        finally:
            dl.close()
