from __future__ import annotations from datetime import date, timedelta from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.orm import Session from app.database import get_db from app.models import User, Workout from app.routers.auth import require_auth from app.schemas import ( AdjustSetRequest, CompleteSetRequest, MoveWorkoutRequest, PromptChoiceRequest, SetWeightRequest, WorkoutDetail, ) from app.services.auth import get_or_create_profile from app.services.reschedule import plan_move from app.services.workout import ( adjust_completed_set, complete_set, load_workout, pause_workout, resolve_prompt, resume_workout, set_exercise_weight, start_workout, workout_detail, ) router = APIRouter(prefix="/api/workouts", tags=["workout-actions"]) @router.post("/{workout_id}/start", response_model=WorkoutDetail) def api_start(workout_id: int, _: User = Depends(require_auth), db: Session = Depends(get_db)) -> WorkoutDetail: w = start_workout(db, load_workout(db, workout_id)) return workout_detail(db, w) @router.post("/{workout_id}/pause", response_model=WorkoutDetail) def api_pause(workout_id: int, _: User = Depends(require_auth), db: Session = Depends(get_db)) -> WorkoutDetail: w = pause_workout(db, load_workout(db, workout_id)) return workout_detail(db, w) @router.post("/{workout_id}/resume", response_model=WorkoutDetail) def api_resume(workout_id: int, _: User = Depends(require_auth), db: Session = Depends(get_db)) -> WorkoutDetail: w = resume_workout(db, load_workout(db, workout_id)) return workout_detail(db, w) @router.post("/{workout_id}/complete-set", response_model=WorkoutDetail) def api_complete_set( workout_id: int, body: CompleteSetRequest, _: User = Depends(require_auth), db: Session = Depends(get_db), ) -> WorkoutDetail: w = complete_set( db, load_workout(db, workout_id), actual_reps=body.actual_reps, actual_weight_kg=body.actual_weight_kg, ) return workout_detail(db, w) @router.post("/{workout_id}/exercises/{exercise_id}/weight", response_model=WorkoutDetail) def api_set_weight( workout_id: int, exercise_id: int, body: SetWeightRequest, _: User = Depends(require_auth), db: Session = Depends(get_db), ) -> WorkoutDetail: workout = load_workout(db, workout_id) exercise = next((e for e in workout.exercises if e.id == exercise_id), None) if not exercise: raise HTTPException(404, "Упражнение не найдено") w = set_exercise_weight(db, workout, exercise, body.weight_kg) return workout_detail(db, w) @router.post("/{workout_id}/exercises/{exercise_id}/sets/{set_id}", response_model=WorkoutDetail) def api_adjust_set( workout_id: int, exercise_id: int, set_id: int, body: AdjustSetRequest, _: User = Depends(require_auth), db: Session = Depends(get_db), ) -> WorkoutDetail: workout = load_workout(db, workout_id) exercise = next((e for e in workout.exercises if e.id == exercise_id), None) if not exercise: raise HTTPException(404, "Упражнение не найдено") workout_set = next((s for s in exercise.sets if s.id == set_id), None) if not workout_set: raise HTTPException(404, "Подход не найден") w = adjust_completed_set( db, workout, exercise, workout_set, actual_weight_kg=body.actual_weight_kg, actual_reps=body.actual_reps, ) return workout_detail(db, w) @router.post("/{workout_id}/move", response_model=WorkoutDetail) def api_move( workout_id: int, body: MoveWorkoutRequest, _: User = Depends(require_auth), db: Session = Depends(get_db), ) -> WorkoutDetail: workout = load_workout(db, workout_id) profile = get_or_create_profile(db) weekdays = profile.training_weekdays or [] if len(weekdays) != 3: raise HTTPException(400, "Не заданы дни тренировок") rows = db.query(Workout).filter(Workout.cycle_number == workout.cycle_number).all() assigned = plan_move(rows, workout_id, body.scheduled_date, list(weekdays)) for row in rows: if row.id in assigned: row.scheduled_date = date(2099, 1, 1) + timedelta(days=row.id % 300) db.flush() by_id = {row.id: row for row in rows} for workout_row_id, scheduled in assigned.items(): by_id[workout_row_id].scheduled_date = scheduled db.commit() return workout_detail(db, load_workout(db, workout_id)) @router.post("/{workout_id}/exercises/{exercise_id}/prompt", response_model=WorkoutDetail) def api_prompt( workout_id: int, exercise_id: int, body: PromptChoiceRequest, _: User = Depends(require_auth), db: Session = Depends(get_db), ) -> WorkoutDetail: workout = load_workout(db, workout_id) exercise = next((e for e in workout.exercises if e.id == exercise_id), None) if not exercise: raise HTTPException(404, "Упражнение не найдено") w = resolve_prompt( db, workout, exercise, choice=body.choice, weight_kg=body.weight_kg, increase_kg=body.increase_kg, decrease_kg=body.decrease_kg, ) return workout_detail(db, w)