const express = require('express'); const logger = require('../utils/logger'); const { updateMuscleGroupRecovery } = require('../services/recoveryService'); function createWorkoutRouter({ pool }) { const router = express.Router(); const authMiddleware = (req, res, next) => { const token = req.headers.authorization?.split(' ')[1]; if (!token) return res.status(401).json({ error: 'No token provided' }); try { const jwt = require('jsonwebtoken'); const JWT_SECRET = process.env.JWT_SECRET || 'gravl-secret-key-change-in-production'; req.user = jwt.verify(token, JWT_SECRET); next(); } catch (err) { res.status(401).json({ error: 'Invalid token' }); } }; // POST /api/workouts/:id/swap - Swap a logged workout with another router.post('/:id/swap', authMiddleware, async (req, res) => { try { const logId = parseInt(req.params.id); const { newWorkoutId } = req.body; const userId = req.user.id; if (!logId || !newWorkoutId) { return res.status(400).json({ error: 'Missing logId or newWorkoutId' }); } // Verify the original log exists and belongs to this user const originalLogResult = await pool.query( 'SELECT * FROM workout_logs WHERE id = $1 AND user_id = $2', [logId, userId] ); if (originalLogResult.rows.length === 0) { return res.status(404).json({ error: 'Workout log not found' }); } const originalLog = originalLogResult.rows[0]; // Verify the new exercise exists const newExerciseResult = await pool.query( 'SELECT * FROM exercises WHERE id = $1', [newWorkoutId] ); if (newExerciseResult.rows.length === 0) { return res.status(404).json({ error: 'New exercise not found' }); } const newExercise = newExerciseResult.rows[0]; const client = await pool.connect(); try { await client.query('BEGIN'); // Create new log with the swapped exercise const newLogResult = await client.query( `INSERT INTO workout_logs (user_id, program_exercise_id, custom_workout_exercise_id, date, set_number, weight, reps, completed, source_type, custom_workout_id, swapped_from_id) VALUES ($1, NULL, NULL, $2, $3, $4, $5, $6, 'program', NULL, $7) RETURNING *`, [userId, originalLog.date, originalLog.set_number, originalLog.weight, originalLog.reps, originalLog.completed, logId] ); const newLog = newLogResult.rows[0]; // Record the swap in workout_swaps table await client.query( `INSERT INTO workout_swaps (user_id, original_log_id, swapped_log_id, swap_date, created_at, updated_at) VALUES ($1, $2, $3, $4, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)`, [userId, logId, newLog.id, originalLog.date] ); // Update muscle group recovery for the new exercise if (originalLog.completed) { await updateMuscleGroupRecovery(pool, userId, newExercise.muscle_group, 0.8); } await client.query('COMMIT'); logger.info('Workout swapped', { userId, originalLogId: logId, newLogId: newLog.id }); res.json({ success: true, message: 'Workout swapped successfully', swap: { originalLogId: logId, newLogId: newLog.id, newExercise: { id: newExercise.id, name: newExercise.name, muscleGroup: newExercise.muscle_group }, date: originalLog.date } }); } catch (err) { await client.query('ROLLBACK'); throw err; } finally { client.release(); } } catch (err) { logger.error('Error swapping workout', { error: err.message, userId: req.user.id }); res.status(500).json({ error: 'Database error' }); } }); // GET /api/workouts/available - Get list of available exercises for swapping router.get('/available', authMiddleware, async (req, res) => { try { const userId = req.user.id; const { muscleGroup, limit = 10 } = req.query; let query = 'SELECT * FROM exercises'; const params = []; if (muscleGroup) { query += ' WHERE muscle_group = $1'; params.push(muscleGroup); } query += ` ORDER BY muscle_group, name LIMIT ${Math.min(parseInt(limit), 100)}`; const result = await pool.query(query, params); res.json({ userId, count: result.rows.length, exercises: result.rows }); } catch (err) { logger.error('Error fetching available exercises', { error: err.message, userId: req.user.id }); res.status(500).json({ error: 'Database error' }); } }); return router; } module.exports = { createWorkoutRouter };