"""Download queue API endpoints for Aniworld web application.

This module provides REST API endpoints for managing the anime download queue,
including adding episodes, removing items, controlling queue processing, and
retrieving queue status and statistics.
"""
from fastapi import APIRouter, Depends, Path, status
from fastapi.responses import JSONResponse

from src.server.exceptions import BadRequestError, NotFoundError, ServerError
from src.server.models.download import (
    DownloadRequest,
    QueueOperationRequest,
    QueueStatusResponse,
)
from src.server.services.download_service import DownloadService, DownloadServiceError
from src.server.utils.dependencies import get_download_service, require_auth

router = APIRouter(prefix="/api/queue", tags=["download"])


@router.get("/status", response_model=QueueStatusResponse)
async def get_queue_status(
    _: dict = Depends(require_auth),
    download_service: DownloadService = Depends(get_download_service),
):
    """Get current download queue status and statistics.

    Returns comprehensive information about all queue items including:
    - Active downloads with progress
    - Pending items waiting to be processed
    - Recently completed downloads
    - Failed downloads

    Requires authentication.

    Returns:
        QueueStatusResponse: Complete queue status and statistics

    Raises:
        HTTPException: 401 if not authenticated, 500 on service error
    """
    try:
        queue_status = await download_service.get_queue_status()
        queue_stats = await download_service.get_queue_stats()

        # Build response matching QueueStatusResponse model
        response = QueueStatusResponse(
            status=queue_status,
            statistics=queue_stats,
        )
        
        return response

    except Exception as e:
        raise ServerError(
            message=f"Failed to retrieve queue status: {str(e)}"
        )


@router.post("/add", status_code=status.HTTP_201_CREATED)
async def add_to_queue(
    request: DownloadRequest,
    _: dict = Depends(require_auth),
    download_service: DownloadService = Depends(get_download_service),
):
    """Add episodes to the download queue.

    Adds one or more episodes to the download queue with specified priority.
    Episodes are validated and queued for processing based on priority level:
    - HIGH priority items are processed first
    - NORMAL and LOW priority items follow FIFO order

    Requires authentication.

    Args:
        request: Download request containing:
            - serie_id: Series key (primary identifier, 'attack-on-titan')
            - serie_folder: Filesystem folder name for storing downloads
            - serie_name: Display name for the series
            - episodes: List of episodes to download
            - priority: Queue priority level

    Returns:
        DownloadResponse: Status and list of created download item IDs

    Raises:
        HTTPException: 401 if not authenticated, 400 for invalid request,
                      500 on service error
    """
    try:
        # Validate request
        if not request.episodes:
            raise BadRequestError(
                message="At least one episode must be specified"
            )

        # Add to queue
        added_ids = await download_service.add_to_queue(
            serie_id=request.serie_id,
            serie_folder=request.serie_folder,
            serie_name=request.serie_name,
            episodes=request.episodes,
            priority=request.priority,
        )

        # Keep a backwards-compatible response shape and return it as a
        # raw JSONResponse so FastAPI won't coerce it based on any
        # response_model defined elsewhere.
        payload = {
            "status": "success",
            "message": f"Added {len(added_ids)} episode(s) to download queue",
            "added_items": added_ids,
            "item_ids": added_ids,
            "failed_items": [],
        }

        return JSONResponse(
            content=payload,
            status_code=status.HTTP_201_CREATED,
        )

    except DownloadServiceError as e:
        raise BadRequestError(message=str(e))
    except (BadRequestError, NotFoundError, ServerError):
        raise
    except Exception as e:
        raise ServerError(
            message=f"Failed to add episodes to queue: {str(e)}"
        )


@router.delete("/completed", status_code=status.HTTP_200_OK)
async def clear_completed(
    _: dict = Depends(require_auth),
    download_service: DownloadService = Depends(get_download_service),
):
    """Clear completed downloads from history.

    Removes all completed download items from the queue history. This helps
    keep the queue display clean and manageable.

    Requires authentication.

    Returns:
        dict: Status message with count of cleared items

    Raises:
        HTTPException: 401 if not authenticated, 500 on service error
    """
    try:
        cleared_count = await download_service.clear_completed()

        return {
            "status": "success",
            "message": f"Cleared {cleared_count} completed item(s)",
            "count": cleared_count,
        }

    except Exception as e:
        raise ServerError(
            message=f"Failed to clear completed items: {str(e)}"
        )


