import 'package:flutter/material.dart'; import 'package:table_calendar/table_calendar.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'training_detail_screen.dart'; const Map categoryColors = { 'Aufwärmen & Mobilisation': Colors.deepOrange, 'Wurf- & Torabschluss': Colors.orange, 'Torwarttraining': Colors.green, 'Athletik': Colors.blue, 'Pass': Colors.purple, 'Koordination': Colors.teal, }; class CalendarTab extends StatefulWidget { final DateTime? initialDate; const CalendarTab({super.key, this.initialDate}); @override State createState() => _CalendarTabState(); } class _CalendarTabState extends State { CalendarFormat _calendarFormat = CalendarFormat.week; late DateTime _focusedDay; DateTime? _selectedDay; Map>> _events = {}; bool _isLoading = false; String? _currentUserId; String? _userRole; final _exerciseController = TextEditingController(); final _durationController = TextEditingController(); @override void dispose() { _exerciseController.dispose(); _durationController.dispose(); super.dispose(); } @override void initState() { super.initState(); _focusedDay = widget.initialDate ?? DateTime.now(); _selectedDay = _focusedDay; _currentUserId = FirebaseAuth.instance.currentUser?.uid; _initializeData(); } @override void didUpdateWidget(covariant CalendarTab oldWidget) { super.didUpdateWidget(oldWidget); if (widget.initialDate != null && widget.initialDate != oldWidget.initialDate) { setState(() { _focusedDay = widget.initialDate!; _selectedDay = widget.initialDate!; }); } } Future _initializeData() async { if (_currentUserId != null) { final userDoc = await FirebaseFirestore.instance .collection('User') .doc(_currentUserId) .get(); if (userDoc.exists) { setState(() { _userRole = userDoc.data()?['role'] as String?; }); await _loadEvents(); } } } Future _loadEvents() async { if (_userRole == null) return; setState(() => _isLoading = true); try { QuerySnapshot trainersSnapshot; if (_userRole == 'trainer') { trainersSnapshot = await FirebaseFirestore.instance .collection('User') .where('role', isEqualTo: 'trainer') .where(FieldPath.documentId, isEqualTo: _currentUserId) .get(); } else { final userDoc = await FirebaseFirestore.instance .collection('User') .doc(_currentUserId) .get(); if (!userDoc.exists) { setState(() => _isLoading = false); return; } final userData = userDoc.data() as Map; final userClub = userData['club'] as String?; if (userClub == null || userClub.isEmpty) { setState(() { _events = {}; _isLoading = false; }); return; } trainersSnapshot = await FirebaseFirestore.instance .collection('User') .where('role', isEqualTo: 'trainer') .where('club', isEqualTo: userClub) .get(); } final events = >>{}; for (var trainerDoc in trainersSnapshot.docs) { final trainerData = trainerDoc.data() as Map; final trainingTimes = trainerData['trainingTimes'] as Map? ?? {}; final trainingDurations = trainerData['trainingDurations'] as Map? ?? {}; final cancelledTrainings = trainerData['cancelledTrainings'] as List? ?? []; final trainingExercises = trainerData['trainingExercises'] as Map? ?? {}; trainingTimes.forEach((day, timeStr) { if (timeStr == null) return; final duration = trainingDurations[day] as int? ?? 60; final timeParts = (timeStr as String).split(':'); if (timeParts.length != 2) return; final hour = int.tryParse(timeParts[0]) ?? 0; final minute = int.tryParse(timeParts[1]) ?? 0; final now = DateTime.now(); final daysUntilNext = _getDaysUntilNext(day, now.weekday); final eventDate = DateTime(now.year, now.month, now.day + daysUntilNext, hour, minute); for (var i = 0; i < 52; i++) { final date = eventDate.add(Duration(days: i * 7)); final normalizedDate = DateTime(date.year, date.month, date.day); final dateString = normalizedDate.toIso8601String(); final isCancelled = cancelledTrainings.any((cancelled) { if (cancelled is Map) { final cancelledDate = DateTime.parse(cancelled['date'] as String); return isSameDay(cancelledDate, normalizedDate); } return false; }); if (!isCancelled) { final exercises = trainingExercises[dateString] as List? ?? []; final totalExerciseDuration = exercises.fold(0, (sum, exercise) { if (exercise is Map) { return sum + (exercise['duration'] as int? ?? 0); } return sum; }); final event = { 'trainerName': trainerData['name'] ?? 'Unbekannter Trainer', 'time': timeStr, 'duration': duration, 'trainerId': trainerDoc.id, 'isCurrentUser': trainerDoc.id == _currentUserId, 'day': day, 'date': dateString, 'exercises': exercises, 'remainingTime': duration - totalExerciseDuration, 'club': trainerData['club'] ?? 'Kein Verein', }; if (events.containsKey(normalizedDate)) { events[normalizedDate]!.add(event); } else { events[normalizedDate] = [event]; } } } }); } setState(() { _events = events; _isLoading = false; }); } catch (e) { print('Error loading events: $e'); setState(() => _isLoading = false); } } Future _addExercise(Map event) async { if (_userRole != 'trainer' || !event['isCurrentUser']) return; // Navigiere zum Suchbildschirm und warte auf das Ergebnis final result = await Navigator.pushNamed( context, '/search', arguments: { 'selectMode': true, 'remainingTime': event['remainingTime'], }, ); // Wenn eine Übung ausgewählt wurde if (result != null && result is Map) { try { final userDoc = await FirebaseFirestore.instance .collection('User') .doc(_currentUserId) .get(); if (!userDoc.exists) return; final data = userDoc.data() as Map; final trainingExercises = Map.from(data['trainingExercises'] ?? {}); final exercises = List>.from(trainingExercises[event['date']] ?? []); exercises.add({ 'id': result['id'], 'name': result['title'], 'description': result['description'], 'duration': result['duration'], }); trainingExercises[event['date']] = exercises; await FirebaseFirestore.instance .collection('User') .doc(_currentUserId) .update({ 'trainingExercises': trainingExercises, }); await _loadEvents(); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Übung wurde hinzugefügt')), ); } } catch (e) { print('Error adding exercise: $e'); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Fehler beim Hinzufügen der Übung')), ); } } } } Future _deleteTraining(Map event) async { if (_userRole != 'trainer' || !event['isCurrentUser']) return; try { final userDoc = await FirebaseFirestore.instance .collection('User') .doc(_currentUserId) .get(); if (!userDoc.exists) return; final data = userDoc.data() as Map; final cancelledTrainings = List>.from(data['cancelledTrainings'] ?? []); cancelledTrainings.add({ 'date': event['date'], 'day': event['day'], 'time': event['time'], 'duration': event['duration'], }); await FirebaseFirestore.instance .collection('User') .doc(_currentUserId) .update({ 'cancelledTrainings': cancelledTrainings, }); await _loadEvents(); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Training wurde gelöscht')), ); } } catch (e) { print('Error deleting training: $e'); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Fehler beim Löschen des Trainings')), ); } } } Future _removeExercise(Map event, Map exercise) async { if (_userRole != 'trainer' || !event['isCurrentUser']) return; try { final userDoc = await FirebaseFirestore.instance .collection('User') .doc(_currentUserId) .get(); if (!userDoc.exists) return; final data = userDoc.data() as Map; final trainingExercises = Map.from(data['trainingExercises'] ?? {}); final exercises = List>.from(trainingExercises[event['date']] ?? []); // Entferne die Übung aus der Liste exercises.removeWhere((e) => e['id'] == exercise['id']); trainingExercises[event['date']] = exercises; await FirebaseFirestore.instance .collection('User') .doc(_currentUserId) .update({ 'trainingExercises': trainingExercises, }); await _loadEvents(); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Übung wurde entfernt')), ); } } catch (e) { print('Error removing exercise: $e'); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Fehler beim Entfernen der Übung')), ); } } } int _getDaysUntilNext(String day, int currentWeekday) { final weekdays = { 'Montag': 1, 'Dienstag': 2, 'Mittwoch': 3, 'Donnerstag': 4, 'Freitag': 5, 'Samstag': 6, 'Sonntag': 7, }; final targetWeekday = weekdays[day] ?? 1; var daysUntilNext = targetWeekday - currentWeekday; if (daysUntilNext <= 0) { daysUntilNext += 7; } return daysUntilNext; } List> _getEventsForDay(DateTime day) { final normalizedDate = DateTime(day.year, day.month, day.day); return _events[normalizedDate] ?? []; } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Kalender'), actions: [ IconButton( icon: const Icon(Icons.refresh), onPressed: _isLoading ? null : _loadEvents, ), ], ), body: SafeArea( child: Column( children: [ Container( padding: const EdgeInsets.all(8.0), child: TableCalendar( firstDay: DateTime.utc(2024, 1, 1), lastDay: DateTime.utc(2025, 12, 31), focusedDay: _focusedDay, calendarFormat: _calendarFormat, selectedDayPredicate: (day) { return isSameDay(_selectedDay, day); }, onDaySelected: (selectedDay, focusedDay) { setState(() { _selectedDay = selectedDay; _focusedDay = focusedDay; }); }, onFormatChanged: (format) { setState(() { _calendarFormat = format; }); }, onPageChanged: (focusedDay) { setState(() { _focusedDay = focusedDay; }); }, eventLoader: _getEventsForDay, calendarStyle: const CalendarStyle( markersMaxCount: 1, markerDecoration: BoxDecoration( color: Colors.blue, shape: BoxShape.circle, ), ), headerStyle: const HeaderStyle( formatButtonVisible: true, titleCentered: true, ), calendarBuilders: CalendarBuilders( markerBuilder: (context, date, events) { if (events.isEmpty) return null; return Positioned( bottom: 1, child: Container( width: 8, height: 8, decoration: const BoxDecoration( color: Colors.blue, shape: BoxShape.circle, ), ), ); }, ), startingDayOfWeek: StartingDayOfWeek.monday, daysOfWeekStyle: const DaysOfWeekStyle( weekdayStyle: TextStyle(fontWeight: FontWeight.bold), weekendStyle: TextStyle(color: Colors.red), ), availableCalendarFormats: const { CalendarFormat.week: 'Woche', CalendarFormat.month: 'Monat', }, ), ), const Divider(), Expanded( child: _isLoading ? const Center(child: CircularProgressIndicator()) : _selectedDay == null ? const Center(child: Text('Bitte wähle einen Tag aus')) : ListView.builder( padding: const EdgeInsets.symmetric(horizontal: 16), itemCount: _getEventsForDay(_selectedDay!).length, itemBuilder: (context, index) { final event = _getEventsForDay(_selectedDay!)[index]; final isCurrentUser = event['isCurrentUser'] as bool; final exercises = event['exercises'] as List; final remainingTime = event['remainingTime'] as int; return Card( margin: const EdgeInsets.only(bottom: 8), color: isCurrentUser ? Colors.blue.withOpacity(0.1) : null, child: Column( children: [ ListTile( leading: Icon( Icons.sports, color: categoryColors[event['day']] ?? (isCurrentUser ? Colors.blue : Colors.grey), ), title: Text( isCurrentUser ? 'Training' : event['trainerName'], style: TextStyle( fontWeight: isCurrentUser ? FontWeight.bold : null, ), ), subtitle: Text( '${event['time']} - ${event['duration']} Minuten\nVerbleibende Zeit: $remainingTime Minuten', ), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ if (isCurrentUser) IconButton( icon: const Icon(Icons.add), onPressed: () => _addExercise(event), ), IconButton( icon: const Icon(Icons.info), onPressed: () { showDialog( context: context, builder: (context) => AlertDialog( title: Text(isCurrentUser ? 'Training' : event['trainerName']), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Zeit: ${event['time']}'), const SizedBox(height: 8), Text('Dauer: ${event['duration']} Minuten'), const SizedBox(height: 8), Text('Verbleibende Zeit: $remainingTime Minuten'), if (exercises.isNotEmpty) ...[ const SizedBox(height: 16), const Text('Übungen:', style: TextStyle(fontWeight: FontWeight.bold)), const SizedBox(height: 8), ...exercises.map((exercise) { if (exercise is Map) { return Padding( padding: const EdgeInsets.only(bottom: 4), child: Text( '${exercise['name']} - ${exercise['duration']} Minuten', ), ); } return const SizedBox.shrink(); }), ], ], ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Schließen'), ), ], ), ); }, ), if (isCurrentUser) IconButton( icon: const Icon(Icons.delete, color: Colors.red), onPressed: () { showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Training löschen'), content: const Text('Möchten Sie dieses Training wirklich löschen?'), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Abbrechen'), ), TextButton( onPressed: () { Navigator.pop(context); _deleteTraining(event); }, child: const Text('Löschen', style: TextStyle(color: Colors.red)), ), ], ), ); }, ), ], ), ), if (exercises.isNotEmpty) Padding( padding: const EdgeInsets.all(8.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Divider(), const Text('Übungen:', style: TextStyle(fontWeight: FontWeight.bold)), const SizedBox(height: 8), ...exercises.map((exercise) { if (exercise is Map) { return Padding( padding: const EdgeInsets.only(bottom: 4), child: Row( children: [ const Icon(Icons.fitness_center, size: 16), const SizedBox(width: 8), Expanded( child: InkWell( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => TrainingDetailScreen( trainingId: exercise['id'], ), ), ); }, child: Text( '${exercise['name']} - ${exercise['duration']} Minuten', style: const TextStyle( decoration: TextDecoration.underline, color: Colors.blue, ), ), ), ), if (isCurrentUser) IconButton( icon: const Icon(Icons.delete, color: Colors.red, size: 20), onPressed: () { showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Übung entfernen'), content: const Text('Möchten Sie diese Übung wirklich entfernen?'), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Abbrechen'), ), TextButton( onPressed: () { Navigator.pop(context); _removeExercise(event, exercise); }, child: const Text('Entfernen', style: TextStyle(color: Colors.red)), ), ], ), ); }, ), ], ), ); } return const SizedBox.shrink(); }), ], ), ), ], ), ); }, ), ), ], ), ), ); } }