import { Injectable, Inject } from "@nestjs/common"; import { Logger } from "nestjs-pino"; import { FreebitOrchestratorService } from "@bff/integrations/freebit/services/freebit-orchestrator.service"; import { SimValidationService } from "./sim-validation.service"; import { SimUsageStoreService } from "../../sim-usage-store.service"; import { getErrorMessage } from "@bff/core/utils/error.util"; import type { SimUsage, SimTopUpHistory } from "@bff/integrations/freebit/interfaces/freebit.types"; import type { SimTopUpHistoryRequest } from "../types/sim-requests.types"; import { BadRequestException } from "@nestjs/common"; @Injectable() export class SimUsageService { constructor( private readonly freebitService: FreebitOrchestratorService, private readonly simValidation: SimValidationService, private readonly usageStore: SimUsageStoreService, @Inject(Logger) private readonly logger: Logger ) {} /** * Get SIM data usage for a subscription */ async getSimUsage(userId: string, subscriptionId: number): Promise { try { const { account } = await this.simValidation.validateSimSubscription(userId, subscriptionId); const simUsage = await this.freebitService.getSimUsage(account); // Persist today's usage for monthly charts and cleanup previous months try { await this.usageStore.upsertToday(account, simUsage.todayUsageMb); await this.usageStore.cleanupPreviousMonths(); const stored = await this.usageStore.getLastNDays(account, 30); if (stored.length > 0) { simUsage.recentDaysUsage = stored.map(d => ({ date: d.date, usageKb: Math.round(d.usageMb * 1000), usageMb: d.usageMb, })); } } catch (e) { const sanitizedError = getErrorMessage(e); this.logger.warn("SIM usage persistence failed (non-fatal)", { account, error: sanitizedError, }); } this.logger.log(`Retrieved SIM usage for subscription ${subscriptionId}`, { userId, subscriptionId, account, todayUsageMb: simUsage.todayUsageMb, }); return simUsage; } catch (error) { const sanitizedError = getErrorMessage(error); this.logger.error(`Failed to get SIM usage for subscription ${subscriptionId}`, { error: sanitizedError, userId, subscriptionId, }); throw error; } } /** * Get SIM top-up history */ async getSimTopUpHistory( userId: string, subscriptionId: number, request: SimTopUpHistoryRequest ): Promise { try { const { account } = await this.simValidation.validateSimSubscription(userId, subscriptionId); // Validate date format if (!/^\d{8}$/.test(request.fromDate) || !/^\d{8}$/.test(request.toDate)) { throw new BadRequestException("Dates must be in YYYYMMDD format"); } const history = await this.freebitService.getSimTopUpHistory( account, request.fromDate, request.toDate ); this.logger.log(`Retrieved SIM top-up history for subscription ${subscriptionId}`, { userId, subscriptionId, account, totalAdditions: history.totalAdditions, }); return history; } catch (error) { const sanitizedError = getErrorMessage(error); this.logger.error(`Failed to get SIM top-up history for subscription ${subscriptionId}`, { error: sanitizedError, userId, subscriptionId, }); throw error; } } }