"""Toggle post like use case. This module implements the use case for toggling likes on blog posts. If the user already liked the post, the like is removed (unlike). If not, a new like is added. """ from uuid import UUID from app.application.dtos.post import PostResponseDTO from app.application.interfaces import TransactionManager from app.domain.entities import Post from app.domain.entities.like import PostLike from app.domain.exceptions import NotFoundException from app.domain.repositories import PostRepository class TogglePostLikeUseCase: """Use case for toggling a like on a blog post. Handles like/unlike toggle logic. If the user or device has already liked the post, the like is removed. Otherwise, a new like is created. Attributes: _post_repo: Repository for post and like data access. _tx_manager: Transaction manager for commit control. Example: >>> use_case = TogglePostLikeUseCase(post_repo, tx_manager) >>> result = await use_case.execute("my-post-slug", "user-123") """ def __init__( self, post_repo: PostRepository, tx_manager: TransactionManager, ) -> None: """Initialize use case with dependencies. Args: post_repo: Repository for post and like operations. tx_manager: Transaction manager instance. """ self._post_repo = post_repo self._tx_manager = tx_manager async def execute(self, post_id: UUID, liked_by: str) -> PostResponseDTO: """Toggle like on a post. If the user/device already liked the post, remove the like. Otherwise, add a new like. Args: post_id: UUID of the post to toggle like on. liked_by: User ID or device identifier. Returns: PostResponseDTO with updated like_count. Raises: NotFoundException: If post with given ID does not exist. """ post = await self._post_repo.get_by_id(post_id) if not post: raise NotFoundException(f"Post with id '{post_id}' not found") existing_like = await self._post_repo.get_like(post_id, liked_by) if existing_like: await self._post_repo.remove_like(post_id, liked_by) post.like_count = max(0, post.like_count - 1) else: new_like = PostLike(post_id=post_id, liked_by=liked_by) await self._post_repo.add_like(new_like) post.like_count += 1 await self._post_repo.update(post) await self._tx_manager.commit() return self._map_to_dto(post) def _map_to_dto(self, post: Post) -> PostResponseDTO: """Map domain entity to response DTO. Args: post: Domain post entity. Returns: PostResponseDTO with all post attributes including like_count. """ return PostResponseDTO( id=post.id, title=post.title.value, content=post.content.value, slug=post.slug.value, author_id=post.author_id, published=post.published, like_count=post.like_count, tags=post.tags.copy(), created_at=post.created_at, updated_at=post.updated_at, )