@router.delete("/failed", status_code=status.HTTP_200_OK)
async def clear_failed(
    _: dict = Depends(require_auth),
    download_service: DownloadService = Depends(get_download_service),
):
    """Clear failed downloads from history.

    Removes all failed download items from the queue history. This helps
    keep the queue display clean and manageable.

    Requires authentication.

    Returns:
        dict: Status message with count of cleared items

    Raises:
        HTTPException: 401 if not authenticated, 500 on service error
    """
    try:
        cleared_count = await download_service.clear_failed()

        return {
            "status": "success",
            "message": f"Cleared {cleared_count} failed item(s)",
            "count": cleared_count,
        }

    except Exception as e:
        raise ServerError(
            message=f"Failed to clear failed items: {str(e)}"
        )


@router.delete("/pending", status_code=status.HTTP_200_OK)
async def clear_pending(
    _: dict = Depends(require_auth),
    download_service: DownloadService = Depends(get_download_service),
):
    """Clear all pending downloads from the queue.

    Removes all pending download items from the queue. This is useful for
    clearing the entire queue at once instead of removing items one by one.

    Requires authentication.

    Returns:
        dict: Status message with count of cleared items

    Raises:
        HTTPException: 401 if not authenticated, 500 on service error
    """
    try:
        cleared_count = await download_service.clear_pending()

        return {
            "status": "success",
            "message": f"Removed {cleared_count} pending item(s)",
            "count": cleared_count,
        }

    except Exception as e:
        raise ServerError(
            message=f"Failed to clear pending items: {str(e)}"
        )


@router.delete("/{item_id}", status_code=status.HTTP_204_NO_CONTENT)
async def remove_from_queue(
    item_id: str = Path(..., description="Download item ID to remove"),
    _: dict = Depends(require_auth),
    download_service: DownloadService = Depends(get_download_service),
):
    """Remove a specific item from the download queue.

    Removes a download item from the queue. If the item is currently
    downloading, it will be cancelled and marked as cancelled. If it's
    pending, it will simply be removed from the queue.

    Requires authentication.

    Args:
        item_id: Unique identifier of the download item to remove

    Raises:
        HTTPException: 401 if not authenticated, 404 if item not found,
                      500 on service error
    """
    try:
        removed_ids = await download_service.remove_from_queue([item_id])

        if not removed_ids:
            raise NotFoundError(
                message=f"Download item {item_id} not found in queue",
                resource_type="download_item",
                resource_id=item_id
            )

    except DownloadServiceError as e:
        raise BadRequestError(message=str(e))
    except (BadRequestError, NotFoundError, ServerError):
        raise
    except Exception as e:
        raise ServerError(
            message=f"Failed to remove item from queue: {str(e)}"
        )


@router.delete("/", status_code=status.HTTP_204_NO_CONTENT)
async def remove_multiple_from_queue(
    request: QueueOperationRequest,
    _: dict = Depends(require_auth),
    download_service: DownloadService = Depends(get_download_service),
):
    """Remove multiple items from the download queue.

    Removes multiple download items from the queue based on provided IDs.
    Items that are currently downloading will be cancelled.

    Requires authentication.

    Args:
        request: Request containing list of item IDs to remove

    Raises:
        HTTPException: 401 if not authenticated, 404 if no items found,
                      500 on service error
    """
    try:
        removed_ids = await download_service.remove_from_queue(
            request.item_ids
        )

        if not removed_ids:
            raise NotFoundError(
                message="No matching items found in queue",
                resource_type="download_items"
            )

    except DownloadServiceError as e:
        raise BadRequestError(message=str(e))
    except (BadRequestError, NotFoundError, ServerError):
        raise
    except Exception as e:
        raise ServerError(
            message=f"Failed to remove items from queue: {str(e)}"
        )


