import { and, asc, desc, eq, gte, isNull, lte, or, type Database, } from "@pkg/db"; import { coupon } from "@pkg/db/schema"; import { getError, Logger } from "@pkg/logger"; import { ERROR_CODES, type Result } from "@pkg/result"; import { couponModel, type CouponModel, type CreateCouponPayload, type UpdateCouponPayload, } from "./data"; export class CouponRepository { private db: Database; constructor(db: Database) { this.db = db; } async getAllCoupons(): Promise> { try { const results = await this.db.query.coupon.findMany({ orderBy: [desc(coupon.createdAt)], }); const out = [] as CouponModel[]; for (const result of results) { const parsed = couponModel.safeParse(result); if (!parsed.success) { Logger.error("Failed to parse coupon"); Logger.error(parsed.error); continue; } out.push(parsed.data); } return { data: out }; } catch (e) { return { error: getError( { code: ERROR_CODES.DATABASE_ERROR, message: "Failed to fetch coupons", detail: "An error occurred while retrieving coupons from the database", userHint: "Please try refreshing the page", actionable: false, }, e, ), }; } } async getCouponById(id: number): Promise> { try { const result = await this.db.query.coupon.findFirst({ where: eq(coupon.id, id), }); if (!result) { return { error: getError({ code: ERROR_CODES.NOT_FOUND_ERROR, message: "Coupon not found", detail: "No coupon exists with the provided ID", userHint: "Please check the coupon ID and try again", actionable: true, }), }; } const parsed = couponModel.safeParse(result); if (!parsed.success) { Logger.error("Failed to parse coupon", result); return { error: getError({ code: ERROR_CODES.INTERNAL_SERVER_ERROR, message: "Failed to parse coupon", userHint: "Please try again", detail: "Failed to parse coupon", }), }; } return { data: parsed.data }; } catch (e) { return { error: getError( { code: ERROR_CODES.DATABASE_ERROR, message: "Failed to fetch coupon", detail: "An error occurred while retrieving the coupon from the database", userHint: "Please try refreshing the page", actionable: false, }, e, ), }; } } async createCoupon(payload: CreateCouponPayload): Promise> { try { // Check if coupon code already exists const existing = await this.db.query.coupon.findFirst({ where: eq(coupon.code, payload.code), }); if (existing) { return { error: getError({ code: ERROR_CODES.DATABASE_ERROR, message: "Coupon code already exists", detail: "A coupon with this code already exists in the system", userHint: "Please use a different coupon code", actionable: true, }), }; } const result = await this.db .insert(coupon) .values({ code: payload.code, description: payload.description || null, discountType: payload.discountType, discountValue: payload.discountValue.toString(), maxUsageCount: payload.maxUsageCount, minOrderValue: payload.minOrderValue ? payload.minOrderValue.toString() : null, maxDiscountAmount: payload.maxDiscountAmount ? payload.maxDiscountAmount.toString() : null, startDate: new Date(payload.startDate), endDate: payload.endDate ? new Date(payload.endDate) : null, isActive: payload.isActive, createdBy: payload.createdBy || null, }) .returning({ id: coupon.id }) .execute(); if (!result || result.length === 0) { throw new Error("Failed to create coupon record"); } return { data: result[0].id }; } catch (e) { return { error: getError( { code: ERROR_CODES.DATABASE_ERROR, message: "Failed to create coupon", detail: "An error occurred while creating the coupon", userHint: "Please try again", actionable: false, }, e, ), }; } } async updateCoupon(payload: UpdateCouponPayload): Promise> { try { if (!payload.id) { return { error: getError({ code: ERROR_CODES.VALIDATION_ERROR, message: "Invalid coupon ID", detail: "No coupon ID was provided for the update operation", userHint: "Please provide a valid coupon ID", actionable: true, }), }; } // Check if coupon exists const existing = await this.db.query.coupon.findFirst({ where: eq(coupon.id, payload.id), }); if (!existing) { return { error: getError({ code: ERROR_CODES.NOT_FOUND_ERROR, message: "Coupon not found", detail: "No coupon exists with the provided ID", userHint: "Please check the coupon ID and try again", actionable: true, }), }; } // If changing the code, check if the new code already exists if (payload.code && payload.code !== existing.code) { const codeExists = await this.db.query.coupon.findFirst({ where: eq(coupon.code, payload.code), }); if (codeExists) { return { error: getError({ code: ERROR_CODES.DATABASE_ERROR, message: "Coupon code already exists", detail: "A coupon with this code already exists in the system", userHint: "Please use a different coupon code", actionable: true, }), }; } } // Build the update object with only the fields that are provided const updateValues: Record = {}; if (payload.code !== undefined) updateValues.code = payload.code; if (payload.description !== undefined) updateValues.description = payload.description; if (payload.discountType !== undefined) updateValues.discountType = payload.discountType; if (payload.discountValue !== undefined) updateValues.discountValue = payload.discountValue.toString(); if (payload.maxUsageCount !== undefined) updateValues.maxUsageCount = payload.maxUsageCount; if (payload.minOrderValue !== undefined) updateValues.minOrderValue = payload.minOrderValue?.toString() || null; if (payload.maxDiscountAmount !== undefined) updateValues.maxDiscountAmount = payload.maxDiscountAmount?.toString() || null; if (payload.startDate !== undefined) updateValues.startDate = new Date(payload.startDate); if (payload.endDate !== undefined) updateValues.endDate = payload.endDate ? new Date(payload.endDate) : null; if (payload.isActive !== undefined) updateValues.isActive = payload.isActive; updateValues.updatedAt = new Date(); await this.db .update(coupon) .set(updateValues) .where(eq(coupon.id, payload.id)) .execute(); return { data: true }; } catch (e) { return { error: getError( { code: ERROR_CODES.DATABASE_ERROR, message: "Failed to update coupon", detail: "An error occurred while updating the coupon", userHint: "Please try again", actionable: false, }, e, ), }; } } async deleteCoupon(id: number): Promise> { try { // Check if coupon exists const existing = await this.db.query.coupon.findFirst({ where: eq(coupon.id, id), }); if (!existing) { return { error: getError({ code: ERROR_CODES.NOT_FOUND_ERROR, message: "Coupon not found", detail: "No coupon exists with the provided ID", userHint: "Please check the coupon ID and try again", actionable: true, }), }; } await this.db.delete(coupon).where(eq(coupon.id, id)).execute(); return { data: true }; } catch (e) { return { error: getError( { code: ERROR_CODES.DATABASE_ERROR, message: "Failed to delete coupon", detail: "An error occurred while deleting the coupon", userHint: "Please try again", actionable: false, }, e, ), }; } } async toggleCouponStatus( id: number, isActive: boolean, ): Promise> { try { // Check if coupon exists const existing = await this.db.query.coupon.findFirst({ where: eq(coupon.id, id), }); if (!existing) { return { error: getError({ code: ERROR_CODES.NOT_FOUND_ERROR, message: "Coupon not found", detail: "No coupon exists with the provided ID", userHint: "Please check the coupon ID and try again", actionable: true, }), }; } await this.db .update(coupon) .set({ isActive, updatedAt: new Date(), }) .where(eq(coupon.id, id)) .execute(); return { data: true }; } catch (e) { return { error: getError( { code: ERROR_CODES.DATABASE_ERROR, message: "Failed to update coupon status", detail: "An error occurred while updating the coupon's status", userHint: "Please try again", actionable: false, }, e, ), }; } } async getCouponByCode(code: string): Promise> { try { const result = await this.db.query.coupon.findFirst({ where: eq(coupon.code, code), }); if (!result) { return { error: getError({ code: ERROR_CODES.NOT_FOUND_ERROR, message: "Coupon not found", detail: "No coupon exists with the provided code", userHint: "Please check the coupon code and try again", actionable: true, }), }; } const parsed = couponModel.safeParse(result); if (!parsed.success) { Logger.error("Failed to parse coupon", result); return { error: getError({ code: ERROR_CODES.INTERNAL_SERVER_ERROR, message: "Failed to parse coupon", userHint: "Please try again", detail: "Failed to parse coupon", }), }; } return { data: parsed.data }; } catch (e) { return { error: getError( { code: ERROR_CODES.DATABASE_ERROR, message: "Failed to fetch coupon", detail: "An error occurred while retrieving the coupon from the database", userHint: "Please try again", actionable: false, }, e, ), }; } } async getActiveCoupons(): Promise> { try { const now = new Date(); const results = await this.db.query.coupon.findMany({ where: and( eq(coupon.isActive, true), lte(coupon.startDate, now), // Either endDate is null (no end date) or it's greater than now or(isNull(coupon.endDate), gte(coupon.endDate, now)), ), orderBy: [asc(coupon.code)], }); const out = [] as CouponModel[]; for (const result of results) { const parsed = couponModel.safeParse(result); if (!parsed.success) { Logger.error("Failed to parse coupon", result); continue; } out.push(parsed.data); } return { data: out }; } catch (e) { return { error: getError( { code: ERROR_CODES.DATABASE_ERROR, message: "Failed to fetch active coupons", detail: "An error occurred while retrieving active coupons from the database", userHint: "Please try refreshing the page", actionable: false, }, e, ), }; } } async getBestActiveCoupon(): Promise> { try { const now = new Date(); // Fetch all active coupons that are currently valid const activeCoupons = await this.db.query.coupon.findMany({ where: and( eq(coupon.isActive, true), lte(coupon.startDate, now), // Either endDate is null (no end date) or it's greater than now or(isNull(coupon.endDate), gte(coupon.endDate, now)), ), orderBy: [ // Order by discount type (PERCENTAGE first) and then by discount value (descending) asc(coupon.discountType), desc(coupon.discountValue), ], }); if (!activeCoupons || activeCoupons.length === 0) { return {}; // No active coupons found } // Get the first (best) coupon const bestCoupon = activeCoupons[0]; // Check if max usage limit is reached if ( bestCoupon.maxUsageCount !== null && bestCoupon.currentUsageCount >= bestCoupon.maxUsageCount ) { return {}; // Coupon usage limit reached } const parsed = couponModel.safeParse(bestCoupon); if (!parsed.success) { Logger.error("Failed to parse coupon", bestCoupon); return {}; // Return null on error, don't break ticket search } return { data: parsed.data }; } catch (e) { Logger.error("Error fetching active coupons", e); return {}; // Return null on error, don't break ticket search } } }