"""
Pydantic Models for API

All request and response models for API endpoints.
Provides validation and documentation for API contracts.
"""

from typing import Dict, List, Optional, Any
from pydantic import BaseModel, Field, field_validator
from datetime import datetime


# ============================================================================
# AUTHENTICATION MODELS
# ============================================================================

class LoginRequest(BaseModel):
    """Login request model"""
    username: str = Field(..., min_length=1, max_length=50)
    password: str = Field(..., min_length=1)
    rememberMe: bool = False


class LoginResponse(BaseModel):
    """Login response model"""
    success: bool
    token: Optional[str] = None
    username: Optional[str] = None
    role: Optional[str] = None
    expires_at: Optional[str] = None
    message: Optional[str] = None


class ChangePasswordRequest(BaseModel):
    """Password change request"""
    current_password: str = Field(..., min_length=1)
    new_password: str = Field(..., min_length=8)


class UserPreferences(BaseModel):
    """User preferences model"""
    theme: Optional[str] = Field(None, pattern=r'^(light|dark|system)$')
    notifications_enabled: Optional[bool] = None
    default_platform: Optional[str] = None


# ============================================================================
# DOWNLOAD MODELS
# ============================================================================

class DownloadResponse(BaseModel):
    """Single download record"""
    id: int
    platform: str
    source: str
    content_type: Optional[str] = None
    filename: Optional[str] = None
    file_path: Optional[str] = None
    file_size: Optional[int] = None
    download_date: str
    post_date: Optional[str] = None
    status: Optional[str] = None
    width: Optional[int] = None
    height: Optional[int] = None


class StatsResponse(BaseModel):
    """Statistics response"""
    total_downloads: int
    by_platform: Dict[str, int]
    total_size: int
    recent_24h: int
    duplicates_prevented: int
    review_queue_count: int
    recycle_bin_count: int = 0


class TriggerRequest(BaseModel):
    """Manual download trigger request"""
    username: Optional[str] = None
    content_types: Optional[List[str]] = None


# ============================================================================
# PLATFORM MODELS
# ============================================================================

class PlatformStatus(BaseModel):
    """Platform status model"""
    platform: str
    enabled: bool
    last_run: Optional[str] = None
    next_run: Optional[str] = None
    status: str


class PlatformConfig(BaseModel):
    """Platform configuration"""
    enabled: bool = False
    username: Optional[str] = None
    interval_hours: int = Field(24, ge=1, le=168)
    randomize: bool = True
    randomize_minutes: int = Field(30, ge=0, le=180)


# ============================================================================
# CONFIGURATION MODELS
# ============================================================================

class PushoverConfig(BaseModel):
    """Pushover notification configuration"""
    enabled: bool
    user_key: Optional[str] = Field(None, min_length=30, max_length=30, pattern=r'^[A-Za-z0-9]+$')
    api_token: Optional[str] = Field(None, min_length=30, max_length=30, pattern=r'^[A-Za-z0-9]+$')
    priority: int = Field(0, ge=-2, le=2)
    sound: str = Field("pushover", pattern=r'^[a-z_]+$')


class SchedulerConfig(BaseModel):
    """Scheduler configuration"""
    enabled: bool
    interval_hours: int = Field(24, ge=1, le=168)
    randomize: bool = True
    randomize_minutes: int = Field(30, ge=0, le=180)


class RecycleBinConfig(BaseModel):
    """Recycle bin configuration"""
    enabled: bool = True
    path: str = "/opt/immich/recycle"
    retention_days: int = Field(30, ge=1, le=365)
    max_size_gb: int = Field(50, ge=1, le=1000)
    auto_cleanup: bool = True


class ConfigUpdate(BaseModel):
    """Configuration update request"""
    config: Dict[str, Any]

    class Config:
        extra = "allow"


# ============================================================================
# HEALTH MODELS
# ============================================================================

