import 'package:flutter/material.dart'; import 'package:drift/drift.dart'; import 'package:trainhub_flutter/database/database.dart'; class ProgramProvider extends ChangeNotifier { final AppDatabase database; List _programs = []; Program? _activeProgram; List _activeWeeks = []; List _activeWorkouts = []; List get programs => _programs; Program? get activeProgram => _activeProgram; List get activeWeeks => _activeWeeks; List get activeWorkouts => _activeWorkouts; ProgramProvider(this.database) { _loadPrograms(); } Future _loadPrograms() async { _programs = await (database.select(database.programs)..orderBy([ (t) => OrderingTerm( expression: t.createdAt, mode: OrderingMode.desc, ), ])) .get(); // Auto-select most recent if none selected, or re-verify active one if (_activeProgram == null && _programs.isNotEmpty) { await loadProgram(_programs.first.id); } else if (_activeProgram != null) { // Refresh active program data await loadProgram(_activeProgram!.id); } else { notifyListeners(); } } Future loadProgram(String id) async { final program = await (database.select( database.programs, )..where((t) => t.id.equals(id))).getSingleOrNull(); if (program != null) { _activeProgram = program; await _loadProgramDetails(id); } else { _activeProgram = null; _activeWeeks = []; _activeWorkouts = []; notifyListeners(); } } Future _loadProgramDetails(String programId) async { _activeWeeks = await (database.select(database.programWeeks) ..where((t) => t.programId.equals(programId)) ..orderBy([(t) => OrderingTerm(expression: t.position)])) .get(); _activeWorkouts = await (database.select( database.programWorkouts, )..where((t) => t.programId.equals(programId))).get(); notifyListeners(); } // --- Program Actions --- Future createProgram(String name) async { final id = DateTime.now().toIso8601String(); await database .into(database.programs) .insert( ProgramsCompanion.insert( id: id, name: name, createdAt: DateTime.now().toIso8601String(), ), ); await _loadPrograms(); await loadProgram(id); } Future deleteProgram(String id) async { await (database.delete( database.programs, )..where((t) => t.id.equals(id))).go(); if (_activeProgram?.id == id) { _activeProgram = null; } await _loadPrograms(); } Future duplicateProgram(String sourceId) async { final sourceProgram = await (database.select( database.programs, )..where((t) => t.id.equals(sourceId))).getSingle(); final newId = DateTime.now().toIso8601String(); await database .into(database.programs) .insert( ProgramsCompanion.insert( id: newId, name: '${sourceProgram.name} (Copy)', createdAt: DateTime.now().toIso8601String(), ), ); // Duplicate Weeks and Workouts // Note: implementing deep copy logic final weeks = await (database.select( database.programWeeks, )..where((t) => t.programId.equals(sourceId))).get(); final workouts = await (database.select( database.programWorkouts, )..where((t) => t.programId.equals(sourceId))).get(); for (var week in weeks) { final newWeekId = '${newId}_${week.position}'; // Simple ID gen await database .into(database.programWeeks) .insert( ProgramWeeksCompanion.insert( id: newWeekId, programId: newId, position: week.position, notes: Value(week.notes), ), ); final weekWorkouts = workouts.where((w) => w.weekId == week.id); for (var workout in weekWorkouts) { final newWorkoutId = DateTime.now().toIso8601String() + workout.id; // ensure uniqueness await database .into(database.programWorkouts) .insert( ProgramWorkoutsCompanion.insert( id: newWorkoutId, weekId: newWeekId, programId: newId, day: workout.day, type: workout.type, refId: Value(workout.refId), name: Value(workout.name), description: Value(workout.description), completed: const Value(false), ), ); } } await _loadPrograms(); await loadProgram(newId); } // --- Week Actions --- Future addWeek() async { if (_activeProgram == null) return; final nextPosition = _activeWeeks.isEmpty ? 1 : _activeWeeks.last.position + 1; final id = DateTime.now().toIso8601String(); await database .into(database.programWeeks) .insert( ProgramWeeksCompanion.insert( id: id, programId: _activeProgram!.id, position: nextPosition, ), ); await _loadProgramDetails(_activeProgram!.id); } Future deleteWeek(String id) async { if (_activeProgram == null) return; await (database.delete( database.programWeeks, )..where((t) => t.id.equals(id))).go(); await _loadProgramDetails(_activeProgram!.id); } Future updateWeekNote(String weekId, String note) async { await (database.update(database.programWeeks) ..where((t) => t.id.equals(weekId))) .write(ProgramWeeksCompanion(notes: Value(note))); if (_activeProgram != null) await _loadProgramDetails(_activeProgram!.id); } // --- Workout Actions --- Future addWorkout(ProgramWorkoutsCompanion workout) async { await database.into(database.programWorkouts).insert(workout); if (_activeProgram != null) await _loadProgramDetails(_activeProgram!.id); } Future updateWorkout(ProgramWorkout workout) async { await (database.update( database.programWorkouts, )..where((t) => t.id.equals(workout.id))).write( ProgramWorkoutsCompanion( day: Value(workout.day), type: Value(workout.type), refId: Value(workout.refId), name: Value(workout.name), description: Value(workout.description), completed: Value(workout.completed), weekId: Value(workout.weekId), ), ); if (_activeProgram != null) await _loadProgramDetails(_activeProgram!.id); } Future deleteWorkout(String id) async { await (database.delete( database.programWorkouts, )..where((t) => t.id.equals(id))).go(); if (_activeProgram != null) await _loadProgramDetails(_activeProgram!.id); } Future toggleWorkoutComplete(String id, bool currentStatus) async { await (database.update(database.programWorkouts) ..where((t) => t.id.equals(id))) .write(ProgramWorkoutsCompanion(completed: Value(!currentStatus))); if (_activeProgram != null) await _loadProgramDetails(_activeProgram!.id); } }