import type { StrengthExercise, StrengthWorkout } from "@/lib/models/strength"; export function exerciseVolumeKg(exercise: StrengthExercise): number { return exercise.sets.reduce((sum, set) => sum + (set.weightKg ?? 0) * (set.reps ?? 0), 0); } export function exerciseTopWeightKg(exercise: StrengthExercise): number | undefined { const weights = exercise.sets .map((set) => set.weightKg) .filter((weight): weight is number => weight !== undefined); return weights.length > 0 ? Math.max(...weights) : undefined; } // Epley formula: weight × (1 + reps / 30). Returns best e1rm across all sets. export function exerciseE1rm(exercise: StrengthExercise): number | undefined { let best: number | undefined; for (const set of exercise.sets) { if (set.weightKg == null || set.reps == null || set.reps < 1) continue; const e1rm = set.weightKg * (1 + set.reps / 30); if (best === undefined || e1rm > best) best = e1rm; } return best !== undefined ? Math.round(best * 10) / 10 : undefined; } export function workoutVolumeKg(workout: StrengthWorkout): number { return workout.exercises.reduce((sum, exercise) => sum + exerciseVolumeKg(exercise), 0); } export type ExerciseHistoryPoint = { date: Date; volumeKg: number; topWeightKg?: number; e1rmKg?: number; }; /** * History of a single exercise across past workouts (oldest first, including * the workout it was found in), used to chart progression. */ export function getExerciseHistory( exerciseName: string, workouts: StrengthWorkout[], limit: number ): ExerciseHistoryPoint[] { const points: ExerciseHistoryPoint[] = []; for (const workout of workouts) { const exercise = workout.exercises.find((e) => e.name === exerciseName); if (!exercise) continue; points.push({ date: workout.date, volumeKg: exerciseVolumeKg(exercise), topWeightKg: exerciseTopWeightKg(exercise), e1rmKg: exerciseE1rm(exercise), }); } points.sort((a, b) => a.date.getTime() - b.date.getTime()); return points.slice(-limit); }