const express = require('express'); const { requireAuth } = require('../middleware/auth'); const { createBet, createBetFromScreenshot, settleBet, listBets } = require('../services/betService'); const { extractFromScreenshot } = require('../services/ocrStub'); const { recalculatePerformance } = require('../services/performanceService'); const router = express.Router(); const VALID_BET_TYPES = new Set(['straight', 'parlay', 'teaser', 'round_robin']); const VALID_SETTLE_STATUSES = new Set(['won', 'lost', 'push', 'void']); function validateQuickslip(body) { if (!Array.isArray(body.legs) || body.legs.length === 0) return 'legs array is required'; if (body.amount == null || body.amount <= 0) return 'amount is required and must be positive'; if (!body.book) return 'book is required'; if (!body.bet_type) return 'bet_type is required'; if (!VALID_BET_TYPES.has(body.bet_type)) return `Invalid bet_type: ${body.bet_type}`; for (let i = 0; i < body.legs.length; i++) { const leg = body.legs[i]; if (!leg.player) return `leg ${i}: player is required`; if (!leg.stat_type) return `leg ${i}: stat_type is required`; if (leg.line == null) return `leg ${i}: line is required`; if (!leg.direction) return `leg ${i}: direction is required`; } return null; } // POST /api/bets/quickslip router.post('/quickslip', requireAuth, async (req, res) => { const error = validateQuickslip(req.body); if (error) return res.status(400).json({ error }); try { const result = await createBet(req.user.id, req.body); return res.status(201).json(result); } catch (err) { if (err.statusCode === 404) return res.status(404).json({ error: err.message }); console.error('[VYNDR] Quickslip error:', err.message); return res.status(503).json({ error: 'Bet submission failed' }); } }); // POST /api/bets/screenshot router.post('/screenshot', requireAuth, async (req, res) => { // For MVP: accept the request but return stub extraction const book = req.body?.book || req.query?.book || 'unknown'; // In a real implementation, we'd parse multipart form data and process the image // For MVP, just return the stub const extracted = extractFromScreenshot(book); return res.status(201).json({ bet_id: null, status: 'pending_confirmation', extracted, needs_confirmation: true, message: extracted.message, }); }); // POST /api/bets/screenshot/confirm router.post('/screenshot/confirm', requireAuth, async (req, res) => { const error = validateQuickslip(req.body); if (error) return res.status(400).json({ error }); try { const result = await createBetFromScreenshot(req.user.id, req.body); return res.status(201).json(result); } catch (err) { if (err.statusCode === 404) return res.status(404).json({ error: err.message }); console.error('[VYNDR] Screenshot confirm error:', err.message); return res.status(503).json({ error: 'Bet submission failed' }); } }); // POST /api/bets/sync router.post('/sync', requireAuth, async (req, res) => { return res.json({ status: 'coming_soon', message: 'Sportsbook sync is coming soon. Use quick slip or screenshot for now.', supported_books: ['draftkings', 'fanduel', 'betmgm', 'caesars', 'fanatics', 'bet365', 'hardrockbet', 'pointsbet', 'betrivers'], }); }); // PATCH /api/bets/:id/settle router.patch('/:id/settle', requireAuth, async (req, res) => { const { status, leg_outcomes } = req.body; if (!status || !VALID_SETTLE_STATUSES.has(status)) { return res.status(400).json({ error: `Invalid status. Must be one of: ${[...VALID_SETTLE_STATUSES].join(', ')}` }); } try { const result = await settleBet(req.user.id, req.params.id, { status, leg_outcomes }); return res.json(result); } catch (err) { if (err.statusCode === 404) return res.status(404).json({ error: err.message }); if (err.statusCode === 422) return res.status(422).json({ error: err.message }); console.error('[VYNDR] Settle error:', err.message); return res.status(503).json({ error: 'Settlement failed' }); } }); // GET /api/bets router.get('/', requireAuth, async (req, res) => { try { const result = await listBets(req.user.id, { status: req.query.status || null, book: req.query.book || null, limit: Math.min(parseInt(req.query.limit) || 20, 100), offset: parseInt(req.query.offset) || 0, }); return res.json(result); } catch (err) { console.error('[VYNDR] List bets error:', err.message); return res.status(503).json({ error: 'Failed to fetch bets' }); } }); // GET /api/bets/performance router.get('/performance', requireAuth, async (req, res) => { try { const result = await recalculatePerformance(req.user.id); return res.json(result); } catch (err) { console.error('[VYNDR] Performance error:', err.message); return res.status(503).json({ error: 'Failed to calculate performance' }); } }); module.exports = router;