"""REST API routes for Copilot proxy communication."""
from __future__ import annotations

import logging
import traceback
import time
import uuid
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel

from prompt_auto_lab.api.models import (
    CopilotRequest,
    CopilotResponse,
    CopilotModelInfo,
    CopilotMessage,
    CopilotMessageRole,
)
from prompt_auto_lab.api.services.copilot_queue_service import get_copilot_queue

logger = logging.getLogger(__name__)

router = APIRouter(prefix="/api/copilot", tags=["copilot"])
logger = logging.getLogger(__name__)


# OpenAI-compatible request/response models
class OpenAIChatMessage(BaseModel):
    """OpenAI chat message format."""
    role: str
    content: str


class OpenAIChatRequest(BaseModel):
    """OpenAI chat completion request format."""
    model: str
    messages: list[OpenAIChatMessage]
    temperature: float | None = None
    max_tokens: int | None = None


class OpenAIChatChoice(BaseModel):
    """OpenAI chat completion choice."""
    index: int
    message: OpenAIChatMessage
    finish_reason: str


class OpenAIChatUsage(BaseModel):
    """OpenAI token usage."""
    prompt_tokens: int = 0
    completion_tokens: int = 0
    total_tokens: int = 0


class OpenAIChatResponse(BaseModel):
    """OpenAI chat completion response format."""
    id: str
    object: str = "chat.completion"
    created: int
    model: str
    choices: list[OpenAIChatChoice]
    usage: OpenAIChatUsage = OpenAIChatUsage()


class ModelsUpdateRequest(BaseModel):
    """Request body for updating available models."""
    models: list[CopilotModelInfo]


class PendingRequestsResponse(BaseModel):
    """Response containing pending LLM requests."""
    requests: list[CopilotRequest]


class StatusResponse(BaseModel):
    """Copilot proxy status."""
    connected: bool
    models: list[CopilotModelInfo]
    pending_count: int


@router.get("/status", response_model=StatusResponse)
async def get_status() -> StatusResponse:
    """Get Copilot proxy status."""
    queue = get_copilot_queue()
    pending = await queue.get_pending()
    return StatusResponse(
        connected=queue.is_extension_connected(),
        models=queue.get_available_models(),
        pending_count=len(pending),
    )


@router.get("/models", response_model=list[CopilotModelInfo])
async def get_models() -> list[CopilotModelInfo]:
    """Get available Copilot models registered by VS Code extension."""
    queue = get_copilot_queue()
    return queue.get_available_models()


@router.post("/models")
async def update_models(request: ModelsUpdateRequest) -> dict[str, str]:
    """Update available models (called by VS Code extension on connect)."""
    queue = get_copilot_queue()
    await queue.set_available_models(request.models)
    return {"status": "ok", "count": str(len(request.models))}


@router.delete("/models")
async def clear_models() -> dict[str, str]:
    """Clear models (called by VS Code extension on disconnect)."""
    queue = get_copilot_queue()
    await queue.clear_models()
    return {"status": "ok"}


@router.get("/pending", response_model=PendingRequestsResponse)
async def get_pending_requests() -> PendingRequestsResponse:
    """Get pending LLM requests for VS Code extension to process."""
    queue = get_copilot_queue()
    requests = await queue.get_pending()
    try:
        req_ids = [getattr(r, "request_id", None) for r in requests]
    except Exception:
        req_ids = []
    # Helps diagnose "same request processed multiple times" loops.
    logger.info(
        "GET /api/copilot/pending -> %d pending; ids=%s",
        len(requests),
        req_ids[:20],
    )
    # Convert requests to ensure they use the correct Pydantic model type
    # (handles case where requests come from copilot_proxy_client with different class)
    converted = [
        CopilotRequest(**req.model_dump()) if hasattr(req, 'model_dump') else req
        for req in requests
    ]
    return PendingRequestsResponse(requests=converted)


