"""
Behavioral tests for url_validator module.

Tests the URLValidator class which validates URLs for security.
"""

import pytest


class TestIsUnsafeScheme:
    """Tests for detecting unsafe URL schemes."""

    def test_javascript_scheme_is_unsafe(self):
        """javascript: scheme is unsafe."""
        from local_deep_research.security.url_validator import URLValidator

        assert URLValidator.is_unsafe_scheme("javascript:alert(1)") is True

    def test_data_scheme_is_unsafe(self):
        """data: scheme is unsafe."""
        from local_deep_research.security.url_validator import URLValidator

        assert URLValidator.is_unsafe_scheme("data:text/html,<script>") is True

    def test_vbscript_scheme_is_unsafe(self):
        """vbscript: scheme is unsafe."""
        from local_deep_research.security.url_validator import URLValidator

        assert URLValidator.is_unsafe_scheme("vbscript:msgbox") is True

    def test_about_scheme_is_unsafe(self):
        """about: scheme is unsafe."""
        from local_deep_research.security.url_validator import URLValidator

        assert URLValidator.is_unsafe_scheme("about:blank") is True

    def test_blob_scheme_is_unsafe(self):
        """blob: scheme is unsafe."""
        from local_deep_research.security.url_validator import URLValidator

        assert (
            URLValidator.is_unsafe_scheme("blob:http://example.com/id") is True
        )

    def test_file_scheme_is_unsafe(self):
        """file: scheme is unsafe."""
        from local_deep_research.security.url_validator import URLValidator

        assert URLValidator.is_unsafe_scheme("file:///etc/passwd") is True

    def test_http_scheme_is_safe(self):
        """http: scheme is safe."""
        from local_deep_research.security.url_validator import URLValidator

        assert URLValidator.is_unsafe_scheme("http://example.com") is False

    def test_https_scheme_is_safe(self):
        """https: scheme is safe."""
        from local_deep_research.security.url_validator import URLValidator

        assert URLValidator.is_unsafe_scheme("https://example.com") is False

    def test_empty_url_is_safe(self):
        """Empty URL returns False (not unsafe)."""
        from local_deep_research.security.url_validator import URLValidator

        assert URLValidator.is_unsafe_scheme("") is False

    def test_none_is_safe(self):
        """None returns False (not unsafe)."""
        from local_deep_research.security.url_validator import URLValidator

        assert URLValidator.is_unsafe_scheme(None) is False

    def test_case_insensitive_javascript(self):
        """JAVASCRIPT: (uppercase) is unsafe."""
        from local_deep_research.security.url_validator import URLValidator

        assert URLValidator.is_unsafe_scheme("JAVASCRIPT:alert(1)") is True

    def test_case_insensitive_mixed(self):
        """JaVaScRiPt: (mixed case) is unsafe."""
        from local_deep_research.security.url_validator import URLValidator

        assert URLValidator.is_unsafe_scheme("JaVaScRiPt:alert(1)") is True

    def test_whitespace_trimmed(self):
        """Whitespace is trimmed before checking."""
        from local_deep_research.security.url_validator import URLValidator

        assert URLValidator.is_unsafe_scheme("  javascript:alert(1)  ") is True


class TestIsSafeUrl:
    """Tests for URL safety validation."""

    def test_https_url_is_safe(self):
        """Standard HTTPS URL is safe."""
        from local_deep_research.security.url_validator import URLValidator

        assert URLValidator.is_safe_url("https://example.com") is True

    def test_http_url_is_safe(self):
        """Standard HTTP URL is safe."""
        from local_deep_research.security.url_validator import URLValidator

        assert URLValidator.is_safe_url("http://example.com") is True

    def test_ftp_url_is_safe(self):
        """FTP URL is safe."""
        from local_deep_research.security.url_validator import URLValidator

        assert URLValidator.is_safe_url("ftp://files.example.com") is True

    def test_ftps_url_is_safe(self):
        """FTPS URL is safe."""
        from local_deep_research.security.url_validator import URLValidator

        assert (
            URLValidator.is_safe_url("ftps://secure.files.example.com") is True
        )

    def test_javascript_url_is_not_safe(self):
        """JavaScript URL is not safe."""
        from local_deep_research.security.url_validator import URLValidator

        assert URLValidator.is_safe_url("javascript:alert(1)") is False

    def test_data_url_is_not_safe(self):
        """Data URL is not safe."""
        from local_deep_research.security.url_validator import URLValidator

        assert URLValidator.is_safe_url("data:text/html,<script>") is False

    def test_empty_url_is_not_safe(self):
        """Empty URL is not safe."""
        from local_deep_research.security.url_validator import URLValidator

        assert URLValidator.is_safe_url("") is False

    def test_none_is_not_safe(self):
        """None is not safe."""
        from local_deep_research.security.url_validator import URLValidator

        assert URLValidator.is_safe_url(None) is False

    def test_non_string_is_not_safe(self):
        """Non-string is not safe."""
        from local_deep_research.security.url_validator import URLValidator

        assert URLValidator.is_safe_url(123) is False

    def test_fragment_only_allowed_by_default(self):
        """Fragment-only URL is allowed by default."""
        from local_deep_research.security.url_validator import URLValidator

        assert URLValidator.is_safe_url("#section") is True

    def test_fragment_only_can_be_disallowed(self):
        """Fragment-only URL can be disallowed."""
        from local_deep_research.security.url_validator import URLValidator

        assert (
            URLValidator.is_safe_url("#section", allow_fragments=False) is False
        )


