"""Redirect-handling tests for ``safe_get``.

``safe_requests.py`` already follows redirects manually and validates
every hop against the SSRF allowlist, but the previous test suite only
covered the first hop. These tests exercise the redirect loop itself:
private-IP targets, AWS metadata targets, redirect-count caps, and
per-hop validator re-evaluation (earlier approvals do not confer
trust on later redirect targets).
"""

from unittest.mock import MagicMock, patch

import pytest

from local_deep_research.security import safe_requests


def _redirect(location: str, status: int = 302) -> MagicMock:
    r = MagicMock()
    r.status_code = status
    r.headers = {"Location": location}
    r.url = "https://example.com/"
    r.close = MagicMock()
    return r


def _ok() -> MagicMock:
    r = MagicMock()
    r.status_code = 200
    r.headers = {}
    r.url = "https://example.com/final"
    r.content = b""
    r.close = MagicMock()
    return r


def test_redirect_to_private_ip_is_blocked():
    def fake_validate(url, allow_localhost=False, allow_private_ips=False):
        return "127.0.0.1" not in url and "169.254" not in url

    with (
        patch.object(
            safe_requests.ssrf_validator,
            "validate_url",
            side_effect=fake_validate,
        ),
        patch(
            "local_deep_research.security.safe_requests.requests.get",
            return_value=_redirect("http://127.0.0.1/admin"),
        ),
    ):
        with pytest.raises(ValueError, match="SSRF validation"):
            safe_requests.safe_get("https://example.com/")


def test_redirect_to_aws_metadata_is_blocked():
    def fake_validate(url, allow_localhost=False, allow_private_ips=False):
        return "169.254.169.254" not in url

    with (
        patch.object(
            safe_requests.ssrf_validator,
            "validate_url",
            side_effect=fake_validate,
        ),
        patch(
            "local_deep_research.security.safe_requests.requests.get",
            return_value=_redirect("http://169.254.169.254/latest/meta-data/"),
        ),
    ):
        with pytest.raises(ValueError, match="SSRF validation"):
            safe_requests.safe_get("https://example.com/")


def test_too_many_redirects_raises():
    # Always redirect to another hop — should exhaust the 10-hop cap.
    hop = _redirect("https://example.com/loop")
    with (
        patch.object(
            safe_requests.ssrf_validator, "validate_url", return_value=True
        ),
        patch(
            "local_deep_research.security.safe_requests.requests.get",
            return_value=hop,
        ),
    ):
        with pytest.raises(ValueError, match="Too many redirects"):
            safe_requests.safe_get("https://example.com/start")


def test_second_hop_blocked_when_validator_rejects_redirect_target():
    """Validator's verdict on the redirect target trumps its earlier pass.

    The redirect loop in ``safe_get`` re-runs ``validate_url`` on every
    hop. If hop-N passes but hop-(N+1) is rejected, the caller sees a
    ``ValueError`` and no further network activity occurs. This is the
    mechanism the SSRF defence relies on — earlier approvals do not
    confer trust on later targets.

    Note: this is *not* a DNS-rebinding test. Modelling rebinding
    requires mocking at the ``socket.getaddrinfo`` layer so the same
    hostname resolves differently across calls. That coverage belongs
    alongside the validator's unit tests (``tests/security/test_ssrf_validator.py``)
    and is not currently covered there either — tracked as a follow-up.
    """
    results = [True, False]

    def fake_validate(url, allow_localhost=False, allow_private_ips=False):
        return results.pop(0)

    with (
        patch.object(
            safe_requests.ssrf_validator,
            "validate_url",
            side_effect=fake_validate,
        ),
        patch(
            "local_deep_research.security.safe_requests.requests.get",
            return_value=_redirect("https://evil.example.com/"),
        ),
    ):
        with pytest.raises(ValueError, match="SSRF validation"):
            safe_requests.safe_get("https://example.com/")


def test_legitimate_redirect_is_followed():
    hops = [_redirect("https://example.com/final"), _ok()]
    with (
        patch.object(
            safe_requests.ssrf_validator, "validate_url", return_value=True
        ),
        patch(
            "local_deep_research.security.safe_requests.requests.get",
            side_effect=hops,
        ),
    ):
        resp = safe_requests.safe_get("https://example.com/")
    assert resp.status_code == 200
