# app/modules/loyalty/routes/api/admin.py """ Loyalty module admin routes. Platform admin endpoints for: - Viewing all loyalty programs (merchant-based) - Merchant loyalty settings management - Platform-wide analytics """ import logging from fastapi import APIRouter, Depends, Path, Query from sqlalchemy.orm import Session from app.api.deps import get_current_admin_api, require_module_access from app.core.database import get_db from app.modules.enums import FrontendType from app.modules.loyalty.schemas import ( MerchantSettingsResponse, MerchantSettingsUpdate, MerchantStatsResponse, ProgramCreate, ProgramListResponse, ProgramResponse, ProgramStatsResponse, ProgramUpdate, ) from app.modules.loyalty.services import program_service from app.modules.tenancy.models import User # API-007 logger = logging.getLogger(__name__) # Admin router with module access control router = APIRouter( prefix="/loyalty", dependencies=[Depends(require_module_access("loyalty", FrontendType.ADMIN))], ) # ============================================================================= # Program Management # ============================================================================= @router.get("/programs", response_model=ProgramListResponse) def list_programs( skip: int = Query(0, ge=0), limit: int = Query(50, ge=1, le=100), is_active: bool | None = Query(None), search: str | None = Query(None, description="Search by merchant name"), current_user: User = Depends(get_current_admin_api), db: Session = Depends(get_db), ): """List all loyalty programs (platform admin).""" programs, total = program_service.list_programs( db, skip=skip, limit=limit, is_active=is_active, search=search, ) program_responses = [] for program in programs: response = ProgramResponse.model_validate(program) response.is_stamps_enabled = program.is_stamps_enabled response.is_points_enabled = program.is_points_enabled response.display_name = program.display_name # Get aggregation stats from service list_stats = program_service.get_program_list_stats(db, program) response.merchant_name = list_stats["merchant_name"] response.total_cards = list_stats["total_cards"] response.active_cards = list_stats["active_cards"] response.total_points_issued = list_stats["total_points_issued"] response.total_points_redeemed = list_stats["total_points_redeemed"] program_responses.append(response) return ProgramListResponse(programs=program_responses, total=total) @router.get("/programs/{program_id}", response_model=ProgramResponse) def get_program( program_id: int, current_user: User = Depends(get_current_admin_api), db: Session = Depends(get_db), ): """Get a specific loyalty program.""" program = program_service.require_program(db, program_id) response = ProgramResponse.model_validate(program) response.is_stamps_enabled = program.is_stamps_enabled response.is_points_enabled = program.is_points_enabled response.display_name = program.display_name return response @router.get("/programs/{program_id}/stats", response_model=ProgramStatsResponse) def get_program_stats( program_id: int, current_user: User = Depends(get_current_admin_api), db: Session = Depends(get_db), ): """Get statistics for a loyalty program.""" stats = program_service.get_program_stats(db, program_id) return ProgramStatsResponse(**stats) @router.post( "/merchants/{merchant_id}/program", response_model=ProgramResponse, status_code=201 ) def create_program_for_merchant( data: ProgramCreate, merchant_id: int = Path(..., gt=0), current_user: User = Depends(get_current_admin_api), db: Session = Depends(get_db), ): """Create a loyalty program for a merchant (admin override).""" program = program_service.create_program(db, merchant_id, data) response = ProgramResponse.model_validate(program) response.is_stamps_enabled = program.is_stamps_enabled response.is_points_enabled = program.is_points_enabled response.display_name = program.display_name logger.info(f"Admin created loyalty program for merchant {merchant_id}") return response @router.patch("/programs/{program_id}", response_model=ProgramResponse) def update_program( data: ProgramUpdate, program_id: int = Path(..., gt=0), current_user: User = Depends(get_current_admin_api), db: Session = Depends(get_db), ): """Update a loyalty program (admin override).""" program = program_service.update_program(db, program_id, data) response = ProgramResponse.model_validate(program) response.is_stamps_enabled = program.is_stamps_enabled response.is_points_enabled = program.is_points_enabled response.display_name = program.display_name logger.info(f"Admin updated loyalty program {program_id}") return response @router.delete("/programs/{program_id}", status_code=204) def delete_program( program_id: int = Path(..., gt=0), current_user: User = Depends(get_current_admin_api), db: Session = Depends(get_db), ): """Delete a loyalty program (admin override).""" program_service.delete_program(db, program_id) logger.info(f"Admin deleted loyalty program {program_id}") @router.post("/programs/{program_id}/activate", response_model=ProgramResponse) def activate_program( program_id: int = Path(..., gt=0), current_user: User = Depends(get_current_admin_api), db: Session = Depends(get_db), ): """Activate a loyalty program.""" program = program_service.activate_program(db, program_id) response = ProgramResponse.model_validate(program) response.is_stamps_enabled = program.is_stamps_enabled response.is_points_enabled = program.is_points_enabled response.display_name = program.display_name logger.info(f"Admin activated loyalty program {program_id}") return response @router.post("/programs/{program_id}/deactivate", response_model=ProgramResponse) def deactivate_program( program_id: int = Path(..., gt=0), current_user: User = Depends(get_current_admin_api), db: Session = Depends(get_db), ): """Deactivate a loyalty program.""" program = program_service.deactivate_program(db, program_id) response = ProgramResponse.model_validate(program) response.is_stamps_enabled = program.is_stamps_enabled response.is_points_enabled = program.is_points_enabled response.display_name = program.display_name logger.info(f"Admin deactivated loyalty program {program_id}") return response # ============================================================================= # Merchant Management # ============================================================================= @router.get("/merchants/{merchant_id}/stats", response_model=MerchantStatsResponse) def get_merchant_stats( merchant_id: int = Path(..., gt=0), current_user: User = Depends(get_current_admin_api), db: Session = Depends(get_db), ): """Get merchant-wide loyalty statistics across all locations.""" stats = program_service.get_merchant_stats(db, merchant_id) return MerchantStatsResponse(**stats) @router.get("/merchants/{merchant_id}/settings", response_model=MerchantSettingsResponse) def get_merchant_settings( merchant_id: int = Path(..., gt=0), current_user: User = Depends(get_current_admin_api), db: Session = Depends(get_db), ): """Get merchant loyalty settings.""" settings = program_service.get_or_create_merchant_settings(db, merchant_id) return MerchantSettingsResponse.model_validate(settings) @router.patch("/merchants/{merchant_id}/settings", response_model=MerchantSettingsResponse) def update_merchant_settings( data: MerchantSettingsUpdate, merchant_id: int = Path(..., gt=0), current_user: User = Depends(get_current_admin_api), db: Session = Depends(get_db), ): """Update merchant loyalty settings (admin only).""" settings = program_service.get_or_create_merchant_settings(db, merchant_id) update_data = data.model_dump(exclude_unset=True) for field, value in update_data.items(): setattr(settings, field, value) db.commit() db.refresh(settings) logger.info(f"Updated merchant {merchant_id} loyalty settings: {list(update_data.keys())}") return MerchantSettingsResponse.model_validate(settings) # ============================================================================= # Platform Stats # ============================================================================= @router.get("/stats") def get_platform_stats( current_user: User = Depends(get_current_admin_api), db: Session = Depends(get_db), ): """Get platform-wide loyalty statistics.""" return program_service.get_platform_stats(db)