class ServiceHealth(BaseModel):
    """Individual service health status"""
    status: str = Field(..., pattern=r'^(healthy|unhealthy|unknown)$')
    message: Optional[str] = None
    last_check: Optional[str] = None
    details: Optional[Dict[str, Any]] = None


class HealthStatus(BaseModel):
    """Overall health status"""
    status: str
    services: Dict[str, ServiceHealth]
    last_check: str
    version: str


# ============================================================================
# MEDIA MODELS
# ============================================================================

class MediaItem(BaseModel):
    """Media item in gallery"""
    id: int
    file_path: str
    filename: str
    platform: str
    source: str
    content_type: str
    file_size: Optional[int] = None
    width: Optional[int] = None
    height: Optional[int] = None
    post_date: Optional[str] = None
    download_date: Optional[str] = None
    face_match: Optional[str] = None
    face_confidence: Optional[float] = None


class BatchDeleteRequest(BaseModel):
    """Batch delete request"""
    file_paths: List[str] = Field(..., min_length=1)
    permanent: bool = False


class BatchMoveRequest(BaseModel):
    """Batch move request"""
    file_paths: List[str] = Field(..., min_length=1)
    destination: str


# ============================================================================
# REVIEW MODELS
# ============================================================================

class ReviewItem(BaseModel):
    """Review queue item"""
    id: int
    file_path: str
    filename: str
    platform: str
    source: str
    content_type: str
    file_size: Optional[int] = None
    width: Optional[int] = None
    height: Optional[int] = None
    detected_faces: Optional[int] = None
    best_match: Optional[str] = None
    match_confidence: Optional[float] = None


class ReviewKeepRequest(BaseModel):
    """Keep review item request"""
    file_path: str
    destination: str
    new_name: Optional[str] = None


# ============================================================================
# FACE RECOGNITION MODELS
# ============================================================================

class FaceReference(BaseModel):
    """Face reference model"""
    id: str
    name: str
    created_at: str
    encoding_count: int = 1
    thumbnail: Optional[str] = None


class AddReferenceRequest(BaseModel):
    """Add face reference request"""
    file_path: str
    name: str


# ============================================================================
# RECYCLE BIN MODELS
# ============================================================================

class RecycleItem(BaseModel):
    """Recycle bin item"""
    id: str
    original_path: str
    original_filename: str
    recycle_path: str
    file_size: Optional[int] = None
    deleted_at: str
    deleted_from: str
    metadata: Optional[Dict[str, Any]] = None


class RestoreRequest(BaseModel):
    """Restore from recycle bin request"""
    recycle_id: str
    restore_to: Optional[str] = None  # Original path if None


# ============================================================================
# VIDEO DOWNLOAD MODELS
# ============================================================================

class VideoInfoRequest(BaseModel):
    """Video info request"""
    url: str = Field(..., pattern=r'^https?://')


class VideoDownloadRequest(BaseModel):
    """Video download request"""
    url: str = Field(..., pattern=r'^https?://')
    format: Optional[str] = None
    quality: Optional[str] = None


class VideoStatus(BaseModel):
    """Video download status"""
    video_id: str
    platform: str
    status: str
    progress: Optional[float] = None
    title: Optional[str] = None
    error: Optional[str] = None


# ============================================================================
# SCHEDULER MODELS
# ============================================================================

class TaskStatus(BaseModel):
    """Scheduler task status"""
    task_id: str
    platform: str
    source: Optional[str] = None
    status: str
    last_run: Optional[str] = None
    next_run: Optional[str] = None
    error_count: int = 0
    last_error: Optional[str] = None


class SchedulerStatus(BaseModel):
    """Overall scheduler status"""
    running: bool
    tasks: List[TaskStatus]
    current_activity: Optional[Dict[str, Any]] = None


# ============================================================================
# NOTIFICATION MODELS
# ============================================================================

class NotificationItem(BaseModel):
    """Notification item"""
    id: int
    platform: str
    source: str
    content_type: str
    message: str
    title: Optional[str] = None
    sent_at: str
    download_count: int
    status: str


