import { sql } from '@vercel/postgres'; const EXCLUDED_MTG_SET_TYPES = new Set(['token', 'memorabilia', 'funny', 'treasure_chest']); /** * Pure filter: Scryfall set objects not yet present in knownCodes (lowercase set codes). */ export function filterMissingMtgSets(scryfallSets, knownCodes, now = new Date()) { const missing = []; for (const set of scryfallSets) { if (!set?.code || set.digital) continue; if (!set.released_at) continue; if (new Date(set.released_at) > now) continue; if (EXCLUDED_MTG_SET_TYPES.has(set.set_type)) continue; const code = set.code.toLowerCase(); if (knownCodes.has(code)) continue; missing.push({ code: set.code, name: set.name, releasedAt: set.released_at, }); } missing.sort((a, b) => b.releasedAt.localeCompare(a.releasedAt)); return missing; } /** * Pure filter: Pokémon TCG set objects not yet present in knownCodes (lowercase set ids). */ export function filterMissingPokemonSets(pokemonSets, knownCodes) { const missing = []; for (const set of pokemonSets) { if (!set?.id) continue; const code = set.id.toLowerCase(); if (knownCodes.has(code)) continue; missing.push({ id: set.id, name: set.name, releasedAt: set.releaseDate || null, }); } missing.sort((a, b) => { const dateA = a.releasedAt || ''; const dateB = b.releasedAt || ''; if (!dateA && !dateB) return 0; if (!dateA) return 1; if (!dateB) return -1; return dateB.localeCompare(dateA); }); return missing; } export async function getKnownMtgSetCodes() { const { rows } = await sql` SELECT DISTINCT LOWER(set_code) AS set_code FROM cards WHERE game = 'MTG' AND set_code IS NOT NULL `; return new Set(rows.map((row) => row.set_code)); } export async function getKnownPokemonSetCodes() { const { rows } = await sql` SELECT DISTINCT LOWER(set_code) AS set_code FROM cards WHERE game = 'Pokemon' AND set_code IS NOT NULL `; return new Set(rows.map((row) => row.set_code)); } export async function discoverMissingMtgSets() { const response = await fetch('https://api.scryfall.com/sets'); if (!response.ok) { throw new Error(`Scryfall sets API error: ${response.status}`); } const data = await response.json(); const knownCodes = await getKnownMtgSetCodes(); return filterMissingMtgSets(data.data || [], knownCodes); } export async function discoverMissingPokemonSets() { const { fetchWithRetry, pokemonHeaders } = await import('./pokemon.js'); const response = await fetchWithRetry('https://api.pokemontcg.io/v2/sets'); const data = await response.json(); const knownCodes = await getKnownPokemonSetCodes(); return filterMissingPokemonSets(data.data || [], knownCodes); }