import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../../blocs/expense/expense_bloc.dart'; import '../../blocs/expense/expense_event.dart'; import '../../blocs/expense/expense_state.dart'; import '../../blocs/balance/balance_bloc.dart'; import '../../blocs/balance/balance_event.dart'; import '../../blocs/balance/balance_state.dart'; import '../../blocs/user/user_bloc.dart'; import '../../blocs/user/user_state.dart' as user_state; import '../../models/account.dart'; import '../../models/group.dart'; import 'add_expense_dialog.dart'; import 'balances_tab.dart'; import 'expenses_tab.dart'; import '../../models/user_balance.dart'; import '../../models/expense.dart'; import '../../services/error_service.dart'; class GroupExpensesPage extends StatefulWidget { final Account account; final Group group; const GroupExpensesPage({ super.key, required this.account, required this.group, }); @override State createState() => _GroupExpensesPageState(); } class _GroupExpensesPageState extends State with SingleTickerProviderStateMixin { late TabController _tabController; ExpenseCategory? _selectedCategory; String? _selectedPayerId; @override void initState() { super.initState(); _tabController = TabController(length: 2, vsync: this); _loadData(); } @override void dispose() { _tabController.dispose(); super.dispose(); } void _loadData() { // Charger les dépenses du groupe context.read().add(LoadExpensesByGroup(widget.group.id)); // Charger les balances du groupe context.read().add(LoadGroupBalances(widget.group.id)); } @override Widget build(BuildContext context) { final theme = Theme.of(context); final isDarkMode = theme.brightness == Brightness.dark; return Scaffold( backgroundColor: isDarkMode ? theme.scaffoldBackgroundColor : Colors.grey[50], appBar: AppBar( title: const Text( 'Dépenses du voyage', style: TextStyle(fontWeight: FontWeight.bold), ), centerTitle: true, backgroundColor: Colors.transparent, elevation: 0, foregroundColor: theme.colorScheme.onSurface, leading: IconButton( icon: const Icon(Icons.arrow_back), onPressed: () => Navigator.pop(context), ), actions: [], ), body: MultiBlocListener( listeners: [ BlocListener( listener: (context, state) { if (state is ExpenseOperationSuccess) { ErrorService().showSnackbar( message: state.message, isError: false, ); _loadData(); // Recharger les données après une opération } else if (state is ExpenseError) { ErrorService().showError(message: state.message); } else if (state is ExpensesLoaded) { // Rafraîchir les balances quand les dépenses changent (ex: via stream) context.read().add( RefreshBalance(widget.group.id), ); } }, ), ], child: Column( children: [ // Summary Card BlocBuilder( builder: (context, state) { if (state is GroupBalancesLoaded) { return _buildSummaryCard(state.balances, isDarkMode); } return const SizedBox.shrink(); }, ), // Tabs Container( decoration: BoxDecoration( border: Border( bottom: BorderSide(color: theme.dividerColor, width: 1), ), ), child: TabBar( controller: _tabController, labelColor: theme.colorScheme.primary, unselectedLabelColor: theme.colorScheme.onSurface.withValues( alpha: 0.6, ), indicatorColor: theme.colorScheme.primary, indicatorWeight: 3, labelStyle: const TextStyle(fontWeight: FontWeight.bold), tabs: const [ Tab(text: 'Toutes les dépenses'), Tab(text: 'Mes soldes'), ], ), ), // Tab View Expanded( child: TabBarView( controller: _tabController, children: [ // Onglet Dépenses BlocBuilder( builder: (context, state) { if (state is ExpenseLoading) { return const Center(child: CircularProgressIndicator()); } else if (state is ExpensesLoaded) { final userState = context.read().state; final currentUserId = userState is user_state.UserLoaded ? userState.user.id : ''; var filteredExpenses = state.expenses; if (_selectedCategory != null) { filteredExpenses = filteredExpenses .where((e) => e.category == _selectedCategory) .toList(); } if (_selectedPayerId != null) { filteredExpenses = filteredExpenses .where((e) => e.paidById == _selectedPayerId) .toList(); } return ExpensesTab( expenses: filteredExpenses, group: widget.group, currentUserId: currentUserId, ); } else if (state is ExpenseError) { return _buildErrorState('Erreur: ${state.message}'); } return _buildEmptyState('Aucune dépense trouvée'); }, ), // Onglet Balances (Combiné) BlocBuilder( builder: (context, state) { if (state is BalanceLoading) { return const Center(child: CircularProgressIndicator()); } else if (state is GroupBalancesLoaded) { return BalancesTab( balances: state.balances, group: widget.group, ); } else if (state is BalanceError) { return _buildErrorState('Erreur: ${state.message}'); } return _buildEmptyState('Aucune balance disponible'); }, ), ], ), ), ], ), ), floatingActionButton: FloatingActionButton( heroTag: 'group_expenses_fab', onPressed: _showAddExpenseDialog, backgroundColor: Colors.blue, foregroundColor: Colors.white, elevation: 4, shape: const CircleBorder(), tooltip: 'Ajouter une dépense', child: const Icon(Icons.add, size: 32), ), ); } Widget _buildSummaryCard(List balances, bool isDarkMode) { // Trouver la balance de l'utilisateur courant final userState = context.read().state; double myBalance = 0; if (userState is user_state.UserLoaded) { final myBalanceObj = balances.firstWhere( (b) => b.userId == userState.user.id, orElse: () => const UserBalance( userId: '', userName: '', totalPaid: 0, totalOwed: 0, balance: 0, ), ); myBalance = myBalanceObj.balance; } final isPositive = myBalance >= 0; final color = isPositive ? Colors.green : Colors.red; final amountStr = '${myBalance.abs().toStringAsFixed(2)} €'; return Container( margin: const EdgeInsets.all(16), padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: isDarkMode ? Colors.grey[800] : Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.05), blurRadius: 10, offset: const Offset(0, 4), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Votre solde total', style: TextStyle( color: isDarkMode ? Colors.grey[400] : Colors.grey[600], fontSize: 14, ), ), const SizedBox(height: 8), Row( children: [ Text( isPositive ? 'On vous doit ' : 'Vous devez ', style: TextStyle( color: color, fontSize: 24, fontWeight: FontWeight.bold, ), ), Text( amountStr, style: TextStyle( color: color, fontSize: 24, fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 8), Text( isPositive ? 'Vous êtes en positif sur ce voyage.' : 'Vous êtes en négatif sur ce voyage.', style: TextStyle( color: isDarkMode ? Colors.grey[400] : Colors.grey[600], fontSize: 14, ), ), const SizedBox(height: 12), InkWell( onTap: () { _tabController.animateTo(1); // Aller à l'onglet Balances }, child: Text( 'Voir le détail des soldes', style: TextStyle( color: Colors.blue[400], fontWeight: FontWeight.w500, fontSize: 14, ), ), ), ], ), ); } Widget _buildErrorState(String message) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.error_outline, size: 80, color: Colors.red[300]), const SizedBox(height: 16), Text( 'Erreur', style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: Colors.red[600], ), ), const SizedBox(height: 8), Padding( padding: const EdgeInsets.symmetric(horizontal: 32), child: Text( message, style: TextStyle(fontSize: 16, color: Colors.grey[600]), textAlign: TextAlign.center, ), ), const SizedBox(height: 32), ElevatedButton.icon( onPressed: _loadData, icon: const Icon(Icons.refresh), label: const Text('Réessayer'), ), ], ), ); } Widget _buildEmptyState(String message) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.info_outline, size: 80, color: Colors.grey[400]), const SizedBox(height: 16), Text( 'Aucune donnée', style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: Colors.grey[600], ), ), const SizedBox(height: 8), Text( message, style: TextStyle(fontSize: 16, color: Colors.grey[500]), textAlign: TextAlign.center, ), ], ), ); } void _showAddExpenseDialog() { final userState = context.read().state; if (userState is user_state.UserLoaded) { showDialog( context: context, builder: (context) => AddExpenseDialog(group: widget.group, currentUser: userState.user), ); } else { ErrorService().showError(message: 'Erreur: utilisateur non connecté'); } } }