# ============================================================================
# SEMANTIC SEARCH MODELS
# ============================================================================

class SemanticSearchRequest(BaseModel):
    """Semantic search request"""
    query: str = Field(..., min_length=1, max_length=500)
    limit: int = Field(50, ge=1, le=200)
    threshold: float = Field(0.3, ge=0.0, le=1.0)


class SimilarImagesRequest(BaseModel):
    """Find similar images request"""
    file_id: int
    limit: int = Field(20, ge=1, le=100)
    threshold: float = Field(0.5, ge=0.0, le=1.0)


# ============================================================================
# TAG MODELS
# ============================================================================

class TagCreate(BaseModel):
    """Create tag request"""
    name: str = Field(..., min_length=1, max_length=50)
    color: Optional[str] = Field(None, pattern=r'^#[0-9A-Fa-f]{6}$')


class TagUpdate(BaseModel):
    """Update tag request"""
    name: Optional[str] = Field(None, min_length=1, max_length=50)
    color: Optional[str] = Field(None, pattern=r'^#[0-9A-Fa-f]{6}$')


class BulkTagRequest(BaseModel):
    """Bulk tag operation request"""
    file_ids: List[int] = Field(..., min_length=1)
    tag_ids: List[int] = Field(..., min_length=1)
    operation: str = Field(..., pattern=r'^(add|remove)$')


# ============================================================================
# COLLECTION MODELS
# ============================================================================

class CollectionCreate(BaseModel):
    """Create collection request"""
    name: str = Field(..., min_length=1, max_length=100)
    description: Optional[str] = Field(None, max_length=500)


class CollectionUpdate(BaseModel):
    """Update collection request"""
    name: Optional[str] = Field(None, min_length=1, max_length=100)
    description: Optional[str] = Field(None, max_length=500)


class CollectionBulkAdd(BaseModel):
    """Bulk add files to collection"""
    file_ids: List[int] = Field(..., min_length=1)


# ============================================================================
# SMART FOLDER MODELS
# ============================================================================

class SmartFolderCreate(BaseModel):
    """Create smart folder request"""
    name: str = Field(..., min_length=1, max_length=100)
    query_rules: Dict[str, Any]


class SmartFolderUpdate(BaseModel):
    """Update smart folder request"""
    name: Optional[str] = Field(None, min_length=1, max_length=100)
    query_rules: Optional[Dict[str, Any]] = None


# ============================================================================
# SCRAPER MODELS
# ============================================================================

class ScraperUpdate(BaseModel):
    """Update scraper settings"""
    enabled: Optional[bool] = None
    proxy: Optional[str] = None
    interval_hours: Optional[int] = Field(None, ge=1, le=168)
    settings: Optional[Dict[str, Any]] = None


class CookieUpload(BaseModel):
    """Cookie upload for scraper"""
    cookies: str  # JSON string of cookies
    source: str = Field(..., pattern=r'^(browser|manual|extension)$')


# ============================================================================
# STANDARD RESPONSE MODELS
# ============================================================================

class SuccessResponse(BaseModel):
    """Standard success response"""
    success: bool = True
    message: str = "Operation completed successfully"


class DataResponse(BaseModel):
    """Standard data response wrapper"""
    success: bool = True
    data: Any


class MessageResponse(BaseModel):
    """Response with just a message"""
    message: str


class CountResponse(BaseModel):
    """Response with count of affected items"""
    message: str
    count: int


class IdResponse(BaseModel):
    """Response with created resource ID"""
    id: int
    message: str = "Resource created successfully"


class PaginatedResponse(BaseModel):
    """Paginated list response base"""
    items: List[Any]
    total: int
    limit: int
    offset: int
    has_more: bool = False

    @classmethod
    def create(cls, items: List[Any], total: int, limit: int, offset: int):
        """Helper to create paginated response"""
        return cls(
            items=items,
            total=total,
            limit=limit,
            offset=offset,
            has_more=(offset + len(items)) < total
        )
