from __future__ import annotations import uuid from sqlalchemy import func, select from sqlalchemy.ext.asyncio import AsyncSession from app.models.user import User class UserRepository: def __init__(self, db: AsyncSession) -> None: self.db = db async def get_by_id(self, user_id: uuid.UUID) -> User | None: return await self.db.get(User, user_id) async def count_admins(self) -> int: result = await self.db.execute( select(func.count()).select_from(User).where(User.is_admin.is_(True)) ) return result.scalar_one() async def get_by_email(self, email: str) -> User | None: result = await self.db.execute(select(User).where(User.email == email.lower())) return result.scalar_one_or_none() async def list_admin_emails(self) -> list[str]: result = await self.db.execute(select(User.email).where(User.is_admin.is_(True))) return list(result.scalars().all()) async def delete(self, user: User) -> None: """Cascades (ON DELETE CASCADE, see migrations) to every row the user owns - companies and everything under them, refresh tokens, security events, etc. Irreversible.""" await self.db.delete(user) await self.db.flush() async def create( self, *, email: str, password_hash: str | None, display_name: str, timezone: str, user_id: uuid.UUID | None = None, is_admin: bool = False, email_verified: bool = False, ) -> User: user = User( id=user_id or uuid.uuid4(), email=email.lower(), password_hash=password_hash, display_name=display_name, timezone=timezone, is_admin=is_admin, email_verified=email_verified, ) self.db.add(user) await self.db.flush() return user