"""
Standardized Response Format

Provides consistent response structures for all API endpoints.
All responses follow a standardized format for error handling and data delivery.
"""

from typing import Any, Dict, List, Optional, TypeVar, Generic
from pydantic import BaseModel, Field
from datetime import datetime, timezone

T = TypeVar('T')


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

class ErrorDetail(BaseModel):
    """Standard error detail structure"""
    error: str = Field(..., description="Error type/code")
    message: str = Field(..., description="Human-readable error message")
    details: Optional[Dict[str, Any]] = Field(None, description="Additional error context")
    timestamp: str = Field(default_factory=lambda: datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"))


class SuccessResponse(BaseModel):
    """Standard success response"""
    success: bool = True
    message: Optional[str] = None
    data: Optional[Any] = None


class PaginatedResponse(BaseModel):
    """Standard paginated response"""
    items: List[Any]
    total: int
    page: int
    page_size: int
    has_more: bool


class ListResponse(BaseModel):
    """Standard list response with metadata"""
    items: List[Any]
    count: int


# ============================================================================
# RESPONSE BUILDERS
# ============================================================================

def success(data: Any = None, message: str = None) -> Dict[str, Any]:
    """Build a success response"""
    response = {"success": True}
    if message:
        response["message"] = message
    if data is not None:
        response["data"] = data
    return response


def error(
    error_type: str,
    message: str,
    details: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
    """Build an error response"""
    return {
        "success": False,
        "error": error_type,
        "message": message,
        "details": details or {},
        "timestamp": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
    }


def paginated(
    items: List[Any],
    total: int,
    page: int,
    page_size: int
) -> Dict[str, Any]:
    """Build a paginated response"""
    return {
        "items": items,
        "total": total,
        "page": page,
        "page_size": page_size,
        "has_more": (page * page_size) < total
    }


def list_response(items: List[Any], key: str = "items") -> Dict[str, Any]:
    """Build a simple list response with custom key"""
    return {
        key: items,
        "count": len(items)
    }


def message_response(message: str) -> Dict[str, str]:
    """Build a simple message response"""
    return {"message": message}


def count_response(message: str, count: int) -> Dict[str, Any]:
    """Build a response with count of affected items"""
    return {"message": message, "count": count}


def id_response(resource_id: int, message: str = "Resource created successfully") -> Dict[str, Any]:
    """Build a response with created resource ID"""
    return {"id": resource_id, "message": message}


def offset_paginated(
    items: List[Any],
    total: int,
    limit: int,
    offset: int,
    key: str = "items",
    include_timestamp: bool = False,
    **extra_fields
) -> Dict[str, Any]:
    """
    Build an offset-based paginated response (common pattern in existing routers).

    This is the standard paginated response format used across all endpoints
    that return lists of items with pagination support.

    Args:
        items: Items for current page
        total: Total count of all items
        limit: Page size (number of items per page)
        offset: Current offset (starting position)
        key: Key name for items list (default "items")
        include_timestamp: Whether to include timestamp field
        **extra_fields: Additional fields to include in response

    Returns:
        Dict with paginated response structure

    Example:
        return offset_paginated(
            items=media_items,
            total=total_count,
            limit=50,
            offset=0,
            key="media",
            stats={"images": 100, "videos": 50}
        )
    """
    response = {
        key: items,
        "total": total,
        "limit": limit,
        "offset": offset
    }

    if include_timestamp:
        response["timestamp"] = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")

    # Add any extra fields
    response.update(extra_fields)

    return response


def batch_operation_response(
    succeeded: bool,
    processed: List[Any] = None,
    errors: List[Dict[str, Any]] = None,
    message: str = None
) -> Dict[str, Any]:
    """
    Build a response for batch operations (batch delete, batch move, etc.).

    This provides a standardized format for operations that affect multiple items.

    Args:
        succeeded: Overall success status
        processed: List of successfully processed items
        errors: List of error details for failed items
        message: Optional message

    Returns:
        Dict with batch operation response structure

    Example:
        return batch_operation_response(
            succeeded=True,
            processed=["/path/to/file1.jpg", "/path/to/file2.jpg"],
            errors=[{"file": "/path/to/file3.jpg", "error": "File not found"}]
        )
    """
    processed = processed or []
    errors = errors or []

    response = {
        "success": succeeded,
        "processed_count": len(processed),
        "error_count": len(errors),
        "timestamp": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
    }

    if processed:
        response["processed"] = processed

    if errors:
        response["errors"] = errors

    if message:
        response["message"] = message

    return response


# ============================================================================
# DATE/TIME UTILITIES
# ============================================================================


def to_iso8601(dt: datetime) -> Optional[str]:
    """
    Convert datetime to ISO 8601 format with UTC timezone.
    This is the standard format for all API responses.

    Example output: "2025-12-04T10:30:00Z"
    """
    if dt is None:
        return None

    # Ensure timezone awareness
    if dt.tzinfo is None:
        dt = dt.replace(tzinfo=timezone.utc)
    else:
        dt = dt.astimezone(timezone.utc)

    return dt.strftime("%Y-%m-%dT%H:%M:%SZ")


def from_iso8601(date_string: str) -> Optional[datetime]:
    """
    Parse ISO 8601 date string to datetime.
    Handles various formats including with and without timezone.
    """
    if not date_string:
        return None

    # Common formats to try
    formats = [
        "%Y-%m-%dT%H:%M:%SZ",      # ISO with Z
        "%Y-%m-%dT%H:%M:%S.%fZ",   # ISO with microseconds and Z
        "%Y-%m-%dT%H:%M:%S",       # ISO without Z
        "%Y-%m-%dT%H:%M:%S.%f",    # ISO with microseconds
        "%Y-%m-%d %H:%M:%S",       # Space separator
        "%Y-%m-%d",                 # Date only
    ]

    for fmt in formats:
        try:
            dt = datetime.strptime(date_string, fmt)
            if dt.tzinfo is None:
                dt = dt.replace(tzinfo=timezone.utc)
            return dt
        except ValueError:
            continue

    # Try parsing with fromisoformat (Python 3.7+)
    try:
        dt = datetime.fromisoformat(date_string.replace('Z', '+00:00'))
        return dt
    except ValueError:
        pass

    return None


def format_timestamp(
    timestamp: Optional[str],
    output_format: str = "iso8601"
) -> Optional[str]:
    """
    Format a timestamp string to the specified format.

    Args:
        timestamp: Input timestamp string
        output_format: "iso8601", "unix", or strftime format string

    Returns:
        Formatted timestamp string
    """
    if not timestamp:
        return None

    dt = from_iso8601(timestamp)
    if not dt:
        return timestamp  # Return original if parsing failed

    if output_format == "iso8601":
        return to_iso8601(dt)
    elif output_format == "unix":
        return str(int(dt.timestamp()))
    else:
        return dt.strftime(output_format)


def now_iso8601() -> str:
    """Get current UTC time in ISO 8601 format"""
    return to_iso8601(datetime.now(timezone.utc))
