$user->id, 'routine_id' => $data['routine_id'] ?? null, 'title' => $data['title'] ?? null, 'started_at' => now(), 'status' => WorkoutSessionStatus::Active, ]); } public function logSet(WorkoutSession $session, array $data): SetLog { if ($session->status->isTerminal()) { throw new WorkoutSessionAlreadyClosedException($session->id); } return $session->setLogs()->create([ 'exercise_id' => $data['exercise_id'], 'set_number' => $data['set_number'], 'reps' => $data['reps'] ?? null, 'weight' => $data['weight'] ?? null, 'rpe' => $data['rpe'] ?? null, 'duration_seconds' => $data['duration_seconds'] ?? null, 'distance_meters' => $data['distance_meters'] ?? null, 'is_warmup' => $data['is_warmup'] ?? false, 'is_completed' => true, 'logged_at' => now(), ]); } public function complete(WorkoutSession $session, array $data): WorkoutSession { if ($session->status->isTerminal()) { throw new WorkoutSessionAlreadyClosedException($session->id); } $session->ended_at = isset($data['ended_at']) ? Carbon::parse($data['ended_at']) : now(); $session->total_volume = $this->calculateTotalVolume($session); $session->status = WorkoutSessionStatus::Completed; $session->save(); // déclenche WorkoutCompleted (recalcul du streak, couche streaks) return $session->fresh('setLogs'); } /** Volume total = somme(reps * poids) des séries effectives (hors échauffement). */ private function calculateTotalVolume(WorkoutSession $session): float { return (float) $session->setLogs() ->where('is_warmup', false) ->get() ->sum(fn (SetLog $setLog) => (float) ($setLog->reps ?? 0) * (float) ($setLog->weight ?? 0)); } }