/** * Queries de Purchase Intentions usando Prisma */ import { prisma } from "../app"; import { Prisma } from "@prisma/client"; export type PurchaseIntentionStatus = "pending" | "accepted" | "rejected" | "expired" | "completed"; export type PaymentMethod = "pix" | "tibia_coins" | "gold_coins" | "trade"; export interface CreatePurchaseIntentionData { listing_id: number; buyer_id: number; payment_method: PaymentMethod; buyer_message?: string; expires_at?: Date; custom_offer_amount?: number | null; custom_offer_currency?: PaymentMethod | null; } export interface PurchaseIntention { intention_id: number; listing_id: number; buyer_id: number; payment_method: string; payment_method_change_count: number; custom_offer_amount: number | null; custom_offer_currency: string | null; custom_offer_created_at: Date | null; status: PurchaseIntentionStatus; buyer_message: string | null; seller_response: string | null; created_at: Date; updated_at: Date; expires_at: Date | null; accepted_at: Date | null; rejected_at: Date | null; // Relacionamentos listing?: { listing_id: number; item_name: string; seller_id: number; }; buyer?: { user_id: number; username: string; email: string; }; } function serializePurchaseIntention(intention: any): PurchaseIntention { if (!intention) return intention; return { ...intention, custom_offer_amount: intention.custom_offer_amount !== null && intention.custom_offer_amount !== undefined ? Number(intention.custom_offer_amount) : null, custom_offer_currency: intention.custom_offer_currency ?? null, custom_offer_created_at: intention.custom_offer_created_at ?? null, }; } /** * Cria uma nova intenção de compra */ export async function createPurchaseIntention( data: CreatePurchaseIntentionData ): Promise { const result = await prisma.purchaseIntention.create({ data: { listing_id: data.listing_id, buyer_id: data.buyer_id, payment_method: data.payment_method, buyer_message: data.buyer_message || null, expires_at: data.expires_at || null, status: "pending", custom_offer_amount: data.custom_offer_amount !== undefined && data.custom_offer_amount !== null ? new Prisma.Decimal(data.custom_offer_amount) : null, custom_offer_currency: data.custom_offer_currency || null, custom_offer_created_at: data.custom_offer_amount !== undefined && data.custom_offer_amount !== null ? new Date() : null, }, include: { listing: { select: { listing_id: true, item_name: true, seller_id: true, }, }, buyer: { select: { user_id: true, username: true, email: true, }, }, }, }); return serializePurchaseIntention(result); } /** * Busca intenções de um anúncio específico */ export async function getPurchaseIntentionsByListing( listingId: number, filters: { status?: PurchaseIntentionStatus; limit?: number } = {} ): Promise { const { status, limit = 50 } = filters; const where: any = { listing_id: listingId }; if (status) { where.status = status; } const intentions = await prisma.purchaseIntention.findMany({ where, include: { listing: { select: { listing_id: true, item_name: true, seller_id: true, }, }, buyer: { select: { user_id: true, username: true, email: true, }, }, }, orderBy: { created_at: "desc" }, take: limit, }); return intentions.map(serializePurchaseIntention); } /** * Busca intenções de um comprador */ export async function getPurchaseIntentionsByBuyer( buyerId: number, filters: { status?: PurchaseIntentionStatus; limit?: number } = {} ): Promise { const { status, limit = 50 } = filters; const where: any = { buyer_id: buyerId }; if (status) { where.status = status; } const intentions = await prisma.purchaseIntention.findMany({ where, include: { listing: { select: { listing_id: true, item_name: true, seller_id: true, }, }, buyer: { select: { user_id: true, username: true, email: true, }, }, }, orderBy: { created_at: "desc" }, take: limit, }); return intentions.map(serializePurchaseIntention); } /** * Busca uma intenção específica por ID */ export async function getPurchaseIntentionById( intentionId: number ): Promise { const intention = await prisma.purchaseIntention.findUnique({ where: { intention_id: intentionId }, include: { listing: { select: { listing_id: true, item_name: true, seller_id: true, }, }, buyer: { select: { user_id: true, username: true, email: true, }, }, }, }); return serializePurchaseIntention(intention); } /** * Atualiza o status de uma intenção */ export async function updatePurchaseIntentionStatus( intentionId: number, status: PurchaseIntentionStatus, sellerResponse?: string ): Promise { const updateData: any = { status, updated_at: new Date(), }; if (status === "accepted") { updateData.accepted_at = new Date(); updateData.seller_response = sellerResponse || null; } else if (status === "rejected") { updateData.rejected_at = new Date(); updateData.seller_response = sellerResponse || null; } else if (sellerResponse) { updateData.seller_response = sellerResponse; } const updated = await prisma.purchaseIntention.update({ where: { intention_id: intentionId }, data: updateData, include: { listing: { select: { listing_id: true, item_name: true, seller_id: true, }, }, buyer: { select: { user_id: true, username: true, email: true, }, }, }, }); return serializePurchaseIntention(updated); } /** * Conta intenções pendentes de um anúncio */ export async function getPendingIntentionsCount(listingId: number): Promise { return await prisma.purchaseIntention.count({ where: { listing_id: listingId, status: "pending", }, }); } /** * Verifica se já existe uma intenção pendente para um listing+buyer */ export async function hasPendingIntention( listingId: number, buyerId: number ): Promise { const count = await prisma.purchaseIntention.count({ where: { listing_id: listingId, buyer_id: buyerId, status: "pending", }, }); return count > 0; } /** * Busca intenção pendente de um comprador para um listing específico */ export async function getPendingIntentionByBuyerAndListing( listingId: number, buyerId: number ): Promise { const intention = await prisma.purchaseIntention.findFirst({ where: { listing_id: listingId, buyer_id: buyerId, status: "pending", }, include: { listing: { select: { listing_id: true, item_name: true, seller_id: true, }, }, buyer: { select: { user_id: true, username: true, email: true, }, }, }, orderBy: { created_at: "desc", }, }); return serializePurchaseIntention(intention); } /** * Atualiza o método de pagamento de uma intenção */ export async function updatePurchaseIntentionPaymentMethod( intentionId: number, newPaymentMethod: PaymentMethod ): Promise { // Buscar intenção atual const intention = await prisma.purchaseIntention.findUnique({ where: { intention_id: intentionId }, }); if (!intention) { throw new Error("Intenção não encontrada"); } // Verificar se pode trocar (máximo 1 troca) if (intention.payment_method_change_count >= 1) { throw new Error("Você já atingiu o limite de trocas de método de pagamento"); } // Verificar se o novo método é diferente if (intention.payment_method === newPaymentMethod) { throw new Error("O novo método de pagamento deve ser diferente do atual"); } // Atualizar const updated = await prisma.purchaseIntention.update({ where: { intention_id: intentionId }, data: { payment_method: newPaymentMethod, payment_method_change_count: intention.payment_method_change_count + 1, updated_at: new Date(), }, include: { listing: { select: { listing_id: true, item_name: true, seller_id: true, }, }, buyer: { select: { user_id: true, username: true, email: true, }, }, }, }); return serializePurchaseIntention(updated); } /** * Retorna os IDs de anúncios com intenções pendentes para um comprador */ export async function getPendingIntentionListingIdsForBuyer( buyerId: number, listingIds: number[] ): Promise { if (listingIds.length === 0) { return []; } const intentions = await prisma.purchaseIntention.findMany({ where: { buyer_id: buyerId, status: "pending", listing_id: { in: listingIds, }, }, select: { listing_id: true, }, }); const uniqueIds = new Set(); intentions.forEach((intention) => { uniqueIds.add(intention.listing_id); }); return Array.from(uniqueIds); } export async function getHighestCustomOfferForListing( listingId: number ): Promise<{ amount: number; currency: string } | null> { const offer = await prisma.purchaseIntention.findFirst({ where: { listing_id: listingId, status: { in: ["pending", "accepted"] }, custom_offer_amount: { not: null }, custom_offer_currency: { not: null }, }, orderBy: { custom_offer_amount: "desc" }, select: { custom_offer_amount: true, custom_offer_currency: true, }, }); if (!offer) { return null; } return { amount: Number(offer.custom_offer_amount), currency: offer.custom_offer_currency!, }; } export async function getHighestCustomOffersForListings( listingIds: number[] ): Promise> { if (listingIds.length === 0) { return new Map(); } const offers = await prisma.purchaseIntention.findMany({ where: { listing_id: { in: listingIds }, status: { in: ["pending", "accepted"] }, custom_offer_amount: { not: null }, custom_offer_currency: { not: null }, }, select: { listing_id: true, custom_offer_amount: true, custom_offer_currency: true, }, orderBy: [ { listing_id: "asc" }, { custom_offer_amount: "desc" }, ], }); const result = new Map(); for (const offer of offers) { if (!result.has(offer.listing_id)) { result.set(offer.listing_id, { amount: Number(offer.custom_offer_amount), currency: offer.custom_offer_currency!, }); } } return result; }