"""Tests for PlaywrightHTMLDownloader and AutoHTMLDownloader.

Covers: SPA signal detection, inheritance, fallback logic.
Playwright browser tests are not run here (require browser install);
these test the logic around when to use Playwright.
"""

from unittest.mock import Mock, patch


from local_deep_research.research_library.downloaders.playwright_html import (
    AutoHTMLDownloader,
    PlaywrightHTMLDownloader,
    SPA_SIGNALS,
)
from local_deep_research.research_library.downloaders.html import HTMLDownloader


LONG_PARA = (
    "This is a sufficiently long paragraph for content extraction "
    "testing purposes that needs to be over two hundred characters long "
    "to pass the minimum content length threshold used by the auto downloader."
)


class TestPlaywrightHTMLDownloaderInheritance:
    """Verify PlaywrightHTMLDownloader inherits from HTMLDownloader."""

    def test_is_subclass_of_html_downloader(self):
        assert issubclass(PlaywrightHTMLDownloader, HTMLDownloader)

    def test_has_language(self):
        dl = PlaywrightHTMLDownloader(timeout=5)
        assert dl.language == "English"
        dl.close()

    def test_can_handle_http(self):
        dl = PlaywrightHTMLDownloader(timeout=5)
        assert dl.can_handle("https://example.com") is True
        assert dl.can_handle("ftp://example.com") is False
        dl.close()


class TestAutoHTMLDownloaderInheritance:
    """Verify AutoHTMLDownloader inherits from HTMLDownloader."""

    def test_is_subclass_of_html_downloader(self):
        assert issubclass(AutoHTMLDownloader, HTMLDownloader)

    def test_has_language(self):
        dl = AutoHTMLDownloader(timeout=5)
        assert dl.language == "English"
        dl.close()


class TestSPASignalDetection:
    """Tests for SPA/JS framework detection heuristics."""

    def test_detects_react_root(self):
        html = '<html><body><div id="root"></div></body></html>'
        assert AutoHTMLDownloader._has_spa_signals(html) is True

    def test_detects_vue_app(self):
        html = '<html><body><div id="app"></div></body></html>'
        assert AutoHTMLDownloader._has_spa_signals(html) is True

    def test_detects_nextjs(self):
        html = '<html><body><div id="__next"><script>__NEXT_DATA__</script></div></body></html>'
        assert AutoHTMLDownloader._has_spa_signals(html) is True

    def test_detects_noscript_warning(self):
        html = "<html><body><noscript>You need to enable JavaScript to run this app.</noscript></body></html>"
        assert AutoHTMLDownloader._has_spa_signals(html) is True

    def test_no_signals_in_normal_html(self):
        html = (
            f"<html><body><article><p>{LONG_PARA}</p></article></body></html>"
        )
        assert AutoHTMLDownloader._has_spa_signals(html) is False

    def test_no_signals_in_empty_html(self):
        assert AutoHTMLDownloader._has_spa_signals("") is False

    def test_all_known_signals_detected(self):
        for signal in SPA_SIGNALS:
            html = f"<html><body>{signal}</body></html>"
            assert AutoHTMLDownloader._has_spa_signals(html) is True, (
                f"Signal not detected: {signal}"
            )


class TestAutoDownloaderFallbackLogic:
    """Tests for the static-first, Playwright-fallback logic."""

    def test_static_success_skips_playwright(self):
        """When static fetch returns enough content, Playwright is not used."""
        dl = AutoHTMLDownloader(timeout=5, min_content_length=50)
        html = f"<html><head><title>Test</title></head><body><p>{LONG_PARA}</p></body></html>"

        with patch.object(HTMLDownloader, "_fetch_html", return_value=html):
            result = dl.download("https://example.com")

        assert result is not None
        assert len(result) > 50
        # Playwright downloader should not have been initialized
        assert dl._playwright_downloader is None
        dl.close()

    def test_short_content_with_spa_triggers_playwright(self):
        """When content is short and SPA signals present, tries Playwright."""
        dl = AutoHTMLDownloader(timeout=5, min_content_length=200)
        spa_html = '<html><body><div id="root"></div><noscript>You need to enable JavaScript</noscript></body></html>'

        pw_content = f"<html><body><p>{LONG_PARA}</p></body></html>"

        # Mock the session.get to return a SPA challenge page
        mock_response = Mock()
        mock_response.status_code = 403
        mock_response.text = spa_html
        dl.session = Mock()
        dl.session.get.return_value = mock_response

        mock_pw = Mock()
        mock_pw.download.return_value = pw_content.encode("utf-8")
        dl._playwright_downloader = mock_pw

        result = dl.download("https://spa-app.com")

        assert result is not None
        assert LONG_PARA.encode("utf-8") in result
        mock_pw.download.assert_called_once()
        dl.close()


class TestPlaywrightDownloaderClose:
    """Tests for resource cleanup."""

    def test_close_without_browser(self):
        """close() works when browser was never started."""
        dl = PlaywrightHTMLDownloader(timeout=5)
        dl.close()  # Should not raise

    def test_auto_close_without_playwright(self):
        """close() works when Playwright fallback was never used."""
        dl = AutoHTMLDownloader(timeout=5)
        dl.close()  # Should not raise
