"""Wallet APIs — accounts + immutable ledger (Phase 7.6).""" from __future__ import annotations from uuid import UUID from fastapi import APIRouter, Depends, Query, status from sqlalchemy.ext.asyncio import AsyncSession from app.api.deps import get_db, get_pagination, require_tenant from app.api.permissions import require_permissions from app.models.types import WalletLedgerEntryType from app.permissions.definitions import ( LOYALTY_WALLETS_ADJUST, LOYALTY_WALLETS_CREATE, LOYALTY_WALLETS_CREDIT, LOYALTY_WALLETS_DEBIT, LOYALTY_WALLETS_DELETE, LOYALTY_WALLETS_MANAGE, LOYALTY_WALLETS_TRANSFER, LOYALTY_WALLETS_VIEW, ) from app.schemas.wallet import ( WalletAccountCreate, WalletAccountRead, WalletAccountUpdate, WalletAdjustRequest, WalletBalanceRead, WalletCreditRequest, WalletDebitRequest, WalletLedgerPage, WalletLedgerEntryRead, WalletTransferRequest, WalletTransferResult, ) from app.services.wallet_engine import WalletEngineService from shared.pagination import PaginationParams from shared.security import CurrentUser router = APIRouter() @router.post("", response_model=WalletAccountRead, status_code=status.HTTP_201_CREATED) async def open_wallet( body: WalletAccountCreate, tenant_id: UUID = Depends(require_tenant), db: AsyncSession = Depends(get_db), user: CurrentUser = Depends(require_permissions(LOYALTY_WALLETS_CREATE)), ): return await WalletEngineService(db).create(tenant_id, body, actor=user) @router.get("", response_model=list[WalletAccountRead]) async def list_wallets( tenant_id: UUID = Depends(require_tenant), pagination: PaginationParams = Depends(get_pagination), db: AsyncSession = Depends(get_db), _user: CurrentUser = Depends(require_permissions(LOYALTY_WALLETS_VIEW)), ): return await WalletEngineService(db).list( tenant_id, offset=pagination.offset, limit=pagination.page_size ) @router.get("/{wallet_id}", response_model=WalletAccountRead) async def get_wallet( wallet_id: UUID, tenant_id: UUID = Depends(require_tenant), db: AsyncSession = Depends(get_db), _user: CurrentUser = Depends(require_permissions(LOYALTY_WALLETS_VIEW)), ): return await WalletEngineService(db).get(tenant_id, wallet_id) @router.get("/{wallet_id}/balance", response_model=WalletBalanceRead) async def get_wallet_balance( wallet_id: UUID, tenant_id: UUID = Depends(require_tenant), db: AsyncSession = Depends(get_db), _user: CurrentUser = Depends(require_permissions(LOYALTY_WALLETS_VIEW)), ): return await WalletEngineService(db).get_balance(tenant_id, wallet_id) @router.get("/{wallet_id}/ledger", response_model=WalletLedgerPage) async def list_wallet_ledger( wallet_id: UUID, offset: int = Query(default=0, ge=0), limit: int = Query(default=50, ge=1, le=200), entry_type: WalletLedgerEntryType | None = None, tenant_id: UUID = Depends(require_tenant), db: AsyncSession = Depends(get_db), _user: CurrentUser = Depends(require_permissions(LOYALTY_WALLETS_VIEW)), ): rows, total = await WalletEngineService(db).list_ledger( tenant_id, wallet_id, entry_type=entry_type, offset=offset, limit=limit ) return WalletLedgerPage(items=rows, total=total, offset=offset, limit=limit) @router.patch("/{wallet_id}", response_model=WalletAccountRead) async def update_wallet( wallet_id: UUID, body: WalletAccountUpdate, tenant_id: UUID = Depends(require_tenant), db: AsyncSession = Depends(get_db), user: CurrentUser = Depends(require_permissions(LOYALTY_WALLETS_MANAGE)), ): return await WalletEngineService(db).update(tenant_id, wallet_id, body, actor=user) @router.post("/{wallet_id}/credit", response_model=WalletLedgerEntryRead) async def credit_wallet( wallet_id: UUID, body: WalletCreditRequest, tenant_id: UUID = Depends(require_tenant), db: AsyncSession = Depends(get_db), user: CurrentUser = Depends(require_permissions(LOYALTY_WALLETS_CREDIT)), ): return await WalletEngineService(db).credit(tenant_id, wallet_id, body, actor=user) @router.post("/{wallet_id}/debit", response_model=WalletLedgerEntryRead) async def debit_wallet( wallet_id: UUID, body: WalletDebitRequest, tenant_id: UUID = Depends(require_tenant), db: AsyncSession = Depends(get_db), user: CurrentUser = Depends(require_permissions(LOYALTY_WALLETS_DEBIT)), ): return await WalletEngineService(db).debit(tenant_id, wallet_id, body, actor=user) @router.post("/{wallet_id}/adjust", response_model=WalletLedgerEntryRead) async def adjust_wallet( wallet_id: UUID, body: WalletAdjustRequest, tenant_id: UUID = Depends(require_tenant), db: AsyncSession = Depends(get_db), user: CurrentUser = Depends(require_permissions(LOYALTY_WALLETS_ADJUST)), ): return await WalletEngineService(db).adjust(tenant_id, wallet_id, body, actor=user) @router.post("/{wallet_id}/transfer", response_model=WalletTransferResult) async def transfer_wallet( wallet_id: UUID, body: WalletTransferRequest, tenant_id: UUID = Depends(require_tenant), db: AsyncSession = Depends(get_db), user: CurrentUser = Depends(require_permissions(LOYALTY_WALLETS_TRANSFER)), ): source_entry, target_entry = await WalletEngineService(db).transfer( tenant_id, wallet_id, body, actor=user ) return WalletTransferResult(source_entry=source_entry, target_entry=target_entry) @router.post("/{wallet_id}/delete", response_model=WalletAccountRead) async def close_wallet( wallet_id: UUID, tenant_id: UUID = Depends(require_tenant), db: AsyncSession = Depends(get_db), user: CurrentUser = Depends(require_permissions(LOYALTY_WALLETS_DELETE)), ): return await WalletEngineService(db).soft_delete(tenant_id, wallet_id, actor=user)