"""Like repository — adapter over ``AsyncSession``. Likes are an append-only event log. Current state = latest event per (user, track). """ import uuid from sqlalchemy import func, select from sqlalchemy.ext.asyncio import AsyncSession from app.domain.entities.like import Like from app.domain.entities.track import Track from app.infrastructure.db.models.like import LikeModel from app.infrastructure.db.models.track import TrackModel def _to_entity(row: LikeModel) -> Like: return Like( id=row.id, user_id=row.user_id, track_id=row.track_id, value=row.value, created_at=row.created_at, ) def _track_to_entity(row: TrackModel) -> Track: return Track( id=row.id, title=row.title, artist_id=row.artist_id, album_id=row.album_id, file_path=row.file_path, file_format=row.file_format, file_size=row.file_size, source=row.source, source_id=row.source_id, duration_seconds=row.duration_seconds, genre=row.genre, year=row.year, metadata_status=row.metadata_status, created_at=row.created_at, updated_at=row.updated_at, ) class SqlAlchemyLikeRepository: def __init__(self, session: AsyncSession) -> None: self._session = session async def add(self, *, user_id: uuid.UUID, track_id: uuid.UUID, value: str) -> Like: row = LikeModel(user_id=user_id, track_id=track_id, value=value) self._session.add(row) await self._session.flush() await self._session.refresh(row) return _to_entity(row) async def get_latest_state( self, *, user_id: uuid.UUID, track_ids: list[uuid.UUID] ) -> list[Like]: if not track_ids: return [] # Subquery: max(created_at) per track for this user max_sq = ( select( LikeModel.track_id, func.max(LikeModel.created_at).label("latest"), ) .where(LikeModel.user_id == user_id, LikeModel.track_id.in_(track_ids)) .group_by(LikeModel.track_id) .subquery() ) rows = ( ( await self._session.execute( select(LikeModel) .join( max_sq, (LikeModel.track_id == max_sq.c.track_id) & (LikeModel.created_at == max_sq.c.latest), ) .where(LikeModel.user_id == user_id) ) ) .scalars() .all() ) return [_to_entity(r) for r in rows] async def list_liked_tracks( self, *, user_id: uuid.UUID, limit: int, offset: int ) -> list[Track]: # Tracks where the latest like event has value='like', ordered by like time desc max_sq = ( select( LikeModel.track_id, func.max(LikeModel.created_at).label("latest"), ) .where(LikeModel.user_id == user_id) .group_by(LikeModel.track_id) .subquery() ) liked_sq = ( select(LikeModel.track_id, LikeModel.created_at) .join( max_sq, (LikeModel.track_id == max_sq.c.track_id) & (LikeModel.created_at == max_sq.c.latest), ) .where(LikeModel.user_id == user_id, LikeModel.value == "like") .subquery() ) rows = ( ( await self._session.execute( select(TrackModel) .join(liked_sq, TrackModel.id == liked_sq.c.track_id) .order_by(liked_sq.c.created_at.desc()) .limit(limit) .offset(offset) ) ) .scalars() .all() ) return [_track_to_entity(r) for r in rows] async def count_liked_tracks(self, *, user_id: uuid.UUID) -> int: max_sq = ( select( LikeModel.track_id, func.max(LikeModel.created_at).label("latest"), ) .where(LikeModel.user_id == user_id) .group_by(LikeModel.track_id) .subquery() ) liked_sq = ( select(LikeModel.track_id) .join( max_sq, (LikeModel.track_id == max_sq.c.track_id) & (LikeModel.created_at == max_sq.c.latest), ) .where(LikeModel.user_id == user_id, LikeModel.value == "like") .subquery() ) return ( await self._session.execute(select(func.count()).select_from(liked_sq)) ).scalar_one()