"""Reward redemption repositories — Phase 7.3.""" from __future__ import annotations from uuid import UUID from sqlalchemy import func, select from app.models.rewards import RewardRedemption from app.models.types import RedemptionStatus from app.repositories.base import TenantBaseRepository _NON_BLOCKING_STATUSES = (RedemptionStatus.CANCELLED,) class RewardRedemptionRepository(TenantBaseRepository[RewardRedemption]): model = RewardRedemption async def get_by_idempotency( self, tenant_id: UUID, idempotency_key: str | None ) -> RewardRedemption | None: if not idempotency_key: return None stmt = select(RewardRedemption).where( RewardRedemption.tenant_id == tenant_id, RewardRedemption.idempotency_key == idempotency_key, ) result = await self.session.execute(stmt) return result.scalar_one_or_none() async def count_for_member_reward( self, tenant_id: UUID, member_id: UUID, reward_id: UUID, *, exclude_statuses: tuple[RedemptionStatus, ...] = _NON_BLOCKING_STATUSES, ) -> int: stmt = ( select(func.count()) .select_from(RewardRedemption) .where( RewardRedemption.tenant_id == tenant_id, RewardRedemption.member_id == member_id, RewardRedemption.reward_id == reward_id, RewardRedemption.status.notin_(exclude_statuses), ) ) result = await self.session.execute(stmt) return int(result.scalar_one()) async def list( self, tenant_id: UUID, *, reward_id: UUID | None = None, member_id: UUID | None = None, status: RedemptionStatus | None = None, offset: int = 0, limit: int = 50, ): clauses = [RewardRedemption.tenant_id == tenant_id] if reward_id is not None: clauses.append(RewardRedemption.reward_id == reward_id) if member_id is not None: clauses.append(RewardRedemption.member_id == member_id) if status is not None: clauses.append(RewardRedemption.status == status) stmt = ( select(RewardRedemption) .where(*clauses) .order_by(RewardRedemption.created_at.desc()) .offset(offset) .limit(limit) ) result = await self.session.execute(stmt) return result.scalars().all() async def count( self, tenant_id: UUID, *, reward_id: UUID | None = None, member_id: UUID | None = None, status: RedemptionStatus | None = None, ) -> int: clauses = [RewardRedemption.tenant_id == tenant_id] if reward_id is not None: clauses.append(RewardRedemption.reward_id == reward_id) if member_id is not None: clauses.append(RewardRedemption.member_id == member_id) if status is not None: clauses.append(RewardRedemption.status == status) stmt = select(func.count()).select_from(RewardRedemption).where(*clauses) result = await self.session.execute(stmt) return int(result.scalar_one()) async def list_for_member( self, tenant_id: UUID, member_id: UUID, *, offset: int = 0, limit: int = 50 ): return await self.list(tenant_id, member_id=member_id, offset=offset, limit=limit) async def list_for_reward( self, tenant_id: UUID, reward_id: UUID, *, offset: int = 0, limit: int = 50 ): return await self.list(tenant_id, reward_id=reward_id, offset=offset, limit=limit)