235 lines
5.8 KiB
TypeScript
235 lines
5.8 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server"
|
|
import { prisma } from "@/lib/prisma"
|
|
import { auth } from "@/lib/auth"
|
|
|
|
export async function POST(request: NextRequest) {
|
|
const session = await auth()
|
|
|
|
if (!session) {
|
|
return NextResponse.json({ error: "Unauthorized" }, { status: 401 })
|
|
}
|
|
|
|
try {
|
|
const body = await request.json()
|
|
const {
|
|
eventId,
|
|
team1P1Id,
|
|
team1P2Id,
|
|
team2P1Id,
|
|
team2P2Id,
|
|
team1Score,
|
|
team2Score,
|
|
playedAt,
|
|
} = body
|
|
|
|
// Validate required fields
|
|
if (
|
|
!team1P1Id ||
|
|
!team1P2Id ||
|
|
!team2P1Id ||
|
|
!team2P2Id ||
|
|
team1Score === undefined ||
|
|
team2Score === undefined
|
|
) {
|
|
return NextResponse.json(
|
|
{ error: "Missing required fields" },
|
|
{ status: 400 }
|
|
)
|
|
}
|
|
|
|
// Create the match
|
|
const match = await prisma.match.create({
|
|
data: {
|
|
eventId: eventId || null,
|
|
team1P1Id,
|
|
team1P2Id,
|
|
team2P1Id,
|
|
team2P2Id,
|
|
team1Score,
|
|
team2Score,
|
|
playedAt: playedAt ? new Date(playedAt) : new Date(),
|
|
status: "completed",
|
|
},
|
|
})
|
|
|
|
// Calculate Elo changes for all players
|
|
const players = [
|
|
{ id: team1P1Id },
|
|
{ id: team1P2Id },
|
|
{ id: team2P1Id },
|
|
{ id: team2P2Id },
|
|
]
|
|
|
|
const team1Won = team1Score > team2Score
|
|
const K = 32 // K-factor for Elo calculation
|
|
|
|
for (const player of players) {
|
|
const playerData = await prisma.player.findUnique({
|
|
where: { id: player.id },
|
|
})
|
|
|
|
if (!playerData) continue
|
|
|
|
const isTeam1 = player.id === team1P1Id || player.id === team1P2Id
|
|
const expectedScore = 1 / (1 + Math.pow(10, (1200 - playerData.currentElo) / 400))
|
|
const actualScore = isTeam1 ? (team1Won ? 1 : 0) : (team1Won ? 0 : 1)
|
|
|
|
const ratingChange = Math.round(K * (actualScore - expectedScore))
|
|
const newRating = playerData.currentElo + ratingChange
|
|
|
|
// Update player's current Elo
|
|
await prisma.player.update({
|
|
where: { id: player.id },
|
|
data: { currentElo: newRating },
|
|
})
|
|
|
|
// Create Elo snapshot
|
|
await prisma.eloSnapshot.create({
|
|
data: {
|
|
playerId: player.id,
|
|
matchId: match.id,
|
|
ratingBefore: playerData.currentElo,
|
|
ratingAfter: newRating,
|
|
ratingChange,
|
|
},
|
|
})
|
|
}
|
|
|
|
// Track partnership games
|
|
await prisma.partnershipGame.create({
|
|
data: {
|
|
player1Id: team1P1Id,
|
|
player2Id: team1P2Id,
|
|
matchId: match.id,
|
|
teamNumber: 1,
|
|
wonMatch: team1Won ? 1 : 0,
|
|
},
|
|
})
|
|
|
|
await prisma.partnershipGame.create({
|
|
data: {
|
|
player1Id: team2P1Id,
|
|
player2Id: team2P2Id,
|
|
matchId: match.id,
|
|
teamNumber: 2,
|
|
wonMatch: team1Won ? 0 : 1,
|
|
},
|
|
})
|
|
|
|
// Update partnership statistics
|
|
const partnerships = [
|
|
[team1P1Id, team1P2Id],
|
|
[team2P1Id, team2P2Id],
|
|
]
|
|
|
|
for (const [p1Id, p2Id] of partnerships) {
|
|
// Ensure p1Id < p2Id for consistent lookup
|
|
const [player1Id, player2Id] = p1Id < p2Id ? [p1Id, p2Id] : [p2Id, p1Id]
|
|
|
|
const existingStat = await prisma.partnershipStat.findFirst({
|
|
where: {
|
|
player1Id,
|
|
player2Id,
|
|
},
|
|
})
|
|
|
|
if (existingStat) {
|
|
const won = (player1Id === team1P1Id && player2Id === team1P2Id && team1Won) ||
|
|
(player1Id === team2P1Id && player2Id === team2P2Id && !team1Won)
|
|
|
|
await prisma.partnershipStat.update({
|
|
where: { id: existingStat.id },
|
|
data: {
|
|
gamesPlayed: { increment: 1 },
|
|
wins: { increment: won ? 1 : 0 },
|
|
losses: { increment: won ? 0 : 1 },
|
|
totalEloChange: { increment: 0 }, // Would need to calculate per player
|
|
lastPlayed: new Date(),
|
|
},
|
|
})
|
|
} else {
|
|
const won = (player1Id === team1P1Id && player2Id === team1P2Id && team1Won) ||
|
|
(player1Id === team2P1Id && player2Id === team2P2Id && !team1Won)
|
|
|
|
await prisma.partnershipStat.create({
|
|
data: {
|
|
player1Id,
|
|
player2Id,
|
|
gamesPlayed: 1,
|
|
wins: won ? 1 : 0,
|
|
losses: won ? 0 : 1,
|
|
totalEloChange: 0,
|
|
lastPlayed: new Date(),
|
|
},
|
|
})
|
|
}
|
|
}
|
|
|
|
return NextResponse.json({ success: true, match })
|
|
} catch (error) {
|
|
console.error("Error creating match:", error)
|
|
return NextResponse.json(
|
|
{ error: "Failed to create match" },
|
|
{ status: 500 }
|
|
)
|
|
}
|
|
}
|
|
|
|
export async function GET(request: NextRequest) {
|
|
const session = await auth()
|
|
|
|
if (!session) {
|
|
return NextResponse.json({ error: "Unauthorized" }, { status: 401 })
|
|
}
|
|
|
|
try {
|
|
const { searchParams } = new URL(request.url)
|
|
const playerId = searchParams.get("playerId")
|
|
|
|
let matches
|
|
|
|
if (playerId) {
|
|
const id = parseInt(playerId)
|
|
matches = await prisma.match.findMany({
|
|
where: {
|
|
OR: [
|
|
{ team1P1Id: id },
|
|
{ team1P2Id: id },
|
|
{ team2P1Id: id },
|
|
{ team2P2Id: id },
|
|
],
|
|
},
|
|
include: {
|
|
event: true,
|
|
team1P1: true,
|
|
team1P2: true,
|
|
team2P1: true,
|
|
team2P2: true,
|
|
},
|
|
orderBy: { playedAt: "desc" },
|
|
take: 50,
|
|
})
|
|
} else {
|
|
matches = await prisma.match.findMany({
|
|
include: {
|
|
event: true,
|
|
team1P1: true,
|
|
team1P2: true,
|
|
team2P1: true,
|
|
team2P2: true,
|
|
},
|
|
orderBy: { playedAt: "desc" },
|
|
take: 50,
|
|
})
|
|
}
|
|
|
|
return NextResponse.json({ matches })
|
|
} catch (error) {
|
|
console.error("Error fetching matches:", error)
|
|
return NextResponse.json(
|
|
{ error: "Failed to fetch matches" },
|
|
{ status: 500 }
|
|
)
|
|
}
|
|
}
|