"""Reverse proxy for OpenClaw, rewriting headers for iframe embedding.

OpenClaw is configured with trusted-proxy auth mode, so the proxy sends
an x-forwarded-user header instead of a Bearer token. All connection
parameters come from :mod:`agenteval.config.settings` — no hardcoded hosts.
"""

import asyncio
import json

import httpx
import websockets
from fastapi import APIRouter, Request, WebSocket
from fastapi.responses import StreamingResponse

from agenteval.config import get_settings


def get_http_upstream() -> str:
    return get_settings().openclaw_upstream


def get_ws_upstream() -> str:
    return get_settings().openclaw_ws_upstream


def get_proxy_user() -> str:
    return get_settings().openclaw_proxy_user


def get_auth_token() -> str:
    return get_settings().openclaw_auth_token


_client = httpx.AsyncClient(follow_redirects=True, timeout=60.0)

router = APIRouter()


def _rewrite_headers(headers: httpx.Headers) -> dict[str, str]:
    """Strip iframe-blocking headers and rewrite CSP."""
    result: dict[str, str] = {}
    for key, value in headers.items():
        lower = key.lower()
        if lower in ("x-frame-options", "transfer-encoding", "content-encoding", "content-length"):
            continue
        if lower == "content-security-policy":
            value = value.replace("frame-ancestors 'none'", "frame-ancestors 'self'")
            value = value.replace("script-src 'self'", "script-src 'self' 'unsafe-inline'")
        result[key] = value
    return result


@router.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"])
async def http_proxy(request: Request, path: str):
    url = f"{get_http_upstream()}/{path}"
    if request.query_params:
        url += f"?{request.query_params}"

    body = await request.body()
    headers = dict(request.headers)
    headers.pop("host", None)
    headers.pop("referer", None)
    headers.pop("origin", None)
    headers["x-forwarded-user"] = get_proxy_user()
    headers["authorization"] = f"Bearer {get_auth_token()}"

    resp = await _client.request(
        method=request.method,
        url=url,
        content=body,
        headers=headers,
    )

    return StreamingResponse(
        content=iter([resp.content]),
        status_code=resp.status_code,
        headers=_rewrite_headers(resp.headers),
    )


async def ws_bridge(client_ws: WebSocket, upstream_url: str):
    """Bidirectional WebSocket bridge between client and upstream."""
    await client_ws.accept()

    settings = get_settings()
    auth_token = settings.openclaw_auth_token
    connect_kwargs: dict = {
        "additional_headers": {
            "x-forwarded-user": settings.openclaw_proxy_user,
            "authorization": f"Bearer {auth_token}",
        },
        "max_size": 10 * 1024 * 1024,
    }
    if settings.openclaw_ws_origin:
        connect_kwargs["origin"] = settings.openclaw_ws_origin

    try:
        async with websockets.connect(upstream_url, **connect_kwargs) as upstream_ws:

            async def client_to_upstream():
                try:
                    while True:
                        msg = await client_ws.receive_text()
                        try:
                            payload = json.loads(msg)
                            if (
                                isinstance(payload, dict)
                                and payload.get("method") == "connect.authenticate"
                                and isinstance(payload.get("payload"), dict)
                            ):
                                params = payload["payload"].setdefault("params", {})
                                params.setdefault("auth", {})
                                params["auth"].setdefault("token", auth_token)
                                msg = json.dumps(payload)
                        except (ValueError, TypeError, AttributeError):
                            pass
                        await upstream_ws.send(msg)
                except Exception:
                    pass

            async def upstream_to_client():
                try:
                    async for msg in upstream_ws:
                        if isinstance(msg, str):
                            await client_ws.send_text(msg)
                        else:
                            await client_ws.send_bytes(msg)
                except Exception:
                    pass

            done, pending = await asyncio.wait(
                [asyncio.create_task(client_to_upstream()), asyncio.create_task(upstream_to_client())],
                return_when=asyncio.FIRST_COMPLETED,
            )
            for task in pending:
                task.cancel()
    except Exception:
        pass
    finally:
        try:
            await client_ws.close()
        except Exception:
            pass
