import { type NextRequest, NextResponse } from "next/server"; import { optionalAuth, requireAuth } from "@/lib/auth/middleware"; import { deleteListing, getListingById, updateListing, } from "@/lib/prisma/queries/listings"; import { hasPendingIntention } from "@/lib/prisma/queries/purchase-intentions"; import { secureLogger } from "@/lib/secure-logger"; import { updateListingSchema } from "@/lib/validation-schemas"; /** * GET /api/listings/[id] * Busca anúncio por ID */ export async function GET( request: NextRequest, { params }: { params: Promise<{ id: string }> }, ) { try { const auth = await optionalAuth(request); const { id } = await params; const listingId = parseInt(id, 10); if (Number.isNaN(listingId)) { return NextResponse.json( { success: false, error: "ID de anúncio inválido", }, { status: 400 }, ); } const listing = await getListingById(listingId); if (!listing) { return NextResponse.json( { success: false, error: "Anúncio não encontrado", }, { status: 404 }, ); } const hasPendingForUser = auth?.userId ? await hasPendingIntention(listingId, auth.userId) : false; return NextResponse.json( { success: true, listing: { ...listing, has_pending_intention_for_current_user: hasPendingForUser, }, }, { status: 200 }, ); } catch (error) { secureLogger.error("❌ Erro ao buscar anúncio", { error: error instanceof Error ? error.message : "Erro desconhecido", stack: error instanceof Error ? error.stack : undefined, }); return NextResponse.json( { success: false, error: "Erro ao buscar anúncio", }, { status: 500 }, ); } } /** * PATCH /api/listings/[id] * Atualiza anúncio (apenas dono) */ export async function PATCH( request: NextRequest, { params }: { params: Promise<{ id: string }> }, ) { try { // Autenticação obrigatória const authResult = await requireAuth(request); if (authResult instanceof NextResponse) { return authResult; } const auth = authResult; const { id } = await params; const listingId = parseInt(id, 10); if (Number.isNaN(listingId)) { return NextResponse.json( { success: false, error: "ID de anúncio inválido", }, { status: 400 }, ); } // Verificar se o anúncio existe e pertence ao usuário const listing = await getListingById(listingId); if (!listing) { return NextResponse.json( { success: false, error: "Anúncio não encontrado", }, { status: 404 }, ); } if (listing.seller_id !== auth.userId) { return NextResponse.json( { success: false, error: "Você não tem permissão para editar este anúncio", }, { status: 403 }, ); } const body = await request.json(); // Validação com Zod const validationResult = updateListingSchema.safeParse(body); if (!validationResult.success) { const errors = validationResult.error.issues.map((err) => ({ field: err.path.join("."), message: err.message, })); return NextResponse.json( { success: false, error: "Dados de entrada inválidos", details: errors, }, { status: 400 }, ); } const validatedData = validationResult.data; // Converter null para undefined para compatibilidade com UpdateListingData const updateData: { quantity?: number; price_pix?: number; price_tibia_coins?: number; price_gold_coins?: number; description?: string; listing_status?: | "active" | "sold" | "cancelled" | "in_escrow" | "expired"; current_bid_amount?: number; current_bidder_id?: number; } = { ...(validatedData.quantity !== undefined && { quantity: validatedData.quantity, }), ...(validatedData.price_pix !== null && validatedData.price_pix !== undefined && { price_pix: validatedData.price_pix, }), ...(validatedData.price_tibia_coins !== null && validatedData.price_tibia_coins !== undefined && { price_tibia_coins: validatedData.price_tibia_coins, }), ...(validatedData.price_gold_coins !== null && validatedData.price_gold_coins !== undefined && { price_gold_coins: validatedData.price_gold_coins, }), ...(validatedData.description !== null && validatedData.description !== undefined && { description: validatedData.description, }), ...(validatedData.listing_status !== undefined && { listing_status: validatedData.listing_status, }), }; // Atualizar anúncio const updatedListing = await updateListing(listingId, updateData); if (!updatedListing) { return NextResponse.json( { success: false, error: "Erro ao atualizar anúncio", }, { status: 500 }, ); } secureLogger.info("✅ Anúncio atualizado com sucesso", { listingId, userId: auth.userId, timestamp: new Date().toISOString(), }); return NextResponse.json( { success: true, listing: updatedListing, }, { status: 200 }, ); } catch (error) { secureLogger.error("❌ Erro ao atualizar anúncio", { error: error instanceof Error ? error.message : "Erro desconhecido", stack: error instanceof Error ? error.stack : undefined, }); return NextResponse.json( { success: false, error: "Erro ao atualizar anúncio", }, { status: 500 }, ); } } /** * DELETE /api/listings/[id] * Cancela anúncio (apenas dono) */ export async function DELETE( request: NextRequest, { params }: { params: Promise<{ id: string }> }, ) { try { // Autenticação obrigatória const authResult = await requireAuth(request); if (authResult instanceof NextResponse) { return authResult; } const auth = authResult; const { id } = await params; const listingId = parseInt(id, 10); if (Number.isNaN(listingId)) { return NextResponse.json( { success: false, error: "ID de anúncio inválido", }, { status: 400 }, ); } // Verificar se o anúncio existe e pertence ao usuário const listing = await getListingById(listingId); if (!listing) { return NextResponse.json( { success: false, error: "Anúncio não encontrado", }, { status: 404 }, ); } if (listing.seller_id !== auth.userId) { return NextResponse.json( { success: false, error: "Você não tem permissão para cancelar este anúncio", }, { status: 403 }, ); } // Cancelar anúncio (soft delete) const deleted = await deleteListing(listingId); if (!deleted) { return NextResponse.json( { success: false, error: "Erro ao cancelar anúncio", }, { status: 500 }, ); } secureLogger.info("✅ Anúncio cancelado com sucesso", { listingId, userId: auth.userId, timestamp: new Date().toISOString(), }); return NextResponse.json( { success: true, message: "Anúncio cancelado com sucesso", }, { status: 200 }, ); } catch (error) { secureLogger.error("❌ Erro ao cancelar anúncio", { error: error instanceof Error ? error.message : "Erro desconhecido", stack: error instanceof Error ? error.stack : undefined, }); return NextResponse.json( { success: false, error: "Erro ao cancelar anúncio", }, { status: 500 }, ); } }