import { desc, eq, type Database } from "@pkg/db"; import { product } from "@pkg/db/schema"; import { getError, Logger } from "@pkg/logger"; import { ERROR_CODES, type Result } from "@pkg/result"; import { nanoid } from "nanoid"; import { productModel, type CreateProductPayload, type ProductModel, type UpdateProductPayload, } from "./data"; export class ProductRepository { private db: Database; constructor(db: Database) { this.db = db; } async listAllProducts(): Promise> { try { const results = await this.db.query.product.findMany({ orderBy: [desc(product.createdAt)], }); const out = [] as ProductModel[]; for (const result of results) { const parsed = productModel.safeParse(result); if (!parsed.success) { Logger.error("Failed to parse product"); 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 products", detail: "An error occurred while retrieving products from the database", userHint: "Please try refreshing the page", actionable: false, }, e, ), }; } } async getProductById(id: number | string): Promise> { try { const result = await this.db.query.product.findFirst({ where: typeof id === "number" ? eq(product.id, id) : eq(product.linkId, id), }); if (!result) { return { error: getError({ code: ERROR_CODES.NOT_FOUND_ERROR, message: "Product not found", detail: "No product exists with the provided ID", userHint: "Please check the product ID and try again", actionable: true, }), }; } const parsed = productModel.safeParse(result); if (!parsed.success) { Logger.error("Failed to parse product", result); return { error: getError({ code: ERROR_CODES.INTERNAL_SERVER_ERROR, message: "Failed to parse product", userHint: "Please try again", detail: "Failed to parse product", }), }; } return { data: parsed.data }; } catch (e) { return { error: getError( { code: ERROR_CODES.DATABASE_ERROR, message: "Failed to fetch product", detail: "An error occurred while retrieving the product from the database", userHint: "Please try refreshing the page", actionable: false, }, e, ), }; } } async createProduct(payload: CreateProductPayload): Promise> { try { // Generate a unique linkId const linkId = nanoid(16); const result = await this.db .insert(product) .values({ linkId, title: payload.title, description: payload.description, longDescription: payload.longDescription, price: payload.price.toString(), discountPrice: payload.discountPrice.toString(), }) .returning({ id: product.id }) .execute(); if (!result || result.length === 0) { throw new Error("Failed to create product record"); } return { data: result[0].id }; } catch (e) { return { error: getError( { code: ERROR_CODES.DATABASE_ERROR, message: "Failed to create product", detail: "An error occurred while creating the product", userHint: "Please try again", actionable: false, }, e, ), }; } } async updateProduct(payload: UpdateProductPayload): Promise> { try { if (!payload.id) { return { error: getError({ code: ERROR_CODES.VALIDATION_ERROR, message: "Invalid product ID", detail: "No product ID was provided for the update operation", userHint: "Please provide a valid product ID", actionable: true, }), }; } // Check if product exists const existing = await this.db.query.product.findFirst({ where: eq(product.id, payload.id), }); if (!existing) { return { error: getError({ code: ERROR_CODES.NOT_FOUND_ERROR, message: "Product not found", detail: "No product exists with the provided ID", userHint: "Please check the product ID and try again", actionable: true, }), }; } // Build the update object with only the fields that are provided const updateValues: Record = {}; if (payload.title !== undefined) updateValues.title = payload.title; if (payload.description !== undefined) updateValues.description = payload.description; if (payload.longDescription !== undefined) updateValues.longDescription = payload.longDescription; if (payload.price !== undefined) updateValues.price = payload.price.toString(); if (payload.discountPrice !== undefined) updateValues.discountPrice = payload.discountPrice.toString(); updateValues.updatedAt = new Date(); await this.db .update(product) .set(updateValues) .where(eq(product.id, payload.id)) .execute(); return { data: true }; } catch (e) { return { error: getError( { code: ERROR_CODES.DATABASE_ERROR, message: "Failed to update product", detail: "An error occurred while updating the product", userHint: "Please try again", actionable: false, }, e, ), }; } } async deleteProduct(id: number): Promise> { try { // Check if product exists const existing = await this.db.query.product.findFirst({ where: eq(product.id, id), }); if (!existing) { return { error: getError({ code: ERROR_CODES.NOT_FOUND_ERROR, message: "Product not found", detail: "No product exists with the provided ID", userHint: "Please check the product ID and try again", actionable: true, }), }; } await this.db.delete(product).where(eq(product.id, id)).execute(); return { data: true }; } catch (e) { return { error: getError( { code: ERROR_CODES.DATABASE_ERROR, message: "Failed to delete product", detail: "An error occurred while deleting the product", userHint: "Please try again", actionable: false, }, e, ), }; } } async refreshProductLinkId(id: number): Promise> { try { // Check if product exists const existing = await this.db.query.product.findFirst({ where: eq(product.id, id), }); if (!existing) { return { error: getError({ code: ERROR_CODES.NOT_FOUND_ERROR, message: "Product not found", detail: "No product exists with the provided ID", userHint: "Please check the product ID and try again", actionable: true, }), }; } // Generate a new unique linkId const newLinkId = nanoid(16); await this.db .update(product) .set({ linkId: newLinkId, updatedAt: new Date(), }) .where(eq(product.id, id)) .execute(); return { data: newLinkId }; } catch (e) { return { error: getError( { code: ERROR_CODES.DATABASE_ERROR, message: "Failed to refresh product link ID", detail: "An error occurred while refreshing the product's link ID", userHint: "Please try again", actionable: false, }, e, ), }; } } }