import { NextRequest, NextResponse } from 'next/server'; import { getServerSession } from 'next-auth'; import { authOptions } from '@/lib/auth'; import { db } from '@/lib/db'; import { z } from 'zod'; import { Prisma } from '@prisma/client'; interface RouteContext { params: Promise<{ id: string }>; } // Validation schema for updating a membership plan const updateMembershipPlanSchema = z.object({ name: z.string().min(1).max(100).optional(), description: z.string().max(500).optional().nullable(), price: z.number().nonnegative().optional(), durationMonths: z.number().int().min(1).optional(), freeHours: z.number().int().min(0).optional().nullable(), bookingDiscount: z.number().min(0).max(100).optional().nullable(), storeDiscount: z.number().min(0).max(100).optional().nullable(), extraBenefits: z.array(z.string()).optional(), isActive: z.boolean().optional(), }); // GET /api/membership-plans/[id] - Get plan details with subscriber count export async function GET( request: NextRequest, context: RouteContext ) { try { const session = await getServerSession(authOptions); if (!session?.user) { return NextResponse.json( { error: 'Unauthorized' }, { status: 401 } ); } const { id } = await context.params; const plan = await db.membershipPlan.findFirst({ where: { id, organizationId: session.user.organizationId, }, include: { memberships: { where: { status: 'ACTIVE', }, include: { client: { select: { id: true, firstName: true, lastName: true, email: true, }, }, }, orderBy: { endDate: 'asc', }, }, _count: { select: { memberships: true, }, }, }, }); if (!plan) { return NextResponse.json( { error: 'Membership plan not found' }, { status: 404 } ); } // Calculate statistics const now = new Date(); const sevenDaysFromNow = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000); const activeMemberships = plan.memberships.filter(m => m.status === 'ACTIVE'); const expiringMemberships = activeMemberships.filter( m => m.endDate <= sevenDaysFromNow && m.endDate > now ); // Transform response const planWithDetails = { ...plan, subscriberCount: activeMemberships.length, totalSubscriptions: plan._count.memberships, expiringCount: expiringMemberships.length, benefitsSummary: { freeHours: plan.courtHours || 0, bookingDiscount: plan.discountPercent ? Number(plan.discountPercent) : 0, extraBenefits: plan.benefits || [], }, activeSubscribers: activeMemberships.map(m => ({ membershipId: m.id, client: m.client, startDate: m.startDate, endDate: m.endDate, remainingHours: m.remainingHours, isExpiring: m.endDate <= sevenDaysFromNow, })), }; return NextResponse.json(planWithDetails); } catch (error) { console.error('Error fetching membership plan:', error); return NextResponse.json( { error: 'Failed to fetch membership plan' }, { status: 500 } ); } } // PUT /api/membership-plans/[id] - Update plan export async function PUT( request: NextRequest, context: RouteContext ) { try { const session = await getServerSession(authOptions); if (!session?.user) { return NextResponse.json( { error: 'Unauthorized' }, { status: 401 } ); } // Check if user has admin role const allowedRoles = ['SUPER_ADMIN', 'ORG_ADMIN']; if (!allowedRoles.includes(session.user.role)) { return NextResponse.json( { error: 'Forbidden: Insufficient permissions' }, { status: 403 } ); } const { id } = await context.params; // Verify plan exists and belongs to user's organization const existingPlan = await db.membershipPlan.findFirst({ where: { id, organizationId: session.user.organizationId, }, }); if (!existingPlan) { return NextResponse.json( { error: 'Membership plan not found' }, { status: 404 } ); } const body = await request.json(); // Validate input const validationResult = updateMembershipPlanSchema.safeParse(body); if (!validationResult.success) { return NextResponse.json( { error: 'Invalid membership plan data', details: validationResult.error.flatten().fieldErrors, }, { status: 400 } ); } const { name, description, price, durationMonths, freeHours, bookingDiscount, storeDiscount, extraBenefits, isActive, } = validationResult.data; // Build update data const updateData: Prisma.MembershipPlanUpdateInput = {}; if (name !== undefined) updateData.name = name; if (description !== undefined) updateData.description = description; if (price !== undefined) updateData.price = price; if (durationMonths !== undefined) updateData.durationMonths = durationMonths; if (freeHours !== undefined) updateData.courtHours = freeHours; if (bookingDiscount !== undefined) updateData.discountPercent = bookingDiscount; if (isActive !== undefined) updateData.isActive = isActive; // Update benefits array if provided if (extraBenefits !== undefined || storeDiscount !== undefined) { const currentBenefits = existingPlan.benefits || []; // Remove old store discount entries const filteredBenefits = currentBenefits.filter( b => !b.includes('store discount') ); updateData.benefits = [ ...(extraBenefits !== undefined ? extraBenefits : filteredBenefits), ...(storeDiscount ? [`${storeDiscount}% store discount`] : []), ]; } const plan = await db.membershipPlan.update({ where: { id }, data: updateData, include: { _count: { select: { memberships: { where: { status: 'ACTIVE', }, }, }, }, }, }); // Transform response const planWithSummary = { ...plan, subscriberCount: plan._count.memberships, benefitsSummary: { freeHours: plan.courtHours || 0, bookingDiscount: plan.discountPercent ? Number(plan.discountPercent) : 0, extraBenefits: plan.benefits || [], }, }; return NextResponse.json(planWithSummary); } catch (error) { console.error('Error updating membership plan:', error); return NextResponse.json( { error: 'Failed to update membership plan' }, { status: 500 } ); } } // DELETE /api/membership-plans/[id] - Soft delete (set isActive = false) export async function DELETE( request: NextRequest, context: RouteContext ) { try { const session = await getServerSession(authOptions); if (!session?.user) { return NextResponse.json( { error: 'Unauthorized' }, { status: 401 } ); } // Check if user has admin role const allowedRoles = ['SUPER_ADMIN', 'ORG_ADMIN']; if (!allowedRoles.includes(session.user.role)) { return NextResponse.json( { error: 'Forbidden: Insufficient permissions' }, { status: 403 } ); } const { id } = await context.params; // Verify plan exists and belongs to user's organization const existingPlan = await db.membershipPlan.findFirst({ where: { id, organizationId: session.user.organizationId, }, include: { _count: { select: { memberships: { where: { status: 'ACTIVE', }, }, }, }, }, }); if (!existingPlan) { return NextResponse.json( { error: 'Membership plan not found' }, { status: 404 } ); } // Warn if there are active subscriptions const activeCount = existingPlan._count.memberships; // Soft delete - set isActive to false await db.membershipPlan.update({ where: { id }, data: { isActive: false, }, }); return NextResponse.json({ message: 'Membership plan deactivated successfully', activeSubscriptionsAffected: activeCount, note: activeCount > 0 ? 'This plan still has active subscriptions that will remain valid until expiration' : undefined, }); } catch (error) { console.error('Error deactivating membership plan:', error); return NextResponse.json( { error: 'Failed to deactivate membership plan' }, { status: 500 } ); } }