import { Controller, Get, Post, Param, Query, Body, Request, ParseIntPipe, BadRequestException, UsePipes, Header, } from "@nestjs/common"; import { SubscriptionsService } from "./subscriptions.service"; import { SimManagementService } from "./sim-management.service"; import { Subscription, SubscriptionList, SubscriptionStats, SimActionResponse, SimPlanChangeResult, subscriptionQuerySchema, type SubscriptionQuery, } from "@customer-portal/domain/subscriptions"; import { InvoiceList } from "@customer-portal/domain/billing"; import { createPaginationSchema } from "@customer-portal/domain/toolkit/validation/helpers"; import type { z } from "zod"; import { simTopupRequestSchema, simChangePlanRequestSchema, simCancelRequestSchema, simFeaturesRequestSchema, simReissueRequestSchema, type SimInfo, type SimTopupRequest, type SimChangePlanRequest, type SimCancelRequest, type SimFeaturesRequest, type SimReissueRequest, } from "@customer-portal/domain/sim"; import { ZodValidationPipe } from "@customer-portal/validation/nestjs"; import type { RequestWithUser } from "@bff/modules/auth/auth.types"; const subscriptionInvoiceQuerySchema = createPaginationSchema({ defaultLimit: 10, maxLimit: 100, minLimit: 1, }); type SubscriptionInvoiceQuery = z.infer; @Controller("subscriptions") export class SubscriptionsController { constructor( private readonly subscriptionsService: SubscriptionsService, private readonly simManagementService: SimManagementService ) {} @Get() @Header("Cache-Control", "private, max-age=300") // 5 minutes, user-specific @UsePipes(new ZodValidationPipe(subscriptionQuerySchema)) async getSubscriptions( @Request() req: RequestWithUser, @Query() query: SubscriptionQuery ): Promise { const { status } = query; return this.subscriptionsService.getSubscriptions(req.user.id, { status }); } @Get("active") @Header("Cache-Control", "private, max-age=300") // 5 minutes, user-specific async getActiveSubscriptions(@Request() req: RequestWithUser): Promise { return this.subscriptionsService.getActiveSubscriptions(req.user.id); } @Get("stats") @Header("Cache-Control", "private, max-age=300") // 5 minutes, user-specific async getSubscriptionStats(@Request() req: RequestWithUser): Promise { return this.subscriptionsService.getSubscriptionStats(req.user.id); } @Get(":id") @Header("Cache-Control", "private, max-age=300") // 5 minutes, user-specific async getSubscriptionById( @Request() req: RequestWithUser, @Param("id", ParseIntPipe) subscriptionId: number ): Promise { return this.subscriptionsService.getSubscriptionById(req.user.id, subscriptionId); } @Get(":id/invoices") @Header("Cache-Control", "private, max-age=60") // 1 minute, may update with payments async getSubscriptionInvoices( @Request() req: RequestWithUser, @Param("id", ParseIntPipe) subscriptionId: number, @Query(new ZodValidationPipe(subscriptionInvoiceQuerySchema)) query: SubscriptionInvoiceQuery ): Promise { return this.subscriptionsService.getSubscriptionInvoices(req.user.id, subscriptionId, query); } // ==================== SIM Management Endpoints ==================== @Get(":id/sim/debug") async debugSimSubscription( @Request() req: RequestWithUser, @Param("id", ParseIntPipe) subscriptionId: number ): Promise> { return this.simManagementService.debugSimSubscription(req.user.id, subscriptionId); } @Get(":id/sim") async getSimInfo( @Request() req: RequestWithUser, @Param("id", ParseIntPipe) subscriptionId: number ): Promise { return this.simManagementService.getSimInfo(req.user.id, subscriptionId); } @Get(":id/sim/details") async getSimDetails( @Request() req: RequestWithUser, @Param("id", ParseIntPipe) subscriptionId: number ) { return this.simManagementService.getSimDetails(req.user.id, subscriptionId); } @Get(":id/sim/usage") async getSimUsage( @Request() req: RequestWithUser, @Param("id", ParseIntPipe) subscriptionId: number ) { return this.simManagementService.getSimUsage(req.user.id, subscriptionId); } @Get(":id/sim/top-up-history") async getSimTopUpHistory( @Request() req: RequestWithUser, @Param("id", ParseIntPipe) subscriptionId: number, @Query("fromDate") fromDate: string, @Query("toDate") toDate: string ) { if (!fromDate || !toDate) { throw new BadRequestException("fromDate and toDate are required"); } return this.simManagementService.getSimTopUpHistory(req.user.id, subscriptionId, { fromDate, toDate, }); } @Post(":id/sim/top-up") @UsePipes(new ZodValidationPipe(simTopupRequestSchema)) async topUpSim( @Request() req: RequestWithUser, @Param("id", ParseIntPipe) subscriptionId: number, @Body() body: SimTopupRequest ): Promise { await this.simManagementService.topUpSim(req.user.id, subscriptionId, body); return { success: true, message: "SIM top-up completed successfully" }; } @Post(":id/sim/change-plan") @UsePipes(new ZodValidationPipe(simChangePlanRequestSchema)) async changeSimPlan( @Request() req: RequestWithUser, @Param("id", ParseIntPipe) subscriptionId: number, @Body() body: SimChangePlanRequest ): Promise { const result = await this.simManagementService.changeSimPlan(req.user.id, subscriptionId, body); return { success: true, message: "SIM plan change completed successfully", ...result, }; } @Post(":id/sim/cancel") @UsePipes(new ZodValidationPipe(simCancelRequestSchema)) async cancelSim( @Request() req: RequestWithUser, @Param("id", ParseIntPipe) subscriptionId: number, @Body() body: SimCancelRequest ): Promise { await this.simManagementService.cancelSim(req.user.id, subscriptionId, body); return { success: true, message: "SIM cancellation completed successfully" }; } @Post(":id/sim/reissue-esim") @UsePipes(new ZodValidationPipe(simReissueRequestSchema)) async reissueEsimProfile( @Request() req: RequestWithUser, @Param("id", ParseIntPipe) subscriptionId: number, @Body() body: SimReissueRequest ): Promise { await this.simManagementService.reissueEsimProfile(req.user.id, subscriptionId, body); return { success: true, message: "eSIM profile reissue completed successfully" }; } @Post(":id/sim/features") @UsePipes(new ZodValidationPipe(simFeaturesRequestSchema)) async updateSimFeatures( @Request() req: RequestWithUser, @Param("id", ParseIntPipe) subscriptionId: number, @Body() body: SimFeaturesRequest ): Promise { await this.simManagementService.updateSimFeatures(req.user.id, subscriptionId, body); return { success: true, message: "SIM features updated successfully" }; } }