import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:intl/intl.dart'; import 'package:collection/collection.dart'; import '../../models/cycle_entry.dart'; import '../../providers/user_provider.dart'; class CycleHistoryScreen extends ConsumerStatefulWidget { const CycleHistoryScreen({super.key}); @override ConsumerState createState() => _CycleHistoryScreenState(); } class _CycleHistoryScreenState extends ConsumerState { bool _isUnlocked = false; void _showDeleteAllDialog(BuildContext context, WidgetRef ref) { showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Delete All History?'), content: const Text( 'This will permanently delete all cycle entries. This action cannot be undone.'), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Cancel'), ), TextButton( onPressed: () { ref.read(cycleEntriesProvider.notifier).clearEntries(); Navigator.pop(context); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('All cycle history has been deleted.')), ); }, child: const Text('Delete All', style: TextStyle(color: Colors.red)), ), ], ), ); } void _showDeleteMonthDialog(BuildContext context, WidgetRef ref) { showDialog( context: context, builder: (context) => const _DeleteMonthDialog(), ); } Future _authenticate() async { final user = ref.read(userProfileProvider); if (user?.privacyPin == null) return; final controller = TextEditingController(); final pin = await showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Enter PIN'), content: TextField( controller: controller, keyboardType: TextInputType.number, obscureText: true, maxLength: 4, style: const TextStyle(fontSize: 24, letterSpacing: 8), textAlign: TextAlign.center, decoration: const InputDecoration( hintText: '....', border: OutlineInputBorder(), ), autofocus: true, ), actions: [ TextButton(onPressed: () => Navigator.pop(context), child: const Text('Cancel')), ElevatedButton( onPressed: () => Navigator.pop(context, controller.text), child: const Text('Unlock'), ), ], ), ); if (pin == user!.privacyPin) { setState(() { _isUnlocked = true; }); } else if (pin != null) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Incorrect PIN')), ); } } } @override Widget build(BuildContext context) { final entries = ref.watch(cycleEntriesProvider); final user = ref.watch(userProfileProvider); // Privacy Check final isProtected = user?.isHistoryProtected ?? false; final hasPin = user?.privacyPin != null && user!.privacyPin!.isNotEmpty; final isLocked = isProtected && hasPin && !_isUnlocked; if (isLocked) { return Scaffold( appBar: AppBar(title: const Text('Cycle History')), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.lock_outline, size: 64, color: Colors.grey), const SizedBox(height: 16), const Text( 'History is Protected', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), const SizedBox(height: 24), ElevatedButton.icon( onPressed: _authenticate, icon: const Icon(Icons.key), label: const Text('Enter PIN to View'), ), ], ), ), ); } final groupedEntries = groupBy( entries, (CycleEntry entry) => DateFormat('MMMM yyyy').format(entry.date), ); return Scaffold( appBar: AppBar( title: const Text('Cycle History'), actions: [ if (entries.isNotEmpty) PopupMenuButton( onSelected: (value) { if (value == 'delete_all') { _showDeleteAllDialog(context, ref); } else if (value == 'delete_month') { _showDeleteMonthDialog(context, ref); } }, itemBuilder: (BuildContext context) => >[ const PopupMenuItem( value: 'delete_month', child: Text('Delete by Month'), ), const PopupMenuItem( value: 'delete_all', child: Text('Delete All Data'), ), ], ), ], ), body: entries.isEmpty ? Center( child: Text( 'No cycle history found.', style: Theme.of(context).textTheme.bodyLarge, ), ) : ListView.builder( itemCount: groupedEntries.keys.length, itemBuilder: (context, index) { final month = groupedEntries.keys.elementAt(index); final monthEntries = groupedEntries[month]!; return ExpansionTile( title: Text(month, style: Theme.of(context).textTheme.titleLarge), initiallyExpanded: index == 0, children: monthEntries.map((entry) { return Dismissible( key: Key(entry.id), direction: DismissDirection.endToStart, onDismissed: (direction) { ref.read(cycleEntriesProvider.notifier).deleteEntry(entry.id); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( 'Entry for ${DateFormat.yMMMd().format(entry.date)} deleted.')), ); }, background: Container( color: Colors.red, alignment: Alignment.centerRight, padding: const EdgeInsets.symmetric(horizontal: 20.0), child: const Icon(Icons.delete, color: Colors.white), ), child: ListTile( title: Text(DateFormat.yMMMMEEEEd().format(entry.date)), subtitle: Text(_buildEntrySummary(entry, ref)), isThreeLine: true, ), ); }).toList(), ); }, ), ); } String _buildEntrySummary(CycleEntry entry, WidgetRef ref) { final summary = []; // Calculate Cycle Day / Phase // This is a simplified calculation. For accurate phase, we need cycle logic. // We'll calculate the 'Day of Cycle' by finding the most recent period start before this entry. final allEntries = ref.read(cycleEntriesProvider); DateTime? lastPeriodStart; // Inefficient for large lists but acceptable for now. // Optimization: Calculate this once or pass cycle context. final sortedEntries = List.from(allEntries)..sort((a,b) => a.date.compareTo(b.date)); for (var e in sortedEntries) { if (e.date.isAfter(entry.date)) break; if (e.isPeriodDay) { // If it's a period day and the previous day wasn't (or gap > 1), it's a start. // Simplified: Just take the period day closest to entry. // Actually, if 'entry' IS a period day, then it's Menstrual phase. // We'll just look for the last period day. lastPeriodStart = e.date; // continuously update to find the latest one <= entry.date // But we need the START of that period block. } } // Better Approach: Use CycleService static helper if available, or just check entry props. if (entry.isPeriodDay) { summary.add('Menstrual Phase'); } else if (lastPeriodStart != null) { final day = entry.date.difference(lastPeriodStart).inDays + 1; // Estimate phase based on standard 28 day. User might want actual phase logic. // Reusing CycleService logic would be best but requires instantiating it with all data. String phase = 'Follicular'; if (day > 14) phase = 'Luteal'; // Very rough approximation if (day == 14) phase = 'Ovulation'; summary.add('Day $day ($phase)'); } if (entry.mood != null) { summary.add('Mood: ${entry.mood!.label}'); } if (entry.symptomCount > 0) { summary.add('${entry.symptomCount} symptom(s)'); } if (entry.notes != null && entry.notes!.isNotEmpty) { summary.add('Note: "${entry.notes}"'); } if (summary.isEmpty) { return 'No specific data logged.'; } return summary.join('\n'); // Use newline for better readability with notes } } class _DeleteMonthDialog extends ConsumerStatefulWidget { const _DeleteMonthDialog(); @override ConsumerState<_DeleteMonthDialog> createState() => _DeleteMonthDialogState(); } class _DeleteMonthDialogState extends ConsumerState<_DeleteMonthDialog> { late int _selectedYear; late int _selectedMonth; @override void initState() { super.initState(); final now = DateTime.now(); _selectedYear = now.year; _selectedMonth = now.month; } @override Widget build(BuildContext context) { final years = List.generate(5, (index) => DateTime.now().year - index); final months = List.generate(12, (index) => index + 1); return AlertDialog( title: const Text('Delete by Month'), content: Column( mainAxisSize: MainAxisSize.min, children: [ const Text('Select a month and year to delete all entries from.'), const SizedBox(height: 24), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ DropdownButton( value: _selectedMonth, items: months.map((month) => DropdownMenuItem( value: month, child: Text(DateFormat('MMMM').format(DateTime(0, month))), )) .toList(), onChanged: (value) { if (value != null) { setState(() { _selectedMonth = value; }); } }, ), const SizedBox(width: 16), DropdownButton( value: _selectedYear, items: years .map((year) => DropdownMenuItem( value: year, child: Text(year.toString()), )) .toList(), onChanged: (value) { if (value != null) { setState(() { _selectedYear = value; }); } }, ), ], ), ], ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Cancel'), ), TextButton( onPressed: () { ref .read(cycleEntriesProvider.notifier) .deleteEntriesForMonth(_selectedYear, _selectedMonth); Navigator.pop(context); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( 'Deleted entries for ${DateFormat('MMMM yyyy').format(DateTime(0, _selectedMonth))}.')), ); }, child: const Text('Delete', style: TextStyle(color: Colors.red)), ), ], ); } }