"""SQLAlchemy implementation of CommentRepository. This module provides the concrete implementation of CommentRepository using SQLAlchemy ORM for data persistence. """ from uuid import UUID from sqlalchemy import func, select from sqlalchemy.ext.asyncio import AsyncSession from app.domain.entities.comment import Comment from app.domain.entities.comment_like import CommentLike from app.domain.repositories import CommentRepository from app.domain.value_objects.comment_content import CommentContent from app.infrastructure.database.models import CommentLikeORM, CommentORM class SQLAlchemyCommentRepository(CommentRepository): """SQLAlchemy implementation of Comment repository. Provides data access methods for Comment entities using SQLAlchemy ORM. Handles conversion between domain entities and ORM models. Attributes: _session: SQLAlchemy async session for database operations. """ def __init__(self, session: AsyncSession) -> None: """Initialize repository with session. Args: session: SQLAlchemy async session instance. """ self._session = session def _to_domain(self, orm: CommentORM) -> Comment: """Convert ORM model to domain entity. Args: orm: SQLAlchemy ORM model instance. Returns: Domain Comment entity with validated value objects. """ return Comment( id=UUID(orm.id), post_id=UUID(orm.post_id), author_id=orm.author_id, content=CommentContent(orm.content), parent_id=UUID(orm.parent_id) if orm.parent_id else None, like_count=orm.like_count, created_at=orm.created_at, updated_at=orm.updated_at, ) def _to_orm(self, comment: Comment) -> CommentORM: """Convert domain entity to ORM model. Args: comment: Domain Comment entity. Returns: SQLAlchemy ORM model instance. """ return CommentORM( id=str(comment.id), post_id=str(comment.post_id), author_id=comment.author_id, content=comment.content.value, parent_id=str(comment.parent_id) if comment.parent_id else None, like_count=comment.like_count, created_at=comment.created_at, updated_at=comment.updated_at, ) async def get_by_id(self, entity_id: UUID) -> Comment | None: """Get comment by ID. Args: entity_id: Unique identifier of the comment. Returns: Comment entity if found, None otherwise. """ result = await self._session.execute( select(CommentORM).where(CommentORM.id == str(entity_id)) ) orm = result.scalar_one_or_none() return self._to_domain(orm) if orm else None async def get_all(self) -> list[Comment]: """Get all comments. Returns: List of all Comment entities. """ result = await self._session.execute(select(CommentORM)) orms = result.scalars().all() return [self._to_domain(orm) for orm in orms] async def add(self, entity: Comment) -> None: """Add new comment. Args: entity: Comment entity to add. """ orm = self._to_orm(entity) self._session.add(orm) async def update(self, entity: Comment) -> None: """Update existing comment. Args: entity: Comment entity with updated data. """ result = await self._session.execute( select(CommentORM).where(CommentORM.id == str(entity.id)) ) orm = result.scalar_one() orm.content = entity.content.value orm.like_count = entity.like_count orm.updated_at = entity.updated_at async def delete(self, entity_id: UUID) -> None: """Delete comment by ID. Args: entity_id: Unique identifier of the comment to delete. """ result = await self._session.execute( select(CommentORM).where(CommentORM.id == str(entity_id)) ) orm = result.scalar_one_or_none() if orm: await self._session.delete(orm) async def exists(self, entity_id: UUID) -> bool: """Check if comment exists. Args: entity_id: Unique identifier of the comment. Returns: True if comment exists, False otherwise. """ result = await self._session.execute( select(CommentORM).where(CommentORM.id == str(entity_id)) ) return result.scalar_one_or_none() is not None async def get_by_post(self, post_id: UUID) -> list[Comment]: """Get all comments for a post, ordered by creation time. Args: post_id: UUID of the post. Returns: List of Comment entities for the post. """ result = await self._session.execute( select(CommentORM) .where(CommentORM.post_id == str(post_id)) .order_by(CommentORM.created_at.asc()) ) orms = result.scalars().all() return [self._to_domain(orm) for orm in orms] async def count_by_post(self, post_id: UUID) -> int: """Get comment count for a post. Args: post_id: UUID of the post. Returns: Number of comments on the post. """ result = await self._session.execute( select(func.count()).select_from(CommentORM).where(CommentORM.post_id == str(post_id)) ) count: int = result.scalar() or 0 return count async def get_like(self, comment_id: UUID, liked_by: str) -> CommentLike | None: """Get a like by comment and user. Args: comment_id: UUID of the comment. liked_by: User ID. Returns: CommentLike if found, None otherwise. """ result = await self._session.execute( select(CommentLikeORM).where( CommentLikeORM.comment_id == str(comment_id), CommentLikeORM.liked_by == liked_by, ) ) orm = result.scalar_one_or_none() if not orm: return None return CommentLike( id=UUID(orm.id), comment_id=UUID(orm.comment_id), liked_by=orm.liked_by, created_at=orm.created_at, ) async def add_like(self, like: CommentLike) -> None: """Add a new like to a comment. Args: like: CommentLike entity to add. """ orm = CommentLikeORM( id=str(like.id), comment_id=str(like.comment_id), liked_by=like.liked_by, created_at=like.created_at, ) self._session.add(orm) async def remove_like(self, comment_id: UUID, liked_by: str) -> None: """Remove a like from a comment by user. Args: comment_id: UUID of the comment. liked_by: User ID. """ result = await self._session.execute( select(CommentLikeORM).where( CommentLikeORM.comment_id == str(comment_id), CommentLikeORM.liked_by == liked_by, ) ) orm = result.scalar_one_or_none() if orm: await self._session.delete(orm)