class TestIsSafeUrlSchemeRequirement:
    """Tests for scheme requirement option."""

    def test_requires_scheme_by_default(self):
        """Scheme is required by default."""
        from local_deep_research.security.url_validator import URLValidator

        assert (
            URLValidator.is_safe_url("example.com", require_scheme=True)
            is False
        )

    def test_allows_no_scheme_when_disabled(self):
        """No scheme allowed when requirement disabled."""
        from local_deep_research.security.url_validator import URLValidator

        assert (
            URLValidator.is_safe_url("example.com", require_scheme=False)
            is True
        )


class TestIsSafeUrlMailto:
    """Tests for mailto URL handling."""

    def test_mailto_disallowed_by_default(self):
        """Mailto is disallowed by default."""
        from local_deep_research.security.url_validator import URLValidator

        assert URLValidator.is_safe_url("mailto:test@example.com") is False

    def test_mailto_allowed_when_enabled(self):
        """Mailto is allowed when enabled."""
        from local_deep_research.security.url_validator import URLValidator

        assert (
            URLValidator.is_safe_url(
                "mailto:test@example.com", allow_mailto=True
            )
            is True
        )


class TestIsSafeUrlTrustedDomains:
    """Tests for trusted domain validation."""

    def test_trusted_domain_passes(self):
        """URL from trusted domain passes."""
        from local_deep_research.security.url_validator import URLValidator

        assert (
            URLValidator.is_safe_url(
                "https://trusted.com/page", trusted_domains=["trusted.com"]
            )
            is True
        )

    def test_untrusted_domain_fails(self):
        """URL from untrusted domain fails."""
        from local_deep_research.security.url_validator import URLValidator

        assert (
            URLValidator.is_safe_url(
                "https://evil.com/page", trusted_domains=["trusted.com"]
            )
            is False
        )

    def test_subdomain_of_trusted_passes(self):
        """Subdomain of trusted domain passes."""
        from local_deep_research.security.url_validator import URLValidator

        assert (
            URLValidator.is_safe_url(
                "https://sub.trusted.com/page", trusted_domains=["trusted.com"]
            )
            is True
        )

    def test_no_trusted_domains_allows_all(self):
        """No trusted domains list allows all domains."""
        from local_deep_research.security.url_validator import URLValidator

        assert URLValidator.is_safe_url("https://any-domain.com/page") is True


class TestSuspiciousPatterns:
    """Tests for suspicious URL pattern detection."""

    def test_double_encoding_is_suspicious(self):
        """Double URL encoding is suspicious."""
        from local_deep_research.security.url_validator import URLValidator

        # %252F is double-encoded /
        assert URLValidator.is_safe_url("https://example.com/%252F") is False

    def test_null_bytes_are_suspicious(self):
        """Null bytes in URL are suspicious."""
        from local_deep_research.security.url_validator import URLValidator

        assert URLValidator.is_safe_url("https://example.com/%00file") is False

    def test_normal_encoding_is_fine(self):
        """Normal URL encoding is fine."""
        from local_deep_research.security.url_validator import URLValidator

        # %20 is normal space encoding
        assert (
            URLValidator.is_safe_url("https://example.com/hello%20world")
            is True
        )


