"""Download queue Pydantic models for the Aniworld web application.

This module defines request/response models used by the download queue API
and the download service. Models are intentionally lightweight and focused
on serialization, validation, and OpenAPI documentation.
"""
from __future__ import annotations

from datetime import datetime, timezone
from enum import Enum
from typing import List, Optional

from pydantic import BaseModel, Field, HttpUrl, field_validator


class DownloadStatus(str, Enum):
    """Status of a download item in the queue."""

    PENDING = "pending"
    DOWNLOADING = "downloading"
    PAUSED = "paused"
    COMPLETED = "completed"
    FAILED = "failed"
    CANCELLED = "cancelled"
    PERMANENTLY_FAILED = "permanently_failed"


class DownloadPriority(str, Enum):
    """Priority level for download queue items."""

    LOW = "LOW"
    NORMAL = "NORMAL"
    HIGH = "HIGH"


class EpisodeIdentifier(BaseModel):
    """Episode identification information for a download item."""

    season: int = Field(..., ge=1, description="Season number (1-based)")
    episode: int = Field(
        ..., ge=1, description="Episode number within season (1-based)"
    )
    title: Optional[str] = Field(None, description="Episode title if known")


class DownloadProgress(BaseModel):
    """Real-time progress information for an active download."""

    percent: float = Field(
        0.0, ge=0.0, le=100.0, description="Download progress percentage"
    )
    downloaded_mb: float = Field(
        0.0, ge=0.0, description="Downloaded size in megabytes"
    )
    total_mb: Optional[float] = Field(
        None, ge=0.0, description="Total size in megabytes if known"
    )
    speed_mbps: Optional[float] = Field(
        None, ge=0.0, description="Download speed in MB/s"
    )
    eta_seconds: Optional[int] = Field(
        None, ge=0, description="Estimated time remaining in seconds"
    )


class DownloadItem(BaseModel):
    """Represents a single download item in the queue.
    
    Note on identifiers:
    - serie_id: The provider-assigned key (e.g., 'attack-on-titan') used for
      all lookups and identification. This is the primary identifier.
    - serie_folder: The filesystem folder name (e.g., 'Attack on Titan (2013)')
      used only for filesystem operations. This is metadata, not an identifier.
    """

    id: str = Field(..., description="Unique download item identifier")
    serie_id: str = Field(
        ...,
        description=(
            "Series key (primary identifier) - provider-assigned URL-safe "
            "key (e.g., 'attack-on-titan'). Used for lookups/identification."
        )
    )
    serie_folder: str = Field(
        ...,
        description=(
            "Series folder name on disk (metadata only) "
            "(e.g., 'Attack on Titan (2013)'). For filesystem ops only."
        )
    )
    serie_name: str = Field(
        ..., min_length=1, description="Series display name"
    )
    episode: EpisodeIdentifier = Field(
        ..., description="Episode identification"
    )
    status: DownloadStatus = Field(
        DownloadStatus.PENDING, description="Current download status"
    )
    priority: DownloadPriority = Field(
        DownloadPriority.NORMAL, description="Queue priority"
    )
    
    # Timestamps
    added_at: datetime = Field(
        default_factory=lambda: datetime.now(timezone.utc),
        description="When item was added to queue",
    )
    started_at: Optional[datetime] = Field(
        None, description="When download started"
    )
    completed_at: Optional[datetime] = Field(
        None, description="When download completed/failed"
    )
    
    # Progress tracking
    progress: Optional[DownloadProgress] = Field(
        None, description="Current progress if downloading"
    )
    
    # Error handling
    error: Optional[str] = Field(None, description="Error message if failed")
    retry_count: int = Field(0, ge=0, description="Number of retry attempts")
    
    # Download source
    source_url: Optional[HttpUrl] = Field(
        None, description="Source URL for download"
    )

    @field_validator('serie_id', mode='before')
    @classmethod
    def normalize_serie_id(cls, v: str) -> str:
        """Normalize serie_id (key) to lowercase and stripped."""
        if isinstance(v, str):
            return v.lower().strip()
        return v


