import { getUserFromRequest } from '../../../lib/permission-middleware'; import { checkScanRateLimit } from '../../../lib/rate-limit.js'; import { analyzeCardImage } from '../../../lib/scan-vision.js'; import { submitScanForReview, logScanAttempt } from '../../../lib/card-catalog-match.js'; export default async function handler(req, res) { if (req.method !== 'POST') { return res.status(405).json({ error: 'Method not allowed' }); } const startedAt = Date.now(); try { const user = await getUserFromRequest(req); if (!user) { return res.status(401).json({ error: 'Authentication required' }); } const { allowed, reset } = await checkScanRateLimit(req, user.userId); if (!allowed) { res.setHeader('Retry-After', Math.ceil((reset - Date.now()) / 1000)); return res.status(429).json({ error: 'Too many attempts. Try again later.' }); } const { imageData, name, game, candidateCardIds = [], } = req.body || {}; let fields = { name: typeof name === 'string' ? name.trim() : null, game: game || null, ocrData: { confidence: 65, rawText: name || null, }, }; if (imageData && typeof imageData === 'string' && imageData.length <= 6_000_000) { try { const ocrResult = await analyzeCardImage(imageData); if (ocrResult.isCard || ocrResult.cardName) { fields = { name: ocrResult.cardName || fields.name, set: ocrResult.setName || null, setCode: ocrResult.setCode || null, cardNumber: ocrResult.cardNumber || null, game: game || ocrResult.game || null, cardType: ocrResult.cardType || null, rarity: ocrResult.rarity || null, hp: ocrResult.hp || null, manaCost: ocrResult.manaCost || null, ocrData: { confidence: ocrResult.confidence || 65, rawText: ocrResult.rawText || name || null, abilities: ocrResult.abilities || [], }, }; } } catch (visionError) { console.warn('[submit-for-review] Vision failed, using name hint:', visionError.message); } } if (!fields.name) { return res.status(400).json({ error: 'Card name is required to submit for review' }); } const candidateIds = Array.isArray(candidateCardIds) ? candidateCardIds.filter((id) => Number.isInteger(id) || (typeof id === 'string' && id !== '')) : []; const matchResult = await submitScanForReview(user.userId, fields, candidateIds); await logScanAttempt({ userId: user.userId, ocrText: fields.ocrData?.rawText, ocrConfidence: fields.ocrData?.confidence, layer: 2, resultKind: 'submitted', latencyMs: Date.now() - startedAt, }); return res.status(200).json({ needsReview: true, submissionId: matchResult.submissionId, message: matchResult.message, }); } catch (error) { console.error('[POST /api/scan/submit-for-review]', error); return res.status(500).json({ error: 'Internal server error' }); } }