const { Pool } = require('pg'); const jwt = require('jsonwebtoken'); const pool = new Pool({ connectionString: process.env.DATABASE_URL, ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false, }); // Middleware to verify user authentication function verifyAuth(req) { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { throw new Error('No token provided'); } const token = authHeader.substring(7); const jwtSecret = process.env.JWT_SECRET || 'fallback-secret-change-in-production'; try { const decoded = jwt.verify(token, jwtSecret); return decoded; } catch (error) { throw new Error('Invalid token'); } } export default async function handler(req, res) { if (req.method !== 'POST') { return res.status(405).json({ error: 'Method not allowed' }); } const client = await pool.connect(); try { // Verify authentication const user = verifyAuth(req); const { name, game, setName = null, setCode = null, rarity = null, cardType = null, manaCost = null, ocrConfidence = null, ocrRawText = null, imageUrl = null } = req.body; // Validate required fields if (!name || !game) { return res.status(400).json({ error: 'Card name and game are required' }); } // Try to find existing card first let searchQuery = ` SELECT id, name, set_name, set_code, rarity, game, card_type, mana_cost, current_price, stock_image_url, image_url, verified FROM cards WHERE LOWER(name) = LOWER($1) AND UPPER(game) = UPPER($2) `; let searchParams = [name.trim(), game.trim()]; // If set name is provided, try to match more specifically if (setName) { searchQuery += ` AND (set_name IS NULL OR LOWER(set_name) = LOWER($3))`; searchParams.push(setName.trim()); } searchQuery += ` ORDER BY verified DESC, created_at DESC LIMIT 1`; const existingCard = await client.query(searchQuery, searchParams); if (existingCard.rows.length > 0) { // Card found, return it const card = existingCard.rows[0]; res.status(200).json({ success: true, found: true, message: 'Card found in database', card: { id: card.id, name: card.name, setName: card.set_name, setCode: card.set_code, rarity: card.rarity, game: card.game, cardType: card.card_type, manaCost: card.mana_cost, currentPrice: card.current_price, stockImageUrl: card.stock_image_url, imageUrl: card.image_url, verified: card.verified } }); } else { // Card not found, create new one const insertQuery = ` INSERT INTO cards ( name, game, set_name, set_code, rarity, card_type, mana_cost, ocr_confidence, ocr_raw_text, image_url, verified, created_at, updated_at ) VALUES ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP ) RETURNING id, name, set_name, set_code, rarity, game, card_type, mana_cost, ocr_confidence, ocr_raw_text, image_url, verified, created_at `; const insertParams = [ name.trim(), game.trim().toUpperCase(), setName?.trim() || null, setCode?.trim() || null, rarity?.trim() || null, cardType?.trim() || null, manaCost?.trim() || null, ocrConfidence || null, ocrRawText?.trim() || null, imageUrl?.trim() || null, false // New cards from OCR are unverified by default ]; const newCard = await client.query(insertQuery, insertParams); const card = newCard.rows[0]; res.status(201).json({ success: true, found: false, message: 'New card created from scan data', card: { id: card.id, name: card.name, setName: card.set_name, setCode: card.set_code, rarity: card.rarity, game: card.game, cardType: card.card_type, manaCost: card.mana_cost, currentPrice: null, stockImageUrl: null, imageUrl: card.image_url, verified: card.verified, ocrConfidence: card.ocr_confidence, ocrRawText: card.ocr_raw_text, createdAt: card.created_at } }); } } catch (error) { console.error('Find or create card error:', error); if (error.message === 'No token provided' || error.message === 'Invalid token') { res.status(401).json({ error: error.message }); } else { res.status(500).json({ error: 'Internal server error', details: error.message }); } } finally { client.release(); } }