import { PrismaClient } from '@prisma/client'; import { getTodayISOString } from './dateUtils'; const prisma = new PrismaClient(); export async function getOrCreateDailyPuzzle(genreName: string | null = null) { try { const today = getTodayISOString(); let genreId: number | null = null; if (genreName) { const genre = await prisma.genre.findUnique({ where: { name: genreName } }); if (genre) { genreId = genre.id; } else { return null; // Genre not found } } let dailyPuzzle = await prisma.dailyPuzzle.findFirst({ where: { date: today, genreId: genreId }, include: { song: true }, }); console.log(`[Daily Puzzle] Date: ${today}, Genre: ${genreName || 'Global'}, Found existing: ${!!dailyPuzzle}`); if (!dailyPuzzle) { // Get songs available for this genre const whereClause = genreId ? { genres: { some: { id: genreId } } } : {}; // Global puzzle picks from ALL songs const allSongs = await prisma.song.findMany({ where: whereClause, include: { puzzles: { where: { genreId: genreId } }, }, }); if (allSongs.length === 0) { console.log(`[Daily Puzzle] No songs available for genre: ${genreName || 'Global'}`); return null; } // Calculate weights const weightedSongs = allSongs.map(song => ({ song, weight: 1.0 / (song.puzzles.length + 1), })); // Calculate total weight const totalWeight = weightedSongs.reduce((sum, item) => sum + item.weight, 0); // Pick a random song based on weights let random = Math.random() * totalWeight; let selectedSong = weightedSongs[0].song; for (const item of weightedSongs) { random -= item.weight; if (random <= 0) { selectedSong = item.song; break; } } // Create the daily puzzle try { dailyPuzzle = await prisma.dailyPuzzle.create({ data: { date: today, songId: selectedSong.id, genreId: genreId }, include: { song: true }, }); console.log(`[Daily Puzzle] Created new puzzle for ${today} (Genre: ${genreName || 'Global'}) with song: ${selectedSong.title}`); } catch (e) { // Handle race condition console.log('[Daily Puzzle] Creation failed, trying to fetch again (likely race condition)'); dailyPuzzle = await prisma.dailyPuzzle.findFirst({ where: { date: today, genreId: genreId }, include: { song: true }, }); } } if (!dailyPuzzle) return null; return { id: dailyPuzzle.id, audioUrl: `/uploads/${dailyPuzzle.song.filename}`, songId: dailyPuzzle.songId, title: dailyPuzzle.song.title, artist: dailyPuzzle.song.artist, coverImage: dailyPuzzle.song.coverImage ? `/uploads/covers/${dailyPuzzle.song.coverImage}` : null, genre: genreName }; } catch (error) { console.error('Error in getOrCreateDailyPuzzle:', error); return null; } } export async function getOrCreateSpecialPuzzle(specialName: string) { try { const today = getTodayISOString(); const special = await prisma.special.findUnique({ where: { name: specialName } }); if (!special) return null; let dailyPuzzle = await prisma.dailyPuzzle.findFirst({ where: { date: today, specialId: special.id }, include: { song: true }, }); if (!dailyPuzzle) { // Get songs available for this special const allSongs = await prisma.song.findMany({ where: { specials: { some: { id: special.id } } }, include: { puzzles: { where: { specialId: special.id } }, }, }); if (allSongs.length === 0) return null; // Calculate weights const weightedSongs = allSongs.map(song => ({ song, weight: 1.0 / (song.puzzles.length + 1), })); const totalWeight = weightedSongs.reduce((sum, item) => sum + item.weight, 0); let random = Math.random() * totalWeight; let selectedSong = weightedSongs[0].song; for (const item of weightedSongs) { random -= item.weight; if (random <= 0) { selectedSong = item.song; break; } } try { dailyPuzzle = await prisma.dailyPuzzle.create({ data: { date: today, songId: selectedSong.id, specialId: special.id }, include: { song: true }, }); } catch (e) { dailyPuzzle = await prisma.dailyPuzzle.findFirst({ where: { date: today, specialId: special.id }, include: { song: true }, }); } } if (!dailyPuzzle) return null; return { id: dailyPuzzle.id, audioUrl: `/uploads/${dailyPuzzle.song.filename}`, songId: dailyPuzzle.songId, title: dailyPuzzle.song.title, artist: dailyPuzzle.song.artist, coverImage: dailyPuzzle.song.coverImage ? `/uploads/covers/${dailyPuzzle.song.coverImage}` : null, special: specialName, maxAttempts: special.maxAttempts, unlockSteps: JSON.parse(special.unlockSteps) }; } catch (error) { console.error('Error in getOrCreateSpecialPuzzle:', error); return null; } }