import logging from typing import Optional from src.ez_lan_manager.services.AccountingService import AccountingService, InsufficientFundsError from src.ez_lan_manager.services.DatabaseService import DatabaseService from src.ez_lan_manager.types.ConfigurationTypes import TicketInfo from src.ez_lan_manager.types.Ticket import Ticket logger = logging.getLogger(__name__.split(".")[-1]) class TicketNotAvailableError(Exception): def __init__(self, category: str): self.category = category class UserAlreadyHasTicketError(Exception): pass class TicketingService: def __init__(self, ticket_infos: tuple[TicketInfo, ...], db_service: DatabaseService, accounting_service: AccountingService) -> None: self._ticket_infos = ticket_infos self._db_service = db_service self._accounting_service = accounting_service def get_ticket_info_by_category(self, category: str) -> Optional[TicketInfo]: return next(filter(lambda t: t.category == category, self._ticket_infos), None) def get_total_tickets(self) -> int: return sum([t_i.total_tickets for t_i in self._ticket_infos]) async def get_available_tickets_for_category(self, category: str) -> int: ticket_info = self.get_ticket_info_by_category(category) if not ticket_info or ticket_info.total_tickets < 1: return 0 result = ticket_info.total_tickets all_tickets = await self._db_service.get_tickets() for ticket in all_tickets: if ticket.category == category: result -= 1 return result async def purchase_ticket(self, user_id: int, category: str) -> Ticket: all_categories = [t_i.category for t_i in self._ticket_infos] if category not in all_categories or (await self.get_available_tickets_for_category(category)) < 1: raise TicketNotAvailableError(category) user_balance = await self._accounting_service.get_balance(user_id) ticket_info = self.get_ticket_info_by_category(category) if not ticket_info: raise TicketNotAvailableError(category) if ticket_info.price > user_balance: raise InsufficientFundsError if await self.get_user_ticket(user_id): raise UserAlreadyHasTicketError if new_ticket := await self._db_service.generate_ticket_for_user(user_id, category): await self._accounting_service.remove_balance( user_id, ticket_info.price, f"TICKET {new_ticket.ticket_id}" ) logger.debug(f"User {user_id} purchased ticket {new_ticket.ticket_id}") return new_ticket raise RuntimeError("An unknown error occurred while purchasing ticket") async def refund_ticket(self, user_id: int) -> bool: user_ticket = await self.get_user_ticket(user_id) if not user_ticket: return False ticket_info = self.get_ticket_info_by_category(user_ticket.category) if await self._db_service.delete_ticket(user_ticket.ticket_id): await self._accounting_service.add_balance(user_id, ticket_info.price, f"TICKET REFUND {user_ticket.ticket_id}") logger.debug(f"User {user_id} refunded ticket {user_ticket.ticket_id}") return True return False async def transfer_ticket(self, ticket_id: int, user_id: int) -> bool: return await self._db_service.change_ticket_owner(ticket_id, user_id) async def get_user_ticket(self, user_id: int) -> Optional[Ticket]: return await self._db_service.get_ticket_for_user(user_id)