from __future__ import annotations import uuid from sqlalchemy import Select, select from sqlalchemy.exc import IntegrityError from sqlalchemy.ext.asyncio import AsyncSession from cpv3.modules.users.models import User from cpv3.modules.users.schemas import UserCreate, UserRegister, UserUpdate from cpv3.infrastructure.security import hash_password class UserRepository: """Repository for User database operations.""" def __init__(self, session: AsyncSession) -> None: self._session = session async def get_by_id(self, user_id: uuid.UUID) -> User | None: result = await self._session.execute(select(User).where(User.id == user_id)) return result.scalar_one_or_none() async def get_by_username(self, username: str) -> User | None: result = await self._session.execute( select(User).where(User.username == username) ) return result.scalar_one_or_none() async def list_all(self, *, requester: User) -> list[User]: stmt: Select[tuple[User]] = select(User) if not requester.is_staff: stmt = stmt.where(User.id == requester.id) result = await self._session.execute(stmt.order_by(User.created_at.desc())) return list(result.scalars().all()) async def create(self, *, data: UserCreate | UserRegister) -> User: user = User( username=data.username, email=data.email, password_hash=hash_password(data.password), first_name=data.first_name, last_name=data.last_name, phone_number=data.phone_number, avatar=data.avatar, ) self._session.add(user) try: await self._session.commit() except IntegrityError as e: await self._session.rollback() raise ValueError("User already exists or violates constraints") from e await self._session.refresh(user) return user async def update(self, user: User, data: UserUpdate) -> User: update_data = data.model_dump(exclude_unset=True) for key, value in update_data.items(): if value is not None: setattr(user, key, value) try: await self._session.commit() except IntegrityError as e: await self._session.rollback() raise ValueError("Update violates constraints") from e await self._session.refresh(user) return user async def update_password(self, user: User, new_hash: str) -> None: user.password_hash = new_hash await self._session.commit() async def deactivate(self, user: User) -> None: user.is_active = False await self._session.commit()