import { NextResponse } from "next/server"; import { getSupabaseAdmin } from "@/lib/admin"; import { requireAdmin } from "@/lib/apiAuth"; import type { CreditTransaction } from "@/types/billing"; export async function GET(request: Request) { const authResult = await requireAdmin(request); if (authResult instanceof NextResponse) return authResult; const supabase = getSupabaseAdmin(); const { searchParams } = new URL(request.url); const orgId = searchParams.get("orgId"); const page = parseInt(searchParams.get("page") || "1"); const limit = parseInt(searchParams.get("limit") || "20"); const offset = (page - 1) * limit; try { if (orgId) { // Get specific org's credit balance and transaction history const [orgResult, transactionsResult] = await Promise.all([ supabase .from("organizations") .select("id, name, credit_balance") .eq("id", orgId) .single(), supabase .from("credit_transactions") .select( "*, creator:users!credit_transactions_created_by_fkey(id, email, name)", { count: "exact" } ) .eq("organization_id", orgId) .order("created_at", { ascending: false }) .range(offset, offset + limit - 1), ]); if (orgResult.error) throw orgResult.error; return NextResponse.json({ organization: orgResult.data, transactions: (transactionsResult.data || []) as unknown as CreditTransaction[], pagination: { page, limit, total: transactionsResult.count || 0, pages: Math.ceil((transactionsResult.count || 0) / limit), }, }); } // List all organizations with credit balances const { data: orgs, count, error } = await supabase .from("organizations") .select("id, name, credit_balance, subscription_tier", { count: "exact" }) .order("credit_balance", { ascending: false }) .range(offset, offset + limit - 1); if (error) throw error; return NextResponse.json({ organizations: orgs || [], pagination: { page, limit, total: count || 0, pages: Math.ceil((count || 0) / limit) }, }); } catch (error) { console.error("Error fetching credits:", error); return NextResponse.json({ error: "Failed to fetch credits" }, { status: 500 }); } } export async function POST(request: Request) { const authResult = await requireAdmin(request); if (authResult instanceof NextResponse) return authResult; const supabase = getSupabaseAdmin(); try { const body = await request.json(); const { organization_id, amount, type, reason, notes } = body; if (!organization_id || amount === undefined || !type || !reason) { return NextResponse.json( { error: "organization_id, amount, type, and reason are required" }, { status: 400 } ); } if (!["credit", "debit"].includes(type)) { return NextResponse.json({ error: "type must be 'credit' or 'debit'" }, { status: 400 }); } // Get current balance const { data: org, error: orgError } = await supabase .from("organizations") .select("id, name, credit_balance") .eq("id", organization_id) .single(); if (orgError || !org) { return NextResponse.json({ error: "Organization not found" }, { status: 404 }); } const absAmount = Math.abs(Math.round(amount)); const signedAmount = type === "credit" ? absAmount : -absAmount; const currentBalance = typeof org.credit_balance === "number" ? org.credit_balance : 0; const newBalance = currentBalance + signedAmount; if (newBalance < 0) { return NextResponse.json( { error: `Insufficient balance. Current: ${currentBalance}, debit: ${absAmount}` }, { status: 400 } ); } // Insert transaction and update balance atomically const { data, error: txError } = await supabase .from("credit_transactions") .insert({ organization_id, amount: signedAmount, balance_after: newBalance, type, reason, notes, created_by: authResult.userId, }) .select() .single(); if (txError) throw txError; const transaction = data as unknown as CreditTransaction; const { error: updateError } = await supabase .from("organizations") .update({ credit_balance: newBalance }) .eq("id", organization_id); if (updateError) throw updateError; return NextResponse.json({ transaction, new_balance: newBalance, organization: { id: org.id, name: org.name }, }); } catch (error) { console.error("Error processing credit transaction:", error); return NextResponse.json({ error: "Failed to process credit transaction" }, { status: 500 }); } }