"""Loyalty foundation repositories — Phase 7.0.""" from __future__ import annotations from uuid import UUID from sqlalchemy import select from app.models.foundation import ( Campaign, LoyaltyAuditLog, LoyaltyProgram, Member, MembershipLifecycleEvent, MembershipTier, PointAccount, Reward, ) from app.repositories.base import TenantBaseRepository class LoyaltyProgramRepository(TenantBaseRepository[LoyaltyProgram]): model = LoyaltyProgram async def get_by_code(self, tenant_id: UUID, code: str) -> LoyaltyProgram | None: stmt = select(LoyaltyProgram).where( LoyaltyProgram.tenant_id == tenant_id, LoyaltyProgram.code == code, LoyaltyProgram.is_deleted.is_(False), ) result = await self.session.execute(stmt) return result.scalar_one_or_none() class MembershipTierRepository(TenantBaseRepository[MembershipTier]): model = MembershipTier async def get_by_code( self, tenant_id: UUID, program_id: UUID, code: str ) -> MembershipTier | None: stmt = select(MembershipTier).where( MembershipTier.tenant_id == tenant_id, MembershipTier.program_id == program_id, MembershipTier.code == code, MembershipTier.is_deleted.is_(False), ) result = await self.session.execute(stmt) return result.scalar_one_or_none() async def list_by_program( self, tenant_id: UUID, program_id: UUID, *, offset: int = 0, limit: int = 50 ): stmt = ( select(MembershipTier) .where( MembershipTier.tenant_id == tenant_id, MembershipTier.program_id == program_id, MembershipTier.is_deleted.is_(False), ) .order_by(MembershipTier.rank.asc(), MembershipTier.created_at.desc()) .offset(offset) .limit(limit) ) result = await self.session.execute(stmt) return result.scalars().all() class MemberRepository(TenantBaseRepository[Member]): model = Member async def get_by_membership_number( self, tenant_id: UUID, membership_number: str ) -> Member | None: stmt = select(Member).where( Member.tenant_id == tenant_id, Member.membership_number == membership_number, Member.is_deleted.is_(False), ) result = await self.session.execute(stmt) return result.scalar_one_or_none() async def get_by_external_ref( self, tenant_id: UUID, program_id: UUID, external_customer_ref: str ) -> Member | None: stmt = select(Member).where( Member.tenant_id == tenant_id, Member.program_id == program_id, Member.external_customer_ref == external_customer_ref, Member.is_deleted.is_(False), ) result = await self.session.execute(stmt) return result.scalar_one_or_none() async def count_blocking_for_program( self, tenant_id: UUID, program_id: UUID, statuses: list ) -> int: from sqlalchemy import func stmt = ( select(func.count()) .select_from(Member) .where( Member.tenant_id == tenant_id, Member.program_id == program_id, Member.is_deleted.is_(False), Member.status.in_(statuses), ) ) result = await self.session.execute(stmt) return int(result.scalar_one()) class PointAccountRepository(TenantBaseRepository[PointAccount]): model = PointAccount async def get_by_member( self, tenant_id: UUID, member_id: UUID ) -> PointAccount | None: stmt = select(PointAccount).where( PointAccount.tenant_id == tenant_id, PointAccount.member_id == member_id, PointAccount.is_deleted.is_(False), ) result = await self.session.execute(stmt) return result.scalar_one_or_none() async def get_by_account_number( self, tenant_id: UUID, account_number: str ) -> PointAccount | None: stmt = select(PointAccount).where( PointAccount.tenant_id == tenant_id, PointAccount.account_number == account_number, PointAccount.is_deleted.is_(False), ) result = await self.session.execute(stmt) return result.scalar_one_or_none() class RewardRepository(TenantBaseRepository[Reward]): model = Reward async def get_by_code( self, tenant_id: UUID, program_id: UUID, code: str ) -> Reward | None: stmt = select(Reward).where( Reward.tenant_id == tenant_id, Reward.program_id == program_id, Reward.code == code, Reward.is_deleted.is_(False), ) result = await self.session.execute(stmt) return result.scalar_one_or_none() class CampaignRepository(TenantBaseRepository[Campaign]): model = Campaign async def get_by_code( self, tenant_id: UUID, program_id: UUID, code: str ) -> Campaign | None: stmt = select(Campaign).where( Campaign.tenant_id == tenant_id, Campaign.program_id == program_id, Campaign.code == code, Campaign.is_deleted.is_(False), ) result = await self.session.execute(stmt) return result.scalar_one_or_none() class LoyaltyAuditLogRepository(TenantBaseRepository[LoyaltyAuditLog]): model = LoyaltyAuditLog async def list_for_entity( self, tenant_id: UUID, entity_type: str, entity_id: UUID, *, limit: int = 100, ): stmt = ( select(LoyaltyAuditLog) .where( LoyaltyAuditLog.tenant_id == tenant_id, LoyaltyAuditLog.entity_type == entity_type, LoyaltyAuditLog.entity_id == entity_id, ) .order_by(LoyaltyAuditLog.created_at.desc()) .limit(limit) ) result = await self.session.execute(stmt) return result.scalars().all() class MembershipLifecycleEventRepository( TenantBaseRepository[MembershipLifecycleEvent] ): model = MembershipLifecycleEvent async def list_for_member( self, tenant_id: UUID, member_id: UUID, *, limit: int = 100 ): stmt = ( select(MembershipLifecycleEvent) .where( MembershipLifecycleEvent.tenant_id == tenant_id, MembershipLifecycleEvent.member_id == member_id, ) .order_by(MembershipLifecycleEvent.created_at.desc()) .limit(limit) ) result = await self.session.execute(stmt) return result.scalars().all()