"""
Async API Routes

FastAPI endpoints for async invoice processing.
"""

import logging
from pathlib import Path
from typing import Annotated
from uuid import UUID

from fastapi import APIRouter, File, HTTPException, Query, UploadFile

from backend.web.dependencies import (
    ApiKeyDep,
    AsyncDBDep,
    PollRateLimitDep,
    SubmitRateLimitDep,
)
from backend.web.schemas.inference import (
    AsyncRequestItem,
    AsyncRequestsListResponse,
    AsyncResultResponse,
    AsyncStatus,
    AsyncStatusResponse,
    AsyncSubmitResponse,
    DetectionResult,
    InferenceResult,
)
from backend.web.schemas.common import ErrorResponse


def _validate_request_id(request_id: str) -> None:
    """Validate that request_id is a valid UUID format."""
    try:
        UUID(request_id)
    except ValueError:
        raise HTTPException(
            status_code=400,
            detail="Invalid request ID format. Must be a valid UUID.",
        )


logger = logging.getLogger(__name__)

# Global reference to async processing service (set during app startup)
_async_service = None


def set_async_service(service) -> None:
    """Set the async processing service instance."""
    global _async_service
    _async_service = service


def get_async_service():
    """Get the async processing service instance."""
    if _async_service is None:
        raise RuntimeError("AsyncProcessingService not initialized")
    return _async_service