class TestSanitizeUrl:
    """Tests for URL sanitization."""

    def test_adds_https_by_default(self):
        """Adds https:// scheme by default."""
        from local_deep_research.security.url_validator import URLValidator

        result = URLValidator.sanitize_url("example.com")
        assert result == "https://example.com"

    def test_adds_custom_scheme(self):
        """Adds custom scheme when specified."""
        from local_deep_research.security.url_validator import URLValidator

        result = URLValidator.sanitize_url("example.com", default_scheme="http")
        assert result == "http://example.com"

    def test_preserves_existing_https(self):
        """Preserves existing https:// scheme."""
        from local_deep_research.security.url_validator import URLValidator

        result = URLValidator.sanitize_url("https://example.com")
        assert result == "https://example.com"

    def test_preserves_existing_http(self):
        """Preserves existing http:// scheme."""
        from local_deep_research.security.url_validator import URLValidator

        result = URLValidator.sanitize_url("http://example.com")
        assert result == "http://example.com"

    def test_returns_none_for_unsafe(self):
        """Returns None for unsafe URL."""
        from local_deep_research.security.url_validator import URLValidator

        result = URLValidator.sanitize_url("javascript:alert(1)")
        assert result is None

    def test_returns_none_for_empty(self):
        """Returns None for empty URL."""
        from local_deep_research.security.url_validator import URLValidator

        result = URLValidator.sanitize_url("")
        assert result is None

    def test_strips_whitespace(self):
        """Strips whitespace from URL."""
        from local_deep_research.security.url_validator import URLValidator

        result = URLValidator.sanitize_url("  example.com  ")
        assert result == "https://example.com"


class TestIsAcademicUrl:
    """Tests for academic URL detection."""

    def test_arxiv_is_academic(self):
        """arxiv.org is academic."""
        from local_deep_research.security.url_validator import URLValidator

        assert (
            URLValidator.is_academic_url("https://arxiv.org/abs/1234.5678")
            is True
        )

    def test_pubmed_is_academic(self):
        """pubmed is academic."""
        from local_deep_research.security.url_validator import URLValidator

        assert (
            URLValidator.is_academic_url(
                "https://pubmed.ncbi.nlm.nih.gov/12345678/"
            )
            is True
        )

    def test_doi_org_is_academic(self):
        """doi.org is academic."""
        from local_deep_research.security.url_validator import URLValidator

        assert (
            URLValidator.is_academic_url("https://doi.org/10.1000/test") is True
        )

    def test_nature_is_academic(self):
        """nature.com is academic."""
        from local_deep_research.security.url_validator import URLValidator

        assert (
            URLValidator.is_academic_url("https://www.nature.com/articles/test")
            is True
        )

    def test_science_is_academic(self):
        """science.org is academic."""
        from local_deep_research.security.url_validator import URLValidator

        assert (
            URLValidator.is_academic_url("https://www.science.org/article")
            is True
        )

    def test_ieee_is_academic(self):
        """ieee.org is academic."""
        from local_deep_research.security.url_validator import URLValidator

        assert (
            URLValidator.is_academic_url("https://ieeexplore.ieee.org/document")
            is True
        )

    def test_random_site_not_academic(self):
        """Random site is not academic."""
        from local_deep_research.security.url_validator import URLValidator

        assert URLValidator.is_academic_url("https://example.com") is False

    def test_google_not_academic(self):
        """google.com is not academic."""
        from local_deep_research.security.url_validator import URLValidator

        assert URLValidator.is_academic_url("https://google.com") is False


class TestExtractDoi:
    """Tests for DOI extraction from URLs."""

    def test_extracts_doi_from_doi_org(self):
        """Extracts DOI from doi.org URL."""
        from local_deep_research.security.url_validator import URLValidator

        url = "https://doi.org/10.1000/test.123"
        doi = URLValidator.extract_doi(url)
        assert doi is not None
        assert "10.1000" in doi

    def test_extracts_direct_doi(self):
        """Extracts direct DOI pattern."""
        from local_deep_research.security.url_validator import URLValidator

        url = "10.1000/test.456"
        doi = URLValidator.extract_doi(url)
        assert doi is not None
        assert "10.1000" in doi

    def test_returns_none_for_no_doi(self):
        """Returns None when no DOI found."""
        from local_deep_research.security.url_validator import URLValidator

        url = "https://example.com/no-doi-here"
        doi = URLValidator.extract_doi(url)
        assert doi is None

    def test_extracts_doi_with_special_chars(self):
        """Extracts DOI with special characters."""
        from local_deep_research.security.url_validator import URLValidator

        url = "https://doi.org/10.1234/journal.test-123_456"
        doi = URLValidator.extract_doi(url)
        assert doi is not None


