"""
Upstream reverse proxy for D3cipher Gateway.

Pure ASGI app that forwards requests to the configured AI provider
with streaming support and persistent connection pooling.
"""

from typing import List, Tuple

import httpx


class ProxyApp:
    """ASGI reverse proxy to upstream AI provider."""

    def __init__(self, upstream_url: str, timeout: float = 120.0):
        self.upstream_url = upstream_url.rstrip("/")
        self.client = httpx.AsyncClient(
            timeout=httpx.Timeout(timeout, connect=10.0),
            limits=httpx.Limits(max_connections=100, max_keepalive_connections=20),
            follow_redirects=True,
        )

    async def __call__(self, scope, receive, send) -> None:
        if scope["type"] != "http":
            return

        path = scope.get("path", "")
        query_string = scope.get("query_string", b"")
        method = scope.get("method", "GET")

        # Build upstream URL
        url = f"{self.upstream_url}{path}"
        if query_string:
            url = f"{url}?{query_string.decode('latin-1')}"

        # Read request body from receive
        body = await self._read_body(receive)

        # Forward headers (strip hop-by-hop and host)
        headers = self._filter_request_headers(scope.get("headers", []))

        # Build and send upstream request with streaming
        req = self.client.build_request(
            method=method,
            url=url,
            headers=headers,
            content=body,
        )

        try:
            resp = await self.client.send(req, stream=True)
        except httpx.RequestError as e:
            await self._send_error(send, 502, f"Upstream connection error: {type(e).__name__}")
            return

        try:
            # Send response start
            response_headers = self._filter_response_headers(resp.headers)
            await send({
                "type": "http.response.start",
                "status": resp.status_code,
                "headers": response_headers,
            })

            # Stream response body
            async for chunk in resp.aiter_raw():
                await send({
                    "type": "http.response.body",
                    "body": chunk,
                    "more_body": True,
                })

            # Final empty chunk
            await send({
                "type": "http.response.body",
                "body": b"",
                "more_body": False,
            })
        finally:
            await resp.aclose()

    async def _read_body(self, receive) -> bytes:
        """Read full request body from ASGI receive."""
        chunks = []
        while True:
            message = await receive()
            body = message.get("body", b"")
            if body:
                chunks.append(body)
            if not message.get("more_body", False):
                break
        return b"".join(chunks)

    def _filter_request_headers(
        self, raw_headers: List[Tuple[bytes, bytes]]
    ) -> List[Tuple[str, str]]:
        """Filter request headers for upstream forwarding."""
        skip = {b"host", b"content-length", b"transfer-encoding"}
        return [
            (name.decode("latin-1"), value.decode("latin-1"))
            for name, value in raw_headers
            if name.lower() not in skip
        ]

    def _filter_response_headers(
        self, headers: httpx.Headers
    ) -> List[Tuple[bytes, bytes]]:
        """Filter response headers for client delivery."""
        skip = {"transfer-encoding", "content-encoding", "content-length"}
        return [
            (k.encode("latin-1"), v.encode("latin-1"))
            for k, v in headers.multi_items()
            if k.lower() not in skip
        ]

    async def _send_error(self, send, status: int, message: str) -> None:
        """Send an error response."""
        import json
        body = json.dumps({"error": message}).encode("utf-8")
        await send({
            "type": "http.response.start",
            "status": status,
            "headers": [
                (b"content-type", b"application/json"),
                (b"content-length", str(len(body)).encode()),
            ],
        })
        await send({
            "type": "http.response.body",
            "body": body,
        })

    async def close(self) -> None:
        """Close the persistent HTTP client."""
        await self.client.aclose()
