import { systemEvents, EVENTS } from "@shared/lib/events"; import type { DomainEventPayloads } from "@shared/lib/events"; import { questService } from "@shared/modules/quest/quest.service"; import { dashboardService } from "@shared/modules/dashboard/dashboard.service"; /** * Registers all domain event listeners. * Must be called once at startup before any domain events are emitted. * * This wiring replaces dynamic imports that were previously used to avoid * circular dependencies between services (e.g., economy -> quest -> economy). */ function fireAndForget(fn: () => Promise) { fn().catch(err => console.error("[EventWiring] Fire-and-forget handler failed:", err)); } let registered = false; export function registerDomainEventListeners() { if (registered) return; registered = true; // --- Quest progress tracking (awaited via emitAsync to preserve tx atomicity) --- systemEvents.on(EVENTS.DOMAIN.BALANCE_CHANGED, async (payload: DomainEventPayloads[typeof EVENTS.DOMAIN.BALANCE_CHANGED]) => { await questService.handleEvent(payload.userId, payload.type, 1, payload.tx); }); systemEvents.on(EVENTS.DOMAIN.XP_GAINED, async (payload: DomainEventPayloads[typeof EVENTS.DOMAIN.XP_GAINED]) => { await questService.handleEvent(payload.userId, 'XP_GAIN', payload.amount, payload.tx); }); systemEvents.on(EVENTS.DOMAIN.ITEM_COLLECTED, async (payload: DomainEventPayloads[typeof EVENTS.DOMAIN.ITEM_COLLECTED]) => { await questService.handleEvent(payload.userId, `ITEM_COLLECT:${payload.itemId}`, payload.quantity, payload.tx); }); systemEvents.on(EVENTS.DOMAIN.ITEM_USED, async (payload: DomainEventPayloads[typeof EVENTS.DOMAIN.ITEM_USED]) => { await questService.handleEvent(payload.userId, `ITEM_USE:${payload.itemId}`, 1, payload.tx); }); systemEvents.on(EVENTS.DOMAIN.TRANSFER_COMPLETED, async (payload: DomainEventPayloads[typeof EVENTS.DOMAIN.TRANSFER_COMPLETED]) => { await questService.handleEvent(payload.userId, 'TRANSFER_OUT', 1, payload.tx); }); systemEvents.on(EVENTS.DOMAIN.DAILY_CLAIMED, async (payload: DomainEventPayloads[typeof EVENTS.DOMAIN.DAILY_CLAIMED]) => { await questService.handleEvent(payload.userId, 'DAILY_REWARD', 1, payload.tx); }); systemEvents.on(EVENTS.DOMAIN.TRIVIA_WON, async (payload: DomainEventPayloads[typeof EVENTS.DOMAIN.TRIVIA_WON]) => { await questService.handleEvent(payload.userId, 'TRIVIA_WIN', 1, payload.tx); }); systemEvents.on(EVENTS.DOMAIN.EXAM_PASSED, async (payload: DomainEventPayloads[typeof EVENTS.DOMAIN.EXAM_PASSED]) => { await questService.handleEvent(payload.userId, 'EXAM_REWARD', 1, payload.tx); }); // --- Dashboard event recording (fire-and-forget) --- systemEvents.on(EVENTS.DOMAIN.TRANSFER_COMPLETED, (payload: DomainEventPayloads[typeof EVENTS.DOMAIN.TRANSFER_COMPLETED]) => { fireAndForget(async () => { await dashboardService.recordEvent({ type: 'info', message: `${payload.username} transferred ${payload.amount.toLocaleString()} AU to User ID ${payload.toUserId}`, icon: '💸' }); }); }); systemEvents.on(EVENTS.DOMAIN.DAILY_CLAIMED, (payload: DomainEventPayloads[typeof EVENTS.DOMAIN.DAILY_CLAIMED]) => { fireAndForget(async () => { await dashboardService.recordEvent({ type: 'success', message: `${payload.username} claimed daily reward: ${payload.amount.toLocaleString()} AU`, icon: '☀️' }); }); }); systemEvents.on(EVENTS.DOMAIN.TRIVIA_STARTED, (payload: DomainEventPayloads[typeof EVENTS.DOMAIN.TRIVIA_STARTED]) => { fireAndForget(async () => { await dashboardService.recordEvent({ type: 'info', message: `${payload.username} started a trivia game (${payload.difficulty})`, icon: '🎯' }); }); }); systemEvents.on(EVENTS.DOMAIN.TRIVIA_WON, (payload: DomainEventPayloads[typeof EVENTS.DOMAIN.TRIVIA_WON]) => { fireAndForget(async () => { await dashboardService.recordEvent({ type: 'success', message: `${payload.username ?? 'Unknown user'} won ${payload.reward.toLocaleString()} AU from trivia!`, icon: '🎉' }); }); }); systemEvents.on(EVENTS.DOMAIN.EXAM_PASSED, (payload: DomainEventPayloads[typeof EVENTS.DOMAIN.EXAM_PASSED]) => { fireAndForget(async () => { await dashboardService.recordEvent({ type: 'success', message: `${payload.username} passed their exam: ${payload.reward.toLocaleString()} AU`, icon: '🎓' }); }); }); }