"""OpenAI-compatible API routes for the routing harness.

This module provides HTTP handlers that follow the OpenAI API specification
for chat completions, enabling drop-in compatibility with existing clients.

Also provides conductor endpoints for model orchestration.
"""

from __future__ import annotations

import json
import time
import uuid
from dataclasses import asdict, dataclass
from typing import TYPE_CHECKING, Any

from aiohttp import web

if TYPE_CHECKING:
    from partner_inference_harness.conductor.interface import ConductorInterface
    from partner_inference_harness.inference.router import Router


@dataclass
class ChatCompletionResponse:
    """OpenAI-compatible chat completion response.

    Note: This harness returns routing decisions only, not actual LLM responses.
    """

    id: str
    object: str
    created: int
    model: str
    choices: list[dict[str, Any]]
    usage: dict[str, int]
    routing_metadata: dict[str, Any]

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary for JSON serialization."""
        return {
            "id": self.id,
            "object": self.object,
            "created": self.created,
            "model": self.model,
            "choices": self.choices,
            "usage": self.usage,
            "routing_metadata": self.routing_metadata,
        }


def create_routes(router: Router) -> web.RouteTableDef:
    """Create route handlers with the given router instance.

    Args:
        router: Router instance for handling inference.

    Returns:
        Route table definition for aiohttp.
    """
    routes = web.RouteTableDef()

    @routes.get("/health")
    async def health_check(request: web.Request) -> web.Response:
        """Health check endpoint."""
        return web.json_response({"status": "healthy"})

    @routes.get("/v1/models")
    async def list_models(request: web.Request) -> web.Response:
        """List available models (OpenAI-compatible)."""
        models = [
            {
                "id": model_id,
                "object": "model",
                "created": 1700000000,
                "owned_by": "partner-harness",
            }
            for model_id in router.model_list
        ]
        return web.json_response({"object": "list", "data": models})

    @routes.post("/v1/chat/completions")
    async def chat_completions(request: web.Request) -> web.Response:
        """Handle chat completion requests (OpenAI-compatible).

        This endpoint accepts the standard OpenAI chat completion format but
        returns routing decisions rather than actual LLM responses. The response
        includes which model would be selected and confidence scores.

        Request body:
            {
                "messages": [{"role": "user", "content": "..."}],
                "model": "router" (optional)
            }

        Response:
            Standard OpenAI chat completion format with routing_metadata
            containing the routing decision details.
        """
        try:
            body = await request.json()
        except json.JSONDecodeError:
            return web.json_response(
                {"error": {"message": "Invalid JSON", "type": "invalid_request_error"}},
                status=400,
            )

        messages = body.get("messages", [])
        if not messages:
            return web.json_response(
                {
                    "error": {
                        "message": "messages is required",
                        "type": "invalid_request_error",
                    }
                },
                status=400,
            )

        # Extract query from the last user message
        query = ""
        for msg in reversed(messages):
            if msg.get("role") == "user":
                content = msg.get("content", "")
                if isinstance(content, str):
                    query = content
                elif isinstance(content, list):
                    # Handle multi-part content
                    query = " ".join(
                        part.get("text", "")
                        for part in content
                        if part.get("type") == "text"
                    )
                break

        if not query:
            return web.json_response(
                {
                    "error": {
                        "message": "No user message content found",
                        "type": "invalid_request_error",
                    }
                },
                status=400,
            )

        # Run routing
        result = router.route(query)

        # Build OpenAI-compatible response
        response = ChatCompletionResponse(
            id=f"chatcmpl-{uuid.uuid4().hex[:24]}",
            object="chat.completion",
            created=int(time.time()),
            model=result.selected_model,
            choices=[
                {
                    "index": 0,
                    "message": {
                        "role": "assistant",
                        "content": f"[Routing decision: {result.selected_model}]",
                    },
                    "finish_reason": "stop",
                }
            ],
            usage={
                "prompt_tokens": len(query.split()),
                "completion_tokens": 0,
                "total_tokens": len(query.split()),
            },
            routing_metadata={
                "selected_model": result.selected_model,
                "confidence": result.confidence,
                "latency_ms": result.latency_ms,
                "model_scores": result.model_scores,
            },
        )

        return web.json_response(response.to_dict())

    @routes.post("/route")
    async def route_query(request: web.Request) -> web.Response:
        """Direct routing endpoint (non-OpenAI format).

        Request body:
            {"query": "...", "queries": ["...", "..."]}

        Response:
            {"results": [{"selected_model": "...", "confidence": 0.XX, ...}]}
        """
        try:
            body = await request.json()
        except json.JSONDecodeError:
            return web.json_response({"error": "Invalid JSON"}, status=400)

        # Handle single query or batch
        queries = body.get("queries", [])
        if not queries and "query" in body:
            queries = [body["query"]]

        if not queries:
            return web.json_response(
                {"error": "query or queries is required"}, status=400
            )

        results = router.route_batch(queries)

        return web.json_response(
            {
                "results": [
                    {
                        "selected_model": r.selected_model,
                        "confidence": r.confidence,
                        "latency_ms": r.latency_ms,
                        "model_scores": r.model_scores,
                    }
                    for r in results
                ]
            }
        )

    @routes.get("/metrics")
    async def metrics(request: web.Request) -> web.Response:
        """Return basic metrics about the router."""
        memory_gb = router.get_memory_usage_gb()
        return web.json_response(
            {
                "memory_usage_gb": memory_gb,
                "model_count": len(router.model_list),
                "device": router.device,
            }
        )

    return routes


def create_conductor_routes(conductor: ConductorInterface) -> web.RouteTableDef:
    """Create conductor route handlers.

    Args:
        conductor: ConductorInterface implementation.

    Returns:
        Route table definition for aiohttp.
    """
    from partner_inference_harness.conductor import ConductorRequest

    routes = web.RouteTableDef()

    @routes.get("/v1/conductor/health")
    async def conductor_health(request: web.Request) -> web.Response:
        """Health check for conductor backend."""
        healthy = conductor.health_check()
        status = 200 if healthy else 503
        return web.json_response(
            {
                "status": "healthy" if healthy else "unhealthy",
                "backend_type": conductor.backend_type,
            },
            status=status,
        )

    @routes.post("/v1/conductor/completions")
    async def conductor_completions(request: web.Request) -> web.Response:
        """Execute completion through configured conductor.

        This endpoint routes requests through the configured conductor
        (RND1 for local inference or Claude for remote API).

        Request body:
            {
                "query": "What is machine learning?",
                "context": "Optional system context",
                "max_tokens": 4096,
                "temperature": 0.7
            }

        Response:
            {
                "text": "Generated response...",
                "model_used": "rnd1:rnd1-base-0910",
                "latency_ms": 1234.5,
                "metadata": {...}
            }
        """
        try:
            body = await request.json()
        except json.JSONDecodeError:
            return web.json_response(
                {"error": {"message": "Invalid JSON", "type": "invalid_request_error"}},
                status=400,
            )

        query = body.get("query")
        if not query:
            return web.json_response(
                {
                    "error": {
                        "message": "query is required",
                        "type": "invalid_request_error",
                    }
                },
                status=400,
            )

        # Build conductor request
        conductor_request = ConductorRequest(
            query=query,
            context=body.get("context"),
            max_tokens=body.get("max_tokens", 4096),
            temperature=body.get("temperature", 0.7),
            metadata=body.get("metadata", {}),
        )

        try:
            response = conductor.complete(conductor_request)
        except Exception as e:
            return web.json_response(
                {
                    "error": {
                        "message": str(e),
                        "type": "conductor_error",
                    }
                },
                status=500,
            )

        return web.json_response(asdict(response))

    return routes
