import { Controller, Get, Param, Query, UseGuards, Request, ParseIntPipe, BadRequestException, } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiResponse, ApiQuery, ApiBearerAuth, ApiParam, } from '@nestjs/swagger'; import { SubscriptionsService } from './subscriptions.service'; import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard'; import { Subscription, SubscriptionList, InvoiceList } from '@customer-portal/shared'; @ApiTags('subscriptions') @Controller('subscriptions') @UseGuards(JwtAuthGuard) @ApiBearerAuth() export class SubscriptionsController { constructor(private readonly subscriptionsService: SubscriptionsService) {} @Get() @ApiOperation({ summary: 'Get all user subscriptions', description: 'Retrieves all subscriptions/services for the authenticated user' }) @ApiQuery({ name: 'status', required: false, type: String, description: 'Filter by subscription status' }) @ApiResponse({ status: 200, description: 'List of user subscriptions', type: Object, // Would be SubscriptionList if we had proper DTO decorators }) async getSubscriptions( @Request() req: any, @Query('status') status?: string, ): Promise { // Validate status if provided if (status && !['Active', 'Suspended', 'Terminated', 'Cancelled', 'Pending'].includes(status)) { throw new BadRequestException('Invalid status filter'); } if (status) { const subscriptions = await this.subscriptionsService.getSubscriptionsByStatus(req.user.id, status); return subscriptions; } return this.subscriptionsService.getSubscriptions(req.user.id); } @Get('active') @ApiOperation({ summary: 'Get active subscriptions only', description: 'Retrieves only active subscriptions for the authenticated user' }) @ApiResponse({ status: 200, description: 'List of active subscriptions', type: [Object], // Would be Subscription[] if we had proper DTO decorators }) async getActiveSubscriptions( @Request() req: any, ): Promise { return this.subscriptionsService.getActiveSubscriptions(req.user.id); } @Get('stats') @ApiOperation({ summary: 'Get subscription statistics', description: 'Retrieves subscription count statistics by status' }) @ApiResponse({ status: 200, description: 'Subscription statistics', type: Object, }) async getSubscriptionStats( @Request() req: any, ): Promise<{ total: number; active: number; suspended: number; cancelled: number; pending: number; }> { return this.subscriptionsService.getSubscriptionStats(req.user.id); } @Get(':id') @ApiOperation({ summary: 'Get subscription details by ID', description: 'Retrieves detailed information for a specific subscription' }) @ApiParam({ name: 'id', type: Number, description: 'Subscription ID' }) @ApiResponse({ status: 200, description: 'Subscription details', type: Object, // Would be Subscription if we had proper DTO decorators }) @ApiResponse({ status: 404, description: 'Subscription not found' }) async getSubscriptionById( @Request() req: any, @Param('id', ParseIntPipe) subscriptionId: number, ): Promise { if (subscriptionId <= 0) { throw new BadRequestException('Subscription ID must be a positive number'); } return this.subscriptionsService.getSubscriptionById(req.user.id, subscriptionId); } @Get(':id/invoices') @ApiOperation({ summary: 'Get invoices for a specific subscription', description: 'Retrieves all invoices related to a specific subscription' }) @ApiParam({ name: 'id', type: Number, description: 'Subscription ID' }) @ApiQuery({ name: 'page', required: false, type: Number, description: 'Page number (default: 1)' }) @ApiQuery({ name: 'limit', required: false, type: Number, description: 'Items per page (default: 10)' }) @ApiResponse({ status: 200, description: 'List of invoices for the subscription', type: Object, // Would be InvoiceList if we had proper DTO decorators }) @ApiResponse({ status: 404, description: 'Subscription not found' }) async getSubscriptionInvoices( @Request() req: any, @Param('id', ParseIntPipe) subscriptionId: number, @Query('page') page?: string, @Query('limit') limit?: string, ): Promise { if (subscriptionId <= 0) { throw new BadRequestException('Subscription ID must be a positive number'); } // Validate and sanitize input const pageNum = this.validatePositiveInteger(page, 1, 'page'); const limitNum = this.validatePositiveInteger(limit, 10, 'limit'); // Limit max page size for performance if (limitNum > 100) { throw new BadRequestException('Limit cannot exceed 100 items per page'); } return this.subscriptionsService.getSubscriptionInvoices( req.user.id, subscriptionId, { page: pageNum, limit: limitNum } ); } private validatePositiveInteger(value: string | undefined, defaultValue: number, fieldName: string): number { if (!value) { return defaultValue; } const parsed = parseInt(value, 10); if (isNaN(parsed) || parsed <= 0) { throw new BadRequestException(`${fieldName} must be a positive integer`); } return parsed; } }