"""Unit tests for RestrictedWebEngineView URL filtering."""

from unittest.mock import MagicMock, patch

from PySide6.QtCore import QUrl
from PySide6.QtWebEngineCore import QWebEngineNavigationRequest

from webdrop_bridge.ui.restricted_web_view import RestrictedWebEngineView


def _create_mock_request(url: str) -> MagicMock:
    """Create properly mocked navigation request.

    Args:
        url: URL string to mock

    Returns:
        Properly mocked QWebEngineNavigationRequest
    """
    request = MagicMock(spec=QWebEngineNavigationRequest)
    request.url = MagicMock(return_value=QUrl(url))
    return request


class TestRestrictedWebEngineView:
    """Test URL whitelist enforcement."""

    def test_no_restrictions_empty_list(self, qtbot):
        """Test that empty allowed_urls means no restrictions."""
        view = RestrictedWebEngineView([])

        # Mock navigation request
        request = _create_mock_request("https://example.com/page")

        # Should not reject any URL
        view._on_navigation_requested(request)
        request.reject.assert_not_called()

    def test_no_restrictions_none(self, qtbot):
        """Test that None allowed_urls means no restrictions."""
        view = RestrictedWebEngineView(None)

        request = _create_mock_request("https://blocked.com/page")

        view._on_navigation_requested(request)
        request.reject.assert_not_called()

    def test_exact_domain_match(self, qtbot):
        """Test exact domain matching."""
        view = RestrictedWebEngineView(["example.com"])

        request = _create_mock_request("https://example.com/page")

        view._on_navigation_requested(request)
        request.reject.assert_not_called()

    def test_exact_domain_mismatch(self, qtbot):
        """Test that mismatched domains are rejected."""
        view = RestrictedWebEngineView(["example.com"])

        request = _create_mock_request("https://other.com/page")

        with patch("webdrop_bridge.ui.restricted_web_view.QDesktopServices"):
            view._on_navigation_requested(request)
            request.reject.assert_called_once()

    def test_wildcard_pattern_match(self, qtbot):
        """Test wildcard pattern matching."""
        view = RestrictedWebEngineView(["*.example.com"])

        request = _create_mock_request("https://sub.example.com/page")

        view._on_navigation_requested(request)
        request.reject.assert_not_called()

    def test_wildcard_pattern_mismatch(self, qtbot):
        """Test that non-matching wildcard patterns are rejected."""
        view = RestrictedWebEngineView(["*.example.com"])

        request = _create_mock_request("https://example.org/page")

        with patch("webdrop_bridge.ui.restricted_web_view.QDesktopServices"):
            view._on_navigation_requested(request)
            request.reject.assert_called_once()

    def test_localhost_allowed(self, qtbot):
        """Test that localhost is allowed."""
        view = RestrictedWebEngineView(["localhost"])

        request = _create_mock_request("http://localhost:8000/page")

        view._on_navigation_requested(request)
        request.reject.assert_not_called()

    def test_file_url_always_allowed(self, qtbot):
        """Test that file:// URLs are always allowed."""
        view = RestrictedWebEngineView(["example.com"])

        request = _create_mock_request("file:///var/www/index.html")

        view._on_navigation_requested(request)
        request.reject.assert_not_called()

    def test_multiple_allowed_urls(self, qtbot):
        """Test multiple allowed URLs."""
        view = RestrictedWebEngineView(["example.com", "test.org"])

        # First allowed URL
        request1 = _create_mock_request("https://example.com/page")
        view._on_navigation_requested(request1)
        request1.reject.assert_not_called()

        # Second allowed URL
        request2 = _create_mock_request("https://test.org/page")
        view._on_navigation_requested(request2)
        request2.reject.assert_not_called()

        # Non-allowed URL
        request3 = _create_mock_request("https://blocked.com/page")
        with patch("webdrop_bridge.ui.restricted_web_view.QDesktopServices"):
            view._on_navigation_requested(request3)
            request3.reject.assert_called_once()

    def test_rejected_url_opens_system_browser(self, qtbot):
        """Test that rejected URLs open in system browser."""
        view = RestrictedWebEngineView(["allowed.com"])

        request = _create_mock_request("https://blocked.com/page")

        with patch("webdrop_bridge.ui.restricted_web_view.QDesktopServices.openUrl") as mock_open:
            view._on_navigation_requested(request)
            request.reject.assert_called_once()
            # Check that openUrl was called with a QUrl
            mock_open.assert_called_once()


class TestURLAllowedLogic:
    """Test _is_url_allowed method directly."""

    def test_is_url_allowed_empty_list(self, qtbot):
        """Test that empty whitelist allows all URLs."""
        view = RestrictedWebEngineView([])

        assert view._is_url_allowed(QUrl("https://anything.com")) is True

    def test_is_url_allowed_file_scheme(self, qtbot):
        """Test that file:// URLs are always allowed."""
        view = RestrictedWebEngineView(["example.com"])

        assert view._is_url_allowed(QUrl("file:///app/index.html")) is True

    def test_is_url_allowed_exact_match(self, qtbot):
        """Test exact domain match."""
        view = RestrictedWebEngineView(["example.com"])

        assert view._is_url_allowed(QUrl("https://example.com/page")) is True
        assert view._is_url_allowed(QUrl("https://other.com/page")) is False

    def test_is_url_allowed_with_port(self, qtbot):
        """Test domain matching with port number."""
        view = RestrictedWebEngineView(["localhost"])

        assert view._is_url_allowed(QUrl("http://localhost:8000/page")) is True

    def test_is_url_allowed_wildcard(self, qtbot):
        """Test wildcard pattern matching."""
        view = RestrictedWebEngineView(["*.example.com", "localhost"])

        # Wildcard *.example.com will match sub.example.com
        assert view._is_url_allowed(QUrl("https://sub.example.com/page")) is True

        # *.example.com will also match example.com (fnmatch behavior)
        assert view._is_url_allowed(QUrl("https://example.com/page")) is True

        # But not other domains
        assert view._is_url_allowed(QUrl("https://other.org/page")) is False

        # localhost should work
        assert view._is_url_allowed(QUrl("http://localhost:3000")) is True
