"""Playlist management API endpoints.""" from typing import Annotated from fastapi import APIRouter, Depends from pydantic import BaseModel from sqlmodel.ext.asyncio.session import AsyncSession from app.core.database import get_db from app.core.dependencies import get_current_active_user_flexible from app.models.playlist import Playlist from app.models.sound import Sound from app.models.user import User from app.services.playlist import PlaylistService router = APIRouter(prefix="/playlists", tags=["playlists"]) class PlaylistCreateRequest(BaseModel): """Request model for creating a playlist.""" name: str description: str | None = None genre: str | None = None class PlaylistUpdateRequest(BaseModel): """Request model for updating a playlist.""" name: str | None = None description: str | None = None genre: str | None = None is_current: bool | None = None class PlaylistResponse(BaseModel): """Response model for playlist data.""" id: int name: str description: str | None genre: str | None is_main: bool is_current: bool is_deletable: bool created_at: str updated_at: str | None @classmethod def from_playlist(cls, playlist: Playlist) -> "PlaylistResponse": """Create response from playlist model.""" return cls( id=playlist.id, name=playlist.name, description=playlist.description, genre=playlist.genre, is_main=playlist.is_main, is_current=playlist.is_current, is_deletable=playlist.is_deletable, created_at=playlist.created_at.isoformat(), updated_at=playlist.updated_at.isoformat() if playlist.updated_at else None, ) class SoundResponse(BaseModel): """Response model for sound data in playlists.""" id: int name: str filename: str type: str duration: int | None size: int | None play_count: int created_at: str @classmethod def from_sound(cls, sound: Sound) -> "SoundResponse": """Create response from sound model.""" return cls( id=sound.id, name=sound.name, filename=sound.filename, type=sound.type, duration=sound.duration, size=sound.size, play_count=sound.play_count, created_at=sound.created_at.isoformat(), ) class AddSoundRequest(BaseModel): """Request model for adding a sound to a playlist.""" sound_id: int position: int | None = None class ReorderRequest(BaseModel): """Request model for reordering sounds in a playlist.""" sound_positions: list[tuple[int, int]] class PlaylistStatsResponse(BaseModel): """Response model for playlist statistics.""" sound_count: int total_duration_ms: int total_play_count: int async def get_playlist_service( session: Annotated[AsyncSession, Depends(get_db)], ) -> PlaylistService: """Get the playlist service.""" return PlaylistService(session) @router.get("/") async def get_all_playlists( current_user: Annotated[User, Depends(get_current_active_user_flexible)], playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], ) -> list[PlaylistResponse]: """Get all playlists from all users.""" playlists = await playlist_service.get_all_playlists() return [PlaylistResponse.from_playlist(playlist) for playlist in playlists] @router.get("/user") async def get_user_playlists( current_user: Annotated[User, Depends(get_current_active_user_flexible)], playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], ) -> list[PlaylistResponse]: """Get playlists for the current user only.""" playlists = await playlist_service.get_user_playlists(current_user.id) return [PlaylistResponse.from_playlist(playlist) for playlist in playlists] @router.get("/main") async def get_main_playlist( current_user: Annotated[User, Depends(get_current_active_user_flexible)], playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], ) -> PlaylistResponse: """Get the global main playlist.""" playlist = await playlist_service.get_main_playlist() return PlaylistResponse.from_playlist(playlist) @router.get("/current") async def get_current_playlist( current_user: Annotated[User, Depends(get_current_active_user_flexible)], playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], ) -> PlaylistResponse: """Get the user's current playlist (falls back to main playlist).""" playlist = await playlist_service.get_current_playlist(current_user.id) return PlaylistResponse.from_playlist(playlist) @router.post("/") async def create_playlist( request: PlaylistCreateRequest, current_user: Annotated[User, Depends(get_current_active_user_flexible)], playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], ) -> PlaylistResponse: """Create a new playlist.""" playlist = await playlist_service.create_playlist( user_id=current_user.id, name=request.name, description=request.description, genre=request.genre, ) return PlaylistResponse.from_playlist(playlist) @router.get("/{playlist_id}") async def get_playlist( playlist_id: int, current_user: Annotated[User, Depends(get_current_active_user_flexible)], playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], ) -> PlaylistResponse: """Get a specific playlist.""" playlist = await playlist_service.get_playlist_by_id(playlist_id) return PlaylistResponse.from_playlist(playlist) @router.put("/{playlist_id}") async def update_playlist( playlist_id: int, request: PlaylistUpdateRequest, current_user: Annotated[User, Depends(get_current_active_user_flexible)], playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], ) -> PlaylistResponse: """Update a playlist.""" playlist = await playlist_service.update_playlist( playlist_id=playlist_id, user_id=current_user.id, name=request.name, description=request.description, genre=request.genre, is_current=request.is_current, ) return PlaylistResponse.from_playlist(playlist) @router.delete("/{playlist_id}") async def delete_playlist( playlist_id: int, current_user: Annotated[User, Depends(get_current_active_user_flexible)], playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], ) -> dict[str, str]: """Delete a playlist.""" await playlist_service.delete_playlist(playlist_id, current_user.id) return {"message": "Playlist deleted successfully"} @router.get("/search/{query}") async def search_playlists( query: str, current_user: Annotated[User, Depends(get_current_active_user_flexible)], playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], ) -> list[PlaylistResponse]: """Search all playlists by name.""" playlists = await playlist_service.search_all_playlists(query) return [PlaylistResponse.from_playlist(playlist) for playlist in playlists] @router.get("/user/search/{query}") async def search_user_playlists( query: str, current_user: Annotated[User, Depends(get_current_active_user_flexible)], playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], ) -> list[PlaylistResponse]: """Search current user's playlists by name.""" playlists = await playlist_service.search_playlists(query, current_user.id) return [PlaylistResponse.from_playlist(playlist) for playlist in playlists] @router.get("/{playlist_id}/sounds") async def get_playlist_sounds( playlist_id: int, current_user: Annotated[User, Depends(get_current_active_user_flexible)], playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], ) -> list[SoundResponse]: """Get all sounds in a playlist.""" sounds = await playlist_service.get_playlist_sounds(playlist_id) return [SoundResponse.from_sound(sound) for sound in sounds] @router.post("/{playlist_id}/sounds") async def add_sound_to_playlist( playlist_id: int, request: AddSoundRequest, current_user: Annotated[User, Depends(get_current_active_user_flexible)], playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], ) -> dict[str, str]: """Add a sound to a playlist.""" await playlist_service.add_sound_to_playlist( playlist_id=playlist_id, sound_id=request.sound_id, user_id=current_user.id, position=request.position, ) return {"message": "Sound added to playlist successfully"} @router.delete("/{playlist_id}/sounds/{sound_id}") async def remove_sound_from_playlist( playlist_id: int, sound_id: int, current_user: Annotated[User, Depends(get_current_active_user_flexible)], playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], ) -> dict[str, str]: """Remove a sound from a playlist.""" await playlist_service.remove_sound_from_playlist( playlist_id=playlist_id, sound_id=sound_id, user_id=current_user.id, ) return {"message": "Sound removed from playlist successfully"} @router.put("/{playlist_id}/sounds/reorder") async def reorder_playlist_sounds( playlist_id: int, request: ReorderRequest, current_user: Annotated[User, Depends(get_current_active_user_flexible)], playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], ) -> dict[str, str]: """Reorder sounds in a playlist.""" await playlist_service.reorder_playlist_sounds( playlist_id=playlist_id, user_id=current_user.id, sound_positions=request.sound_positions, ) return {"message": "Playlist sounds reordered successfully"} @router.put("/{playlist_id}/set-current") async def set_current_playlist( playlist_id: int, current_user: Annotated[User, Depends(get_current_active_user_flexible)], playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], ) -> PlaylistResponse: """Set a playlist as the current playlist.""" playlist = await playlist_service.set_current_playlist(playlist_id, current_user.id) return PlaylistResponse.from_playlist(playlist) @router.delete("/current") async def unset_current_playlist( current_user: Annotated[User, Depends(get_current_active_user_flexible)], playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], ) -> dict[str, str]: """Unset the current playlist.""" await playlist_service.unset_current_playlist(current_user.id) return {"message": "Current playlist unset successfully"} @router.get("/{playlist_id}/stats") async def get_playlist_stats( playlist_id: int, current_user: Annotated[User, Depends(get_current_active_user_flexible)], playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], ) -> PlaylistStatsResponse: """Get statistics for a playlist.""" stats = await playlist_service.get_playlist_stats(playlist_id) return PlaylistStatsResponse(**stats)