class TestValidateHttpUrl:
    """Tests for HTTP URL validation."""

    def test_valid_https_url(self):
        """Valid HTTPS URL passes."""
        from local_deep_research.security.url_validator import URLValidator

        assert (
            URLValidator.validate_http_url("https://example.com/path") is True
        )

    def test_valid_http_url(self):
        """Valid HTTP URL passes."""
        from local_deep_research.security.url_validator import URLValidator

        assert URLValidator.validate_http_url("http://example.com/path") is True

    def test_rejects_empty_url(self):
        """Empty URL raises error."""
        from local_deep_research.security.url_validator import (
            URLValidator,
            URLValidationError,
        )

        with pytest.raises(URLValidationError):
            URLValidator.validate_http_url("")

    def test_rejects_none_url(self):
        """None URL raises error."""
        from local_deep_research.security.url_validator import (
            URLValidator,
            URLValidationError,
        )

        with pytest.raises(URLValidationError):
            URLValidator.validate_http_url(None)

    def test_rejects_no_scheme(self):
        """URL without scheme raises error."""
        from local_deep_research.security.url_validator import (
            URLValidator,
            URLValidationError,
        )

        with pytest.raises(URLValidationError):
            URLValidator.validate_http_url("example.com")

    def test_rejects_ftp_scheme(self):
        """FTP scheme raises error for HTTP callback."""
        from local_deep_research.security.url_validator import (
            URLValidator,
            URLValidationError,
        )

        with pytest.raises(URLValidationError):
            URLValidator.validate_http_url("ftp://files.example.com")

    def test_rejects_no_hostname(self):
        """URL without hostname raises error."""
        from local_deep_research.security.url_validator import (
            URLValidator,
            URLValidationError,
        )

        with pytest.raises(URLValidationError):
            URLValidator.validate_http_url("https:///path")

    def test_rejects_invalid_hostname_prefix(self):
        """Hostname starting with dot raises error."""
        from local_deep_research.security.url_validator import (
            URLValidator,
            URLValidationError,
        )

        with pytest.raises(URLValidationError):
            URLValidator.validate_http_url("https://.example.com")

    def test_rejects_invalid_hostname_suffix(self):
        """Hostname ending with dot raises error."""
        from local_deep_research.security.url_validator import (
            URLValidator,
            URLValidationError,
        )

        with pytest.raises(URLValidationError):
            URLValidator.validate_http_url("https://example.com.")


class TestIsSafeRedirectUrl:
    """Tests for safe redirect URL validation."""

    def test_relative_url_is_safe(self):
        """Relative URL is safe."""
        from local_deep_research.security.url_validator import URLValidator

        assert (
            URLValidator.is_safe_redirect_url("/path", "http://example.com/")
            is True
        )

    def test_same_host_absolute_is_safe(self):
        """Absolute URL to same host is safe."""
        from local_deep_research.security.url_validator import URLValidator

        assert (
            URLValidator.is_safe_redirect_url(
                "http://example.com/path", "http://example.com/"
            )
            is True
        )

    def test_different_host_is_not_safe(self):
        """Absolute URL to different host is not safe."""
        from local_deep_research.security.url_validator import URLValidator

        assert (
            URLValidator.is_safe_redirect_url(
                "http://evil.com/path", "http://example.com/"
            )
            is False
        )

    def test_empty_target_is_not_safe(self):
        """Empty target URL is not safe."""
        from local_deep_research.security.url_validator import URLValidator

        assert (
            URLValidator.is_safe_redirect_url("", "http://example.com/")
            is False
        )

    def test_crlf_injection_blocked(self):
        """CRLF injection in URL is blocked."""
        from local_deep_research.security.url_validator import URLValidator

        assert (
            URLValidator.is_safe_redirect_url(
                "/path\r\nSet-Cookie: evil", "http://example.com/"
            )
            is False
        )

    def test_newline_injection_blocked(self):
        """Newline injection is blocked."""
        from local_deep_research.security.url_validator import URLValidator

        assert (
            URLValidator.is_safe_redirect_url(
                "/path\nHeader: value", "http://example.com/"
            )
            is False
        )

    def test_carriage_return_blocked(self):
        """Carriage return is blocked."""
        from local_deep_research.security.url_validator import URLValidator

        assert (
            URLValidator.is_safe_redirect_url(
                "/path\rHeader: value", "http://example.com/"
            )
            is False
        )

    def test_path_traversal_blocked(self):
        """Path traversal in redirect URL path is blocked."""
        from local_deep_research.security.url_validator import URLValidator

        assert (
            URLValidator.is_safe_redirect_url(
                "/../../../etc/passwd", "http://example.com/"
            )
            is False
        )

    def test_relative_path_traversal_blocked(self):
        """Relative path traversal is blocked."""
        from local_deep_research.security.url_validator import URLValidator

        assert (
            URLValidator.is_safe_redirect_url("../admin", "http://example.com/")
            is False
        )

    def test_encoded_path_traversal_blocked(self):
        """URL-encoded path traversal (%2e%2e) is blocked."""
        from local_deep_research.security.url_validator import URLValidator

        assert (
            URLValidator.is_safe_redirect_url(
                "%2e%2e/admin", "http://example.com/"
            )
            is False
        )

    def test_mid_path_traversal_blocked(self):
        """Mid-path traversal (/foo/../bar) is blocked."""
        from local_deep_research.security.url_validator import URLValidator

        assert (
            URLValidator.is_safe_redirect_url(
                "/foo/../bar", "http://example.com/"
            )
            is False
        )

    def test_dotdot_in_query_param_not_blocked(self):
        """Double dots in query parameters should NOT trigger path traversal blocking."""
        from local_deep_research.security.url_validator import URLValidator

        assert (
            URLValidator.is_safe_redirect_url(
                "/search?q=foo..bar", "http://example.com/"
            )
            is True
        )


