"""Instance proxy router for forwarding HTTP requests to running containers."""

import logging
import uuid

import httpx
from fastapi import APIRouter, Depends, HTTPException, Request, Response, status
from sqlalchemy.ext.asyncio import AsyncSession

from src.auth.dependencies import get_current_user_id, get_db_session
from src.models import ToolInstance
from src.models import ToolType

logger = logging.getLogger(__name__)

router = APIRouter(prefix="/instances", tags=["instance-proxy"])


async def _proxy_request(
    request: Request,
    instance_id: uuid.UUID,
    path: str,
    user_id: uuid.UUID,
    session: AsyncSession,
) -> Response:
    """Proxy an HTTP request to a running instance."""
    instance = await session.get(ToolInstance, instance_id)
    if instance is None:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND, detail="instance not found"
        )

    # Verify ownership
    if instance.owner_id != user_id:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="not authorized to access this instance",
        )

    if instance.status != "running" or not instance.container_name:
        raise HTTPException(
            status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
            detail="instance is not running",
        )

    # Get the tool type to find the internal port
    tool_type = await session.get(ToolType, instance.tool_type_id)
    internal_port = tool_type.default_port if tool_type and tool_type.default_port else instance.port

    # Build target URL using internal port
    target_url = f"http://{instance.container_name}:{internal_port}"
    if path:
        target_url += f"/{path}"

    # Get query string
    query_string = str(request.query_params)
    if query_string:
        target_url += f"?{query_string}"

    # Forward headers (excluding host and cookies)
    headers: dict[str, str] = {}
    for key, value in request.headers.items():
        if key.lower() not in ("host", "cookie", "content-length"):
            headers[key] = value

    # Forward the request
    try:
        async with httpx.AsyncClient() as client:
            body = await request.body()
            response = await client.request(
                method=request.method,
                url=target_url,
                headers=headers,
                content=body,
                follow_redirects=False,
                timeout=30.0,
            )
    except Exception as exc:
        logger.error("Proxy error to %s: %s", target_url, exc)
        raise HTTPException(
            status_code=status.HTTP_502_BAD_GATEWAY,
            detail=f"failed to reach instance: {exc}",
        )

    # Build response
    response_headers = dict(response.headers)
    # Remove hop-by-hop headers
    for header in ("content-encoding", "transfer-encoding", "connection"):
        response_headers.pop(header, None)

    return Response(
        content=response.content,
        status_code=response.status_code,
        headers=response_headers,
    )


@router.get("/{instance_id}/proxy/{path:path}")
@router.post("/{instance_id}/proxy/{path:path}", include_in_schema=False)
@router.put("/{instance_id}/proxy/{path:path}", include_in_schema=False)
@router.delete("/{instance_id}/proxy/{path:path}", include_in_schema=False)
@router.patch("/{instance_id}/proxy/{path:path}", include_in_schema=False)
@router.head("/{instance_id}/proxy/{path:path}", include_in_schema=False)
@router.options("/{instance_id}/proxy/{path:path}", include_in_schema=False)
async def proxy_to_instance(
    request: Request,
    instance_id: uuid.UUID,
    path: str = "",
    user_id: uuid.UUID = Depends(get_current_user_id),
    session: AsyncSession = Depends(get_db_session),
) -> Response:
    """Proxy requests to a running tool instance.

    Args:
        request: The incoming HTTP request.
        instance_id: UUID of the instance.
        path: The path to proxy to the instance.
        user_id: ID of the authenticated user.
        session: Database session.

    Returns:
        Response from the proxied instance.
    """
    return await _proxy_request(request, instance_id, path, user_id, session)
