"""
Tests for AgentClient HTTP mode behavior.

Tests that the SDK correctly follows 307 redirects for POST requests.
This is a safety net for cases where the gateway returns a redirect
(e.g., FastAPI adding a trailing slash to bare agent paths).
"""

import json
import os
import pytest
from unittest.mock import patch

import httpx

from orchagent.client import AgentClient, DependencyCallError

# Capture the real AsyncClient before any patching
_RealAsyncClient = httpx.AsyncClient


def _make_client_factory(transport):
    """Return a factory that creates real httpx.AsyncClient with injected transport."""

    def factory(**kwargs):
        return _RealAsyncClient(
            transport=transport,
            follow_redirects=kwargs.get("follow_redirects", False),
            timeout=kwargs.get("timeout", 5.0),
        )

    return factory


class TestRedirectFollowing:
    """Test that POST redirects are followed transparently."""

    @pytest.mark.asyncio
    async def test_follows_307_redirect(self):
        """SDK should follow 307 redirects and return the final response."""

        async def handler(request: httpx.Request):
            if request.url.path == "/org/agent/v1/run":
                return httpx.Response(
                    200,
                    json={
                        "data": {"result": "success"},
                        "metadata": {"request_id": "req_123"},
                        "warnings": [],
                    },
                )
            # Simulate a redirect (e.g., some intermediate proxy)
            return httpx.Response(
                307, headers={"Location": "http://test/org/agent/v1/run"}
            )

        transport = httpx.MockTransport(handler)

        with patch.dict(os.environ, {"ORCHAGENT_SERVICE_KEY": "sk_test"}, clear=True):
            client = AgentClient(gateway_url="http://test")

            with patch("orchagent.client.httpx.AsyncClient", _make_client_factory(transport)):
                result = await client.call(
                    "org/agent@v1", {"input": "test"}, endpoint="redirect-me"
                )
                assert result == {"result": "success"}

    @pytest.mark.asyncio
    async def test_307_preserves_post_method_and_body(self):
        """307 redirect must preserve POST method and request body."""
        captured_requests: list[httpx.Request] = []

        async def handler(request: httpx.Request):
            captured_requests.append(request)
            if request.url.path == "/org/agent/v1/run/":
                return httpx.Response(
                    200,
                    json={
                        "data": {"echoed": True},
                        "metadata": {},
                        "warnings": [],
                    },
                )
            # Redirect: add trailing slash
            return httpx.Response(
                307, headers={"Location": str(request.url) + "/"}
            )

        transport = httpx.MockTransport(handler)

        with patch.dict(os.environ, {"ORCHAGENT_SERVICE_KEY": "sk_test"}, clear=True):
            client = AgentClient(gateway_url="http://test")

            with patch("orchagent.client.httpx.AsyncClient", _make_client_factory(transport)):
                result = await client.call("org/agent@v1", {"input": "data"})

        # Two requests: original + redirect
        assert len(captured_requests) == 2
        # Both must be POST (307 preserves method)
        assert captured_requests[0].method == "POST"
        assert captured_requests[1].method == "POST"
        # Body preserved through redirect
        assert json.loads(captured_requests[1].content) == {"input": "data"}

    @pytest.mark.asyncio
    async def test_error_response_after_redirect(self):
        """Errors from the final destination after redirect should propagate correctly."""

        async def handler(request: httpx.Request):
            if request.url.path == "/org/agent/v1/run/":
                return httpx.Response(
                    404,
                    json={"error": "agent_not_found", "detail": "No such agent"},
                )
            return httpx.Response(
                307, headers={"Location": str(request.url) + "/"}
            )

        transport = httpx.MockTransport(handler)

        with patch.dict(os.environ, {"ORCHAGENT_SERVICE_KEY": "sk_test"}, clear=True):
            client = AgentClient(gateway_url="http://test")

            with patch("orchagent.client.httpx.AsyncClient", _make_client_factory(transport)):
                with pytest.raises(DependencyCallError) as exc_info:
                    await client.call("org/agent@v1", {"input": "test"})

                assert exc_info.value.status_code == 404
                assert exc_info.value.response_body["error"] == "agent_not_found"

    @pytest.mark.asyncio
    async def test_no_redirect_normal_flow(self):
        """Normal 200 responses should work as before (no redirect)."""

        async def handler(request: httpx.Request):
            return httpx.Response(
                200,
                json={
                    "data": {"status": "ok"},
                    "metadata": {"request_id": "req_456"},
                    "warnings": [],
                },
            )

        transport = httpx.MockTransport(handler)

        with patch.dict(os.environ, {"ORCHAGENT_SERVICE_KEY": "sk_test"}, clear=True):
            client = AgentClient(gateway_url="http://test")

            with patch("orchagent.client.httpx.AsyncClient", _make_client_factory(transport)):
                result = await client.call("org/agent@v1", {"input": "test"})
                assert result == {"status": "ok"}
