import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:trainhub_flutter/core/utils/id_generator.dart'; import 'package:trainhub_flutter/injection.dart'; import 'package:trainhub_flutter/domain/entities/exercise.dart'; import 'package:trainhub_flutter/domain/entities/training_exercise.dart'; import 'package:trainhub_flutter/domain/entities/training_section.dart'; import 'package:trainhub_flutter/domain/repositories/exercise_repository.dart'; import 'package:trainhub_flutter/domain/repositories/training_plan_repository.dart'; import 'package:trainhub_flutter/presentation/plan_editor/plan_editor_state.dart'; part 'plan_editor_controller.g.dart'; @riverpod class PlanEditorController extends _$PlanEditorController { late TrainingPlanRepository _planRepo; late ExerciseRepository _exerciseRepo; @override Future build(String planId) async { _planRepo = getIt(); _exerciseRepo = getIt(); final plan = await _planRepo.getById(planId); final exercises = await _exerciseRepo.getAll(); return PlanEditorState(plan: plan, availableExercises: exercises); } void updatePlanName(String name) { final current = state.valueOrNull; if (current == null) return; state = AsyncValue.data( current.copyWith(plan: current.plan.copyWith(name: name), isDirty: true), ); } void addSection() { final current = state.valueOrNull; if (current == null) return; final newSection = TrainingSectionEntity( id: IdGenerator.generate(), name: 'New Section', ); state = AsyncValue.data( current.copyWith( plan: current.plan.copyWith( sections: [...current.plan.sections, newSection], ), isDirty: true, ), ); } void deleteSection(int index) { final current = state.valueOrNull; if (current == null) return; final sections = List.from(current.plan.sections); sections.removeAt(index); state = AsyncValue.data( current.copyWith( plan: current.plan.copyWith(sections: sections), isDirty: true, ), ); } void reorderSection(int oldIndex, int newIndex) { final current = state.valueOrNull; if (current == null) return; final sections = List.from(current.plan.sections); if (oldIndex < newIndex) newIndex -= 1; final item = sections.removeAt(oldIndex); sections.insert(newIndex, item); state = AsyncValue.data( current.copyWith( plan: current.plan.copyWith(sections: sections), isDirty: true, ), ); } void updateSectionName(int sectionIndex, String name) { final current = state.valueOrNull; if (current == null) return; final sections = List.from(current.plan.sections); sections[sectionIndex] = sections[sectionIndex].copyWith(name: name); state = AsyncValue.data( current.copyWith( plan: current.plan.copyWith(sections: sections), isDirty: true, ), ); } void addExerciseToSection(int sectionIndex, ExerciseEntity exercise) { final current = state.valueOrNull; if (current == null) return; final sections = List.from(current.plan.sections); final newExercise = TrainingExerciseEntity( instanceId: IdGenerator.generate(), exerciseId: exercise.id, name: exercise.name, ); sections[sectionIndex] = sections[sectionIndex].copyWith( exercises: [...sections[sectionIndex].exercises, newExercise], ); state = AsyncValue.data( current.copyWith( plan: current.plan.copyWith(sections: sections), isDirty: true, ), ); } void removeExerciseFromSection(int sectionIndex, int exerciseIndex) { final current = state.valueOrNull; if (current == null) return; final sections = List.from(current.plan.sections); final exercises = List.from( sections[sectionIndex].exercises, ); exercises.removeAt(exerciseIndex); sections[sectionIndex] = sections[sectionIndex].copyWith( exercises: exercises, ); state = AsyncValue.data( current.copyWith( plan: current.plan.copyWith(sections: sections), isDirty: true, ), ); } void moveExerciseBetweenSections({ required int fromSectionIndex, required int exerciseIndex, required int toSectionIndex, }) { final current = state.valueOrNull; if (current == null) return; if (fromSectionIndex == toSectionIndex) return; final sections = List.from(current.plan.sections); final fromExercises = List.from( sections[fromSectionIndex].exercises, ); final toExercises = List.from( sections[toSectionIndex].exercises, ); final exercise = fromExercises.removeAt(exerciseIndex); toExercises.add(exercise); sections[fromSectionIndex] = sections[fromSectionIndex].copyWith( exercises: fromExercises, ); sections[toSectionIndex] = sections[toSectionIndex].copyWith( exercises: toExercises, ); state = AsyncValue.data( current.copyWith( plan: current.plan.copyWith(sections: sections), isDirty: true, ), ); } void updateExerciseParams( int sectionIndex, int exerciseIndex, { int? sets, int? value, bool? isTime, int? rest, }) { final current = state.valueOrNull; if (current == null) return; final sections = List.from(current.plan.sections); final exercises = List.from( sections[sectionIndex].exercises, ); exercises[exerciseIndex] = exercises[exerciseIndex].copyWith( sets: sets ?? exercises[exerciseIndex].sets, value: value ?? exercises[exerciseIndex].value, isTime: isTime ?? exercises[exerciseIndex].isTime, rest: rest ?? exercises[exerciseIndex].rest, ); sections[sectionIndex] = sections[sectionIndex].copyWith( exercises: exercises, ); state = AsyncValue.data( current.copyWith( plan: current.plan.copyWith(sections: sections), isDirty: true, ), ); } void reorderExercise(int sectionIndex, int oldIndex, int newIndex) { final current = state.valueOrNull; if (current == null) return; final sections = List.from(current.plan.sections); final exercises = List.from( sections[sectionIndex].exercises, ); if (oldIndex < newIndex) newIndex -= 1; final item = exercises.removeAt(oldIndex); exercises.insert(newIndex, item); sections[sectionIndex] = sections[sectionIndex].copyWith( exercises: exercises, ); state = AsyncValue.data( current.copyWith( plan: current.plan.copyWith(sections: sections), isDirty: true, ), ); } Future createExercise({ required String name, String? instructions, String? tags, String? videoUrl, }) async { final current = state.valueOrNull; if (current == null) throw StateError('Controller state not loaded'); final exercise = await _exerciseRepo.create( name: name, instructions: instructions, tags: tags, videoUrl: videoUrl, ); final exercises = await _exerciseRepo.getAll(); state = AsyncValue.data(current.copyWith(availableExercises: exercises)); return exercise; } Future save() async { final current = state.valueOrNull; if (current == null) return; await _planRepo.update(current.plan); state = AsyncValue.data(current.copyWith(isDirty: false)); } }