@router.post("/response/{request_id}")
async def submit_response(request_id: str, response: CopilotResponse) -> dict[str, str]:
    """Submit response for a pending request (called by VS Code extension)."""
    queue = get_copilot_queue()
    logger.info(
        "POST /api/copilot/response/%s body.request_id=%s has_error=%s has_tool_calls=%s finish_reason=%s",
        request_id,
        getattr(response, "request_id", None),
        bool(getattr(response, "error", None)),
        bool(getattr(response, "tool_calls", None)),
        getattr(response, "finish_reason", None),
    )

    # Ensure request_id matches
    if response.request_id != request_id:
        raise HTTPException(
            status_code=400,
            detail=f"request_id mismatch: URL={request_id}, body={response.request_id}"
        )

    try:
        found = await queue.submit_response(request_id, response)
    except Exception as exc:
        logger.error(
            "submit_response crashed for request_id=%s: %s\n%s",
            request_id,
            exc,
            traceback.format_exc(),
        )
        raise
    if not found:
        raise HTTPException(status_code=404, detail=f"Request not found: {request_id}")

    return {"status": "ok"}


@router.delete("/pending/{request_id}")
async def cancel_request(request_id: str) -> dict[str, str]:
    """Cancel a pending request."""
    queue = get_copilot_queue()
    found = await queue.cancel_request(request_id)
    if not found:
        raise HTTPException(status_code=404, detail=f"Request not found: {request_id}")
    return {"status": "cancelled"}


# OpenAI-compatible endpoint for external tools (e.g., btql-agent)
@router.post("/v1/chat/completions", response_model=OpenAIChatResponse)
async def openai_chat_completions(request: OpenAIChatRequest) -> OpenAIChatResponse:
    """OpenAI-compatible chat completions endpoint.

    Allows external tools (like btql-agent) to use Copilot via standard OpenAI API.
    The request is queued and processed by the VS Code extension.

    Args:
        request: OpenAI-format chat completion request

    Returns:
        OpenAI-format chat completion response

    Raises:
        HTTPException 503: VS Code extension not connected
        HTTPException 500: Copilot API error
        HTTPException 504: Request timeout
    """
    logger.info(f"[Copilot Proxy] Received OpenAI-compatible request: model={request.model}, messages={len(request.messages)}")
    queue = get_copilot_queue()

    if not queue.is_extension_connected():
        raise HTTPException(
            status_code=503,
            detail="VS Code extension not connected. Please open VS Code with the AutoPromptLab extension."
        )

    # Convert OpenAI messages to CopilotMessage format
    request_id = str(uuid.uuid4())
    copilot_messages = []
    for msg in request.messages:
        role = CopilotMessageRole.USER
        if msg.role == "system":
            role = CopilotMessageRole.SYSTEM
        elif msg.role == "assistant":
            role = CopilotMessageRole.ASSISTANT
        copilot_messages.append(CopilotMessage(role=role, content=msg.content))

    # Create CopilotRequest
    copilot_req = CopilotRequest(
        request_id=request_id,
        messages=copilot_messages,
        model=request.model,
        temperature=request.temperature,
        max_tokens=request.max_tokens,
    )

    # Queue the request
    await queue.queue_request(copilot_req)
    logger.info(f"[Copilot Proxy] Queued request {request_id}, waiting for VS Code extension response...")

    # Wait for response (5 minute timeout for LLM operations)
    try:
        response = await queue.wait_for_response(request_id, timeout=300.0)
    except TimeoutError:
        raise HTTPException(
            status_code=504,
            detail=f"Request timeout after 300 seconds. Request ID: {request_id}"
        )

    # Check for error
    if response.error:
        logger.error(f"[Copilot Proxy] Request {request_id} failed: {response.error}")
        raise HTTPException(status_code=500, detail=f"Copilot API error: {response.error}")

    logger.info(f"[Copilot Proxy] Request {request_id} completed successfully, response length={len(response.content or '')}")

    # Convert to OpenAI response format
    return OpenAIChatResponse(
        id=f"chatcmpl-{request_id}",
        created=int(time.time()),
        model=request.model,
        choices=[
            OpenAIChatChoice(
                index=0,
                message=OpenAIChatMessage(role="assistant", content=response.content or ""),
                finish_reason=response.finish_reason or "stop",
            )
        ],
    )