class TestURLValidationErrorClass:
    """Tests for URLValidationError exception."""

    def test_is_value_error_subclass(self):
        """URLValidationError is ValueError subclass."""
        from local_deep_research.security.url_validator import (
            URLValidationError,
        )

        assert issubclass(URLValidationError, ValueError)

    def test_can_be_raised(self):
        """URLValidationError can be raised."""
        from local_deep_research.security.url_validator import (
            URLValidationError,
        )

        with pytest.raises(URLValidationError) as excinfo:
            raise URLValidationError("test error")
        assert "test error" in str(excinfo.value)


class TestTrustedAcademicDomains:
    """Tests for trusted academic domains constant."""

    def test_contains_arxiv(self):
        """Trusted domains include arxiv.org."""
        from local_deep_research.security.url_validator import URLValidator

        assert "arxiv.org" in URLValidator.TRUSTED_ACADEMIC_DOMAINS

    def test_contains_pubmed(self):
        """Trusted domains include pubmed.ncbi.nlm.nih.gov."""
        from local_deep_research.security.url_validator import URLValidator

        assert (
            "pubmed.ncbi.nlm.nih.gov" in URLValidator.TRUSTED_ACADEMIC_DOMAINS
        )

    def test_contains_doi(self):
        """Trusted domains include doi.org."""
        from local_deep_research.security.url_validator import URLValidator

        assert "doi.org" in URLValidator.TRUSTED_ACADEMIC_DOMAINS

    def test_is_tuple(self):
        """Trusted domains is a tuple."""
        from local_deep_research.security.url_validator import URLValidator

        assert isinstance(URLValidator.TRUSTED_ACADEMIC_DOMAINS, tuple)


class TestUnsafeSchemes:
    """Tests for unsafe schemes constant."""

    def test_contains_javascript(self):
        """Unsafe schemes include javascript."""
        from local_deep_research.security.url_validator import URLValidator

        assert "javascript" in URLValidator.UNSAFE_SCHEMES

    def test_contains_data(self):
        """Unsafe schemes include data."""
        from local_deep_research.security.url_validator import URLValidator

        assert "data" in URLValidator.UNSAFE_SCHEMES

    def test_contains_file(self):
        """Unsafe schemes include file."""
        from local_deep_research.security.url_validator import URLValidator

        assert "file" in URLValidator.UNSAFE_SCHEMES

    def test_is_tuple(self):
        """Unsafe schemes is a tuple."""
        from local_deep_research.security.url_validator import URLValidator

        assert isinstance(URLValidator.UNSAFE_SCHEMES, tuple)


class TestSafeSchemes:
    """Tests for safe schemes constant."""

    def test_contains_http(self):
        """Safe schemes include http."""
        from local_deep_research.security.url_validator import URLValidator

        assert "http" in URLValidator.SAFE_SCHEMES

    def test_contains_https(self):
        """Safe schemes include https."""
        from local_deep_research.security.url_validator import URLValidator

        assert "https" in URLValidator.SAFE_SCHEMES

    def test_contains_ftp(self):
        """Safe schemes include ftp."""
        from local_deep_research.security.url_validator import URLValidator

        assert "ftp" in URLValidator.SAFE_SCHEMES

    def test_is_tuple(self):
        """Safe schemes is a tuple."""
        from local_deep_research.security.url_validator import URLValidator

        assert isinstance(URLValidator.SAFE_SCHEMES, tuple)