class QueueStatus(BaseModel):
    """Overall status of the download queue system."""

    is_running: bool = Field(
        False, description="Whether the queue processor is running"
    )
    is_paused: bool = Field(False, description="Whether downloads are paused")
    active_downloads: List[DownloadItem] = Field(
        default_factory=list, description="Currently downloading items"
    )
    pending_queue: List[DownloadItem] = Field(
        default_factory=list, description="Items waiting to be downloaded"
    )
    completed_downloads: List[DownloadItem] = Field(
        default_factory=list, description="Recently completed downloads"
    )
    failed_downloads: List[DownloadItem] = Field(
        default_factory=list, description="Failed download items"
    )


class QueueStats(BaseModel):
    """Statistics about the download queue."""

    total_items: int = Field(
        0, ge=0, description="Total number of items in all queues"
    )
    pending_count: int = Field(0, ge=0, description="Number of pending items")
    active_count: int = Field(
        0, ge=0, description="Number of active downloads"
    )
    completed_count: int = Field(
        0, ge=0, description="Number of completed downloads"
    )
    failed_count: int = Field(
        0, ge=0, description="Number of failed downloads"
    )
    
    total_downloaded_mb: float = Field(
        0.0, ge=0.0, description="Total megabytes downloaded"
    )
    average_speed_mbps: Optional[float] = Field(
        None, ge=0.0, description="Average download speed in MB/s"
    )
    estimated_time_remaining: Optional[int] = Field(
        None, ge=0, description="Estimated time to complete queue in seconds"
    )


class DownloadRequest(BaseModel):
    """Request to add episode(s) to the download queue.
    
    Note on identifiers:
    - serie_id: The provider-assigned key (e.g., 'attack-on-titan') used as
      the primary identifier for all operations. This is the unique key.
    - serie_folder: The filesystem folder name (e.g., 'Attack on Titan (2013)')
      used only for storing downloaded files. This is metadata.
    """

    serie_id: str = Field(
        ...,
        description=(
            "Series key (primary identifier) - provider-assigned URL-safe "
            "key (e.g., 'attack-on-titan'). Used for lookups/identification."
        )
    )
    serie_folder: str = Field(
        ...,
        description=(
            "Series folder name on disk (metadata only) "
            "(e.g., 'Attack on Titan (2013)'). For filesystem ops only."
        )
    )
    serie_name: str = Field(
        ..., min_length=1, description="Series display name"
    )
    episodes: List[EpisodeIdentifier] = Field(
        ..., description="List of episodes to download"
    )
    priority: DownloadPriority = Field(
        DownloadPriority.NORMAL, description="Priority level for queue items"
    )
    
    @field_validator('priority', mode='before')
    @classmethod
    def normalize_priority(cls, v):
        """Normalize priority to uppercase for case-insensitive matching."""
        if isinstance(v, str):
            return v.upper()
        return v

    @field_validator('serie_id', mode='before')
    @classmethod
    def normalize_serie_id(cls, v: str) -> str:
        """Normalize serie_id (key) to lowercase and stripped."""
        if isinstance(v, str):
            return v.lower().strip()
        return v


class DownloadResponse(BaseModel):
    """Response after adding items to the download queue."""

    status: str = Field(..., description="Status of the request")
    message: str = Field(..., description="Human-readable status message")
    added_items: List[str] = Field(
        default_factory=list,
        description="IDs of successfully added download items"
    )
    failed_items: List[str] = Field(
        default_factory=list, description="Episodes that failed to be added"
    )


class QueueOperationRequest(BaseModel):
    """Request to perform operations on queue items."""

    item_ids: List[str] = Field(
        ..., description="List of download item IDs"
    )


class QueueReorderRequest(BaseModel):
    """Request to reorder items in the pending queue."""

    item_id: str = Field(..., description="Download item ID to move")
    new_position: int = Field(
        ..., ge=0, description="New position in queue (0-based)"
    )


class QueueStatusResponse(BaseModel):
    """Complete response for queue status endpoint."""

    status: QueueStatus = Field(..., description="Current queue status")
    statistics: QueueStats = Field(..., description="Queue statistics")