@router.post("/start", status_code=status.HTTP_200_OK)
async def start_queue(
    _: dict = Depends(require_auth),
    download_service: DownloadService = Depends(get_download_service),
):
    """Start automatic queue processing.

    Starts processing all pending downloads sequentially, one at a time.
    The queue will continue processing until all items are complete or
    the queue is manually stopped. Processing continues even if the browser
    is closed.

    Only one download can be active at a time. If a download is already
    active or queue processing is running, an error is returned.

    Requires authentication.

    Returns:
        dict: Status message confirming queue processing started

    Raises:
        HTTPException: 401 if not authenticated, 400 if queue is empty or
                      processing already active, 500 on service error
    """
    try:
        result = await download_service.start_queue_processing()
        
        if result is None:
            raise BadRequestError(
                message="No pending downloads in queue"
            )

        return {
            "status": "success",
            "message": "Queue processing started",
        }
    
    except DownloadServiceError as e:
        raise BadRequestError(message=str(e))
    except (BadRequestError, NotFoundError, ServerError):
        raise
    except Exception as e:
        raise ServerError(
            message=f"Failed to start queue processing: {str(e)}"
        )


@router.post("/stop", status_code=status.HTTP_200_OK)
async def stop_queue(
    _: dict = Depends(require_auth),
    download_service: DownloadService = Depends(get_download_service),
):
    """Stop processing new downloads from queue.

    Prevents new downloads from starting. The current active download will
    continue to completion, but no new downloads will be started from the
    pending queue.

    Requires authentication.

    Returns:
        dict: Status message indicating queue processing has been stopped

    Raises:
        HTTPException: 401 if not authenticated, 500 on service error
    """
    try:
        await download_service.stop_downloads()

        return {
            "status": "success",
            "message": (
                "Queue processing stopped (current download will continue)"
            ),
        }

    except Exception as e:
        raise ServerError(
            message=f"Failed to stop queue processing: {str(e)}"
        )


@router.post("/pause", status_code=status.HTTP_200_OK)
async def pause_queue(
    _: dict = Depends(require_auth),
    download_service: DownloadService = Depends(get_download_service),
):
    """Pause queue processing (alias for stop).

    Prevents new downloads from starting. The current active download will
    continue to completion, but no new downloads will be started from the
    pending queue.

    Requires authentication.

    Returns:
        dict: Status message indicating queue processing has been paused

    Raises:
        HTTPException: 401 if not authenticated, 500 on service error
    """
    try:
        await download_service.stop_downloads()

        return {
            "status": "success",
            "message": "Queue processing paused",
        }

    except Exception as e:
        raise ServerError(
            message=f"Failed to pause queue processing: {str(e)}"
        )


@router.post("/reorder", status_code=status.HTTP_200_OK)
async def reorder_queue(
    request: QueueOperationRequest,
    _: dict = Depends(require_auth),
    download_service: DownloadService = Depends(get_download_service),
):
    """Reorder items in the pending queue.

    Reorders the pending queue based on the provided list of item IDs.
    Items will be placed in the order specified by the item_ids list.
    Items not included in the list will remain at the end of the queue.

    Requires authentication.

    Args:
        request: List of download item IDs in desired order

    Returns:
        dict: Status message

    Raises:
        HTTPException: 401 if not authenticated, 404 if no items match,
                      500 on service error
    """
    try:
        await download_service.reorder_queue(request.item_ids)
        return {
            "status": "success",
            "message": f"Queue reordered with {len(request.item_ids)} items",
        }

    except Exception as e:
        raise ServerError(
            message=f"Failed to reorder queue: {str(e)}"
        )


@router.post("/retry", status_code=status.HTTP_200_OK)
async def retry_failed(
    request: QueueOperationRequest,
    _: dict = Depends(require_auth),
    download_service: DownloadService = Depends(get_download_service),
):
    """Retry failed downloads.

    Moves failed download items back to the pending queue for retry. Only items
    that haven't exceeded the maximum retry count will be retried.

    Requires authentication.

    Args:
        request: List of download item IDs to retry (empty list retries all)

    Returns:
        dict: Status message with count of retried items

    Raises:
        HTTPException: 401 if not authenticated, 500 on service error
    """
    try:
        # If no specific IDs provided, retry all failed items
        item_ids = request.item_ids if request.item_ids else None

        retried_ids = await download_service.retry_failed(item_ids)

        return {
            "status": "success",
            "message": f"Retrying {len(retried_ids)} failed item(s)",
            "retried_count": len(retried_ids),
            "retried_ids": retried_ids,
        }

    except Exception as e:
        raise ServerError(
            message=f"Failed to retry downloads: {str(e)}"
        )