def create_async_router(allowed_extensions: tuple[str, ...]) -> APIRouter:
    """Create async API router."""
    router = APIRouter(prefix="/async", tags=["Async Processing"])

    @router.post(
        "/submit",
        response_model=AsyncSubmitResponse,
        responses={
            400: {"model": ErrorResponse, "description": "Invalid file"},
            401: {"model": ErrorResponse, "description": "Invalid API key"},
            429: {"model": ErrorResponse, "description": "Rate limit exceeded"},
            503: {"model": ErrorResponse, "description": "Queue full"},
        },
        summary="Submit PDF for async processing",
        description="Submit a PDF or image file for asynchronous processing. "
        "Returns a request_id that can be used to poll for results.",
    )
    async def submit_document(
        api_key: SubmitRateLimitDep,
        file: UploadFile = File(..., description="PDF or image file to process"),
    ) -> AsyncSubmitResponse:
        """Submit a document for async processing."""
        # Validate filename
        if not file.filename:
            raise HTTPException(status_code=400, detail="Filename is required")

        # Validate file extension
        file_ext = Path(file.filename).suffix.lower()
        if file_ext not in allowed_extensions:
            raise HTTPException(
                status_code=400,
                detail=f"Unsupported file type: {file_ext}. "
                f"Allowed: {', '.join(allowed_extensions)}",
            )

        # Read file content
        try:
            content = await file.read()
        except Exception as e:
            logger.error(f"Failed to read uploaded file: {e}")
            raise HTTPException(status_code=400, detail="Failed to read file")

        # Check file size (get from config via service)
        service = get_async_service()
        max_size = service._async_config.max_file_size_mb * 1024 * 1024
        if len(content) > max_size:
            raise HTTPException(
                status_code=400,
                detail=f"File too large. Maximum size: "
                f"{service._async_config.max_file_size_mb}MB",
            )

        # Submit request
        result = service.submit_request(
            api_key=api_key,
            file_content=content,
            filename=file.filename,
            content_type=file.content_type or "application/octet-stream",
        )

        if not result.success:
            if "queue" in (result.error or "").lower():
                raise HTTPException(status_code=503, detail=result.error)
            raise HTTPException(status_code=500, detail=result.error)

        return AsyncSubmitResponse(
            status="accepted",
            message="Request submitted for processing",
            request_id=result.request_id,
            estimated_wait_seconds=result.estimated_wait_seconds,
            poll_url=f"/api/v1/async/status/{result.request_id}",
        )

    @router.get(
        "/status/{request_id}",
        response_model=AsyncStatusResponse,
        responses={
            401: {"model": ErrorResponse, "description": "Invalid API key"},
            404: {"model": ErrorResponse, "description": "Request not found"},
            429: {"model": ErrorResponse, "description": "Polling too frequently"},
        },
        summary="Get request status",
        description="Get the current processing status of an async request.",
    )
    async def get_status(
        request_id: str,
        api_key: PollRateLimitDep,
        db: AsyncDBDep,
    ) -> AsyncStatusResponse:
        """Get the status of an async request."""
        # Validate UUID format
        _validate_request_id(request_id)

        # Get request from database (validates API key ownership)
        request = db.get_request_by_api_key(request_id, api_key)

        if request is None:
            raise HTTPException(
                status_code=404,
                detail="Request not found or does not belong to this API key",
            )

        # Get queue position for pending requests
        position = None
        if request.status == "pending":
            position = db.get_queue_position(request_id)

        # Build result URL for completed requests
        result_url = None
        if request.status == "completed":
            result_url = f"/api/v1/async/result/{request_id}"

        return AsyncStatusResponse(
            request_id=str(request.request_id),
            status=AsyncStatus(request.status),
            filename=request.filename,
            created_at=request.created_at,
            started_at=request.started_at,
            completed_at=request.completed_at,
            position_in_queue=position,
            error_message=request.error_message,
            result_url=result_url,
        )

    @router.get(
        "/result/{request_id}",
        response_model=AsyncResultResponse,
        responses={
            401: {"model": ErrorResponse, "description": "Invalid API key"},
            404: {"model": ErrorResponse, "description": "Request not found"},
            409: {"model": ErrorResponse, "description": "Request not completed"},
            429: {"model": ErrorResponse, "description": "Polling too frequently"},
        },
        summary="Get extraction results",
        description="Get the extraction results for a completed async request.",
    )
    async def get_result(
        request_id: str,
        api_key: PollRateLimitDep,
        db: AsyncDBDep,
    ) -> AsyncResultResponse:
        """Get the results of a completed async request."""
        # Validate UUID format
        _validate_request_id(request_id)

        # Get request from database (validates API key ownership)
        request = db.get_request_by_api_key(request_id, api_key)

        if request is None:
            raise HTTPException(
                status_code=404,
                detail="Request not found or does not belong to this API key",
            )

        # Check if completed or failed
        if request.status not in ("completed", "failed"):
            raise HTTPException(
                status_code=409,
                detail=f"Request not yet completed. Current status: {request.status}",
            )

        # Build inference result from stored data
        inference_result = None
        if request.result:
            # Convert detections to DetectionResult objects
            detections = []
            for d in request.result.get("detections", []):
                detections.append(DetectionResult(
                    field=d.get("field", ""),
                    confidence=d.get("confidence", 0.0),
                    bbox=d.get("bbox", [0, 0, 0, 0]),
                ))

            inference_result = InferenceResult(
                document_id=request.result.get("document_id", str(request.request_id)[:8]),
                success=request.result.get("success", False),
                document_type=request.result.get("document_type", "invoice"),
                fields=request.result.get("fields", {}),
                confidence=request.result.get("confidence", {}),
                detections=detections,
                processing_time_ms=request.processing_time_ms or 0.0,
                errors=request.result.get("errors", []),
            )

        # Build visualization URL
        viz_url = None
        if request.visualization_path:
            viz_url = f"/api/v1/results/{request.visualization_path}"

        return AsyncResultResponse(
            request_id=str(request.request_id),
            status=AsyncStatus(request.status),
            processing_time_ms=request.processing_time_ms or 0.0,
            result=inference_result,
            visualization_url=viz_url,
        )

    @router.get(
        "/requests",
        response_model=AsyncRequestsListResponse,
        responses={
            401: {"model": ErrorResponse, "description": "Invalid API key"},
        },
        summary="List requests",
        description="List all async requests for the authenticated API key.",
    )
    async def list_requests(
        api_key: ApiKeyDep,
        db: AsyncDBDep,
        status: Annotated[
            str | None,
            Query(description="Filter by status (pending, processing, completed, failed)"),
        ] = None,
        limit: Annotated[
            int,
            Query(ge=1, le=100, description="Maximum number of results"),
        ] = 20,
        offset: Annotated[
            int,
            Query(ge=0, description="Pagination offset"),
        ] = 0,
    ) -> AsyncRequestsListResponse:
        """List all requests for the authenticated API key."""
        # Validate status filter
        if status and status not in ("pending", "processing", "completed", "failed"):
            raise HTTPException(
                status_code=400,
                detail=f"Invalid status filter: {status}. "
                "Must be one of: pending, processing, completed, failed",
            )

        # Get requests from database
        requests, total = db.get_requests_by_api_key(
            api_key=api_key,
            status=status,
            limit=limit,
            offset=offset,
        )

        # Convert to response items
        items = [
            AsyncRequestItem(
                request_id=str(r.request_id),
                status=AsyncStatus(r.status),
                filename=r.filename,
                file_size=r.file_size,
                created_at=r.created_at,
                completed_at=r.completed_at,
            )
            for r in requests
        ]

        return AsyncRequestsListResponse(
            total=total,
            limit=limit,
            offset=offset,
            requests=items,
        )

    @router.delete(
        "/requests/{request_id}",
        responses={
            401: {"model": ErrorResponse, "description": "Invalid API key"},
            404: {"model": ErrorResponse, "description": "Request not found"},
            409: {"model": ErrorResponse, "description": "Cannot delete processing request"},
        },
        summary="Cancel/delete request",
        description="Cancel a pending request or delete a completed/failed request.",
    )
    async def delete_request(
        request_id: str,
        api_key: ApiKeyDep,
        db: AsyncDBDep,
    ) -> dict:
        """Delete or cancel an async request."""
        # Validate UUID format
        _validate_request_id(request_id)

        # Get request from database
        request = db.get_request_by_api_key(request_id, api_key)

        if request is None:
            raise HTTPException(
                status_code=404,
                detail="Request not found or does not belong to this API key",
            )

        # Cannot delete processing requests
        if request.status == "processing":
            raise HTTPException(
                status_code=409,
                detail="Cannot delete a request that is currently processing",
            )

        # Delete from database (will cascade delete related records)
        conn = db.connect()
        with conn.cursor() as cursor:
            cursor.execute(
                "DELETE FROM async_requests WHERE request_id = %s",
                (request_id,),
            )
        conn.commit()

        return {
            "status": "deleted",
            "request_id": request_id,
            "message": "Request deleted successfully",
        }

    return router
