"""Playlist management API endpoints.""" from typing import Annotated from fastapi import APIRouter, Depends, HTTPException, Query, status 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.user import User from app.repositories.playlist import PlaylistSortField, SortOrder from app.schemas.common import MessageResponse from app.schemas.playlist import ( PlaylistAddSoundRequest, PlaylistCreateRequest, PlaylistReorderRequest, PlaylistResponse, PlaylistSoundResponse, PlaylistStatsResponse, PlaylistUpdateRequest, ) from app.services.favorite import FavoriteService from app.services.playlist import PlaylistService router = APIRouter(prefix="/playlists", tags=["playlists"]) async def get_playlist_service( session: Annotated[AsyncSession, Depends(get_db)], ) -> PlaylistService: """Get the playlist service.""" return PlaylistService(session) def get_favorite_service() -> FavoriteService: """Get the favorite service.""" from app.core.database import get_session_factory return FavoriteService(get_session_factory()) @router.get("/") async def get_all_playlists( # noqa: PLR0913 current_user: Annotated[User, Depends(get_current_active_user_flexible)], playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], favorite_service: Annotated[FavoriteService, Depends(get_favorite_service)], search: Annotated[ str | None, Query(description="Search playlists by name"), ] = None, sort_by: Annotated[ PlaylistSortField | None, Query(description="Sort by field"), ] = None, sort_order: Annotated[ SortOrder, Query(description="Sort order (asc or desc)"), ] = SortOrder.ASC, limit: Annotated[ int | None, Query(description="Maximum number of results", ge=1, le=1000), ] = None, offset: Annotated[ int, Query(description="Number of results to skip", ge=0), ] = 0, favorites_only: Annotated[ bool, Query(description="Show only favorited playlists"), ] = False, ) -> list[PlaylistResponse]: """Get all playlists from all users with search and sorting.""" playlists = await playlist_service.search_and_sort_playlists( search_query=search, sort_by=sort_by, sort_order=sort_order, user_id=None, include_stats=True, limit=limit, offset=offset, favorites_only=favorites_only, current_user_id=current_user.id, ) # Convert to PlaylistResponse with favorite indicators playlist_responses = [] for playlist_dict in playlists: # The playlist service returns dict, need to create playlist object-like structure is_favorited = await favorite_service.is_playlist_favorited(current_user.id, playlist_dict["id"]) favorite_count = await favorite_service.get_playlist_favorite_count(playlist_dict["id"]) # Create a PlaylistResponse-like dict with proper datetime conversion playlist_response = { **playlist_dict, "created_at": playlist_dict["created_at"].isoformat() if playlist_dict["created_at"] else None, "updated_at": playlist_dict["updated_at"].isoformat() if playlist_dict["updated_at"] else None, "is_favorited": is_favorited, "favorite_count": favorite_count, } playlist_responses.append(playlist_response) return playlist_responses @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)], favorite_service: Annotated[FavoriteService, Depends(get_favorite_service)], ) -> list[PlaylistResponse]: """Get playlists for the current user only.""" playlists = await playlist_service.get_user_playlists(current_user.id) # Add favorite indicators for each playlist playlist_responses = [] for playlist in playlists: is_favorited = await favorite_service.is_playlist_favorited(current_user.id, playlist.id) favorite_count = await favorite_service.get_playlist_favorite_count(playlist.id) playlist_response = PlaylistResponse.from_playlist(playlist, is_favorited, favorite_count) playlist_responses.append(playlist_response) return playlist_responses @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)], favorite_service: Annotated[FavoriteService, Depends(get_favorite_service)], ) -> PlaylistResponse: """Get the global main playlist.""" playlist = await playlist_service.get_main_playlist() is_favorited = await favorite_service.is_playlist_favorited(current_user.id, playlist.id) favorite_count = await favorite_service.get_playlist_favorite_count(playlist.id) return PlaylistResponse.from_playlist(playlist, is_favorited, favorite_count) @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)], favorite_service: Annotated[FavoriteService, Depends(get_favorite_service)], ) -> PlaylistResponse: """Get the global current playlist (falls back to main playlist).""" playlist = await playlist_service.get_current_playlist() is_favorited = await favorite_service.is_playlist_favorited(current_user.id, playlist.id) favorite_count = await favorite_service.get_playlist_favorite_count(playlist.id) return PlaylistResponse.from_playlist(playlist, is_favorited, favorite_count) @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)], favorite_service: Annotated[FavoriteService, Depends(get_favorite_service)], ) -> PlaylistResponse: """Get a specific playlist.""" playlist = await playlist_service.get_playlist_by_id(playlist_id) is_favorited = await favorite_service.is_playlist_favorited(current_user.id, playlist.id) favorite_count = await favorite_service.get_playlist_favorite_count(playlist.id) return PlaylistResponse.from_playlist(playlist, is_favorited, favorite_count) @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.""" if current_user.id is None: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="User ID not available", ) 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=None, # is_current is not handled by this endpoint ) 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)], ) -> MessageResponse: """Delete a playlist.""" await playlist_service.delete_playlist(playlist_id, current_user.id) return MessageResponse(message="Playlist deleted successfully") @router.get("/search/{query}") async def search_playlists( query: str, current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001 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)], # noqa: ARG001 playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], ) -> list[PlaylistSoundResponse]: """Get all sounds in a playlist.""" sounds = await playlist_service.get_playlist_sounds(playlist_id) return [PlaylistSoundResponse.from_sound(sound) for sound in sounds] @router.post("/{playlist_id}/sounds") async def add_sound_to_playlist( playlist_id: int, request: PlaylistAddSoundRequest, current_user: Annotated[User, Depends(get_current_active_user_flexible)], playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], ) -> MessageResponse: """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 MessageResponse(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)], ) -> MessageResponse: """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 MessageResponse(message="Sound removed from playlist successfully") @router.put("/{playlist_id}/sounds/reorder") async def reorder_playlist_sounds( playlist_id: int, request: PlaylistReorderRequest, current_user: Annotated[User, Depends(get_current_active_user_flexible)], playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], ) -> MessageResponse: """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 MessageResponse(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)], # noqa: ARG001 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) return PlaylistResponse.from_playlist(playlist) @router.delete("/current") async def unset_current_playlist( current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001 playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], ) -> MessageResponse: """Unset the current playlist.""" await playlist_service.unset_current_playlist() return MessageResponse(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)], # noqa: ARG001 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)