import 'package:flutter/material.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import 'package:playmaker/classe/theme.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../controllers/team_controller.dart'; import '../controllers/active_team.dart'; import '../utils/size_extension.dart'; class StatusPage extends StatefulWidget { final String? initialTeamId; final String initialTeamName; final String? initialTeamLogo; const StatusPage({ super.key, this.initialTeamId, this.initialTeamName = "Selecionar Equipa", this.initialTeamLogo, }); @override State createState() => _StatusPageState(); } class _StatusPageState extends State { final TeamController _teamController = TeamController(); final _supabase = Supabase.instance.client; late String? _selectedTeamId; late String _selectedTeamName; late String? _selectedTeamLogo; String _sortColumn = 'pts'; bool _isAscending = false; @override void initState() { super.initState(); _selectedTeamId = widget.initialTeamId; _selectedTeamName = widget.initialTeamName; _selectedTeamLogo = widget.initialTeamLogo; // Se não vieram parâmetros da HomeScreen, tenta carregar do SharedPreferences if (_selectedTeamId == null) { _loadSelectedTeamFallback(); } // Listen to global active team changes (e.g., when user marks favorite) globalActiveTeam.addListener(_onGlobalActiveTeamChanged); // Se já existe um globalActiveTeam no momento da abertura da página, aplica-o final atNow = globalActiveTeam.value; if (atNow != null) { _selectedTeamId = atNow.id; _selectedTeamName = atNow.name; _selectedTeamLogo = atNow.logo; } } void _onGlobalActiveTeamChanged() { final at = globalActiveTeam.value; if (!mounted) return; // Atualiza sempre para a equipa ativa global (favorita). Isto força a Status // a mostrar a equipa marcada como favorita assim que o utilizador a define. if (at != null) { setState(() { _selectedTeamId = at.id; _selectedTeamName = at.name; _selectedTeamLogo = at.logo; }); } } String _prefsKey(String key) { final userId = _supabase.auth.currentUser?.id ?? 'guest'; return '${key}_$userId'; } @override void didUpdateWidget(StatusPage oldWidget) { super.didUpdateWidget(oldWidget); // Quando a HomeScreen muda a equipa, a StatusPage atualiza automaticamente if (widget.initialTeamId != oldWidget.initialTeamId) { setState(() { _selectedTeamId = widget.initialTeamId; _selectedTeamName = widget.initialTeamName; _selectedTeamLogo = widget.initialTeamLogo; }); } } /// Fallback: só usado se a HomeScreen não passou nenhuma equipa ainda Future _loadSelectedTeamFallback() async { final prefs = await SharedPreferences.getInstance(); final savedId = prefs.getString(_prefsKey('last_team_id')); if (savedId != null && mounted) { setState(() { _selectedTeamId = savedId; _selectedTeamName = prefs.getString(_prefsKey('last_team_name')) ?? "Selecionar Equipa"; _selectedTeamLogo = prefs.getString(_prefsKey('last_team_logo')); }); } } /// Guarda a equipa selecionada localmente (quando muda dentro da StatusPage) Future _saveSelectedTeamLocally() async { final prefs = await SharedPreferences.getInstance(); if (_selectedTeamId != null) { await prefs.setString(_prefsKey('last_team_id'), _selectedTeamId!); await prefs.setString(_prefsKey('last_team_name'), _selectedTeamName); if (_selectedTeamLogo != null && _selectedTeamLogo!.isNotEmpty) { await prefs.setString(_prefsKey('last_team_logo'), _selectedTeamLogo!); } else { await prefs.remove(_prefsKey('last_team_logo')); } } // Também guarda no Supabase final userId = _supabase.auth.currentUser?.id; if (userId != null && _selectedTeamId != null) { try { await _supabase.from('profiles').upsert({ 'id': userId, 'selected_team_id': _selectedTeamId, }); } catch (e) { debugPrint("Erro ao guardar equipa no Supabase: $e"); } } } @override Widget build(BuildContext context) { final bgColor = Theme.of(context).cardTheme.color ?? Colors.white; final textColor = Theme.of(context).colorScheme.onSurface; return Column( children: [ Padding( padding: EdgeInsets.all(16.0 * context.sf), child: InkWell( onTap: () => _showTeamSelector(context), child: Container( padding: EdgeInsets.all(12 * context.sf), decoration: BoxDecoration( color: bgColor, borderRadius: BorderRadius.circular(15 * context.sf), border: Border.all(color: Colors.grey.withOpacity(0.2)), boxShadow: [ BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 5) ], ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row(children: [ (_selectedTeamLogo != null && _selectedTeamLogo!.isNotEmpty) ? ClipOval( child: CachedNetworkImage( imageUrl: _selectedTeamLogo!, width: 24 * context.sf, height: 24 * context.sf, fit: BoxFit.cover, placeholder: (context, url) => Icon(Icons.shield, color: AppTheme.primaryRed, size: 24 * context.sf), errorWidget: (context, url, error) => Icon( Icons.shield, color: AppTheme.primaryRed, size: 24 * context.sf), ), ) : Icon(Icons.shield, color: AppTheme.primaryRed, size: 24 * context.sf), SizedBox(width: 10 * context.sf), Text(_selectedTeamName, style: TextStyle( fontSize: 16 * context.sf, fontWeight: FontWeight.bold, color: textColor)), ]), Icon(Icons.arrow_drop_down, color: textColor), ], ), ), ), ), Expanded( child: _selectedTeamId == null ? Center( child: Text( "Seleciona uma equipa acima.", style: TextStyle( color: Colors.grey, fontSize: 14 * context.sf), ), ) : StreamBuilder>>( stream: _supabase .from('player_stats_with_names') .stream(primaryKey: ['id']).eq('team_id', _selectedTeamId!), builder: (context, statsSnapshot) { return StreamBuilder>>( stream: _supabase .from('games') .stream(primaryKey: ['id']).eq('my_team', _selectedTeamName), builder: (context, gamesSnapshot) { return StreamBuilder>>( stream: _supabase .from('members') .stream(primaryKey: ['id']).eq('team_id', _selectedTeamId!), builder: (context, membersSnapshot) { if (statsSnapshot.connectionState == ConnectionState.waiting || gamesSnapshot.connectionState == ConnectionState.waiting || membersSnapshot.connectionState == ConnectionState.waiting) { return const Center( child: CircularProgressIndicator( color: AppTheme.primaryRed)); } final membersData = membersSnapshot.data ?? []; if (membersData.isEmpty) { return Center( child: Text( "Esta equipa não tem jogadores registados.", style: TextStyle( color: Colors.grey, fontSize: 14 * context.sf))); } final statsData = statsSnapshot.data ?? []; final gamesData = gamesSnapshot.data ?? []; final totalGamesPlayedByTeam = gamesData .where((g) => g['status'] == 'Terminado') .length; final List> playerTotals = _aggregateStats(statsData, gamesData, membersData); final teamTotals = _calculateTeamTotals( playerTotals, totalGamesPlayedByTeam); playerTotals.sort((a, b) { var valA = a[_sortColumn] ?? 0; var valB = b[_sortColumn] ?? 0; return _isAscending ? valA.compareTo(valB) : valB.compareTo(valA); }); return _buildStatsGrid( context, playerTotals, teamTotals, bgColor, textColor); }, ); }, ); }, ), ), ], ); } @override void dispose() { globalActiveTeam.removeListener(_onGlobalActiveTeamChanged); super.dispose(); } List> _aggregateStats( List stats, List games, List members) { Map> aggregated = {}; for (var member in members) { String name = member['name']?.toString() ?? "Desconhecido"; String? imageUrl = member['image_url']?.toString(); aggregated[name] = { 'name': name, 'image_url': imageUrl, 'j': 0, 'pts': 0, 'ast': 0, 'rbs': 0, 'stl': 0, 'blk': 0, 'mvp': 0, 'def': 0, }; } for (var row in stats) { String name = row['player_name']?.toString() ?? "Desconhecido"; if (!aggregated.containsKey(name)) { aggregated[name] = { 'name': name, 'image_url': null, 'j': 0, 'pts': 0, 'ast': 0, 'rbs': 0, 'stl': 0, 'blk': 0, 'mvp': 0, 'def': 0, }; } aggregated[name]!['j'] += 1; aggregated[name]!['pts'] += (row['pts'] ?? 0); aggregated[name]!['ast'] += (row['ast'] ?? 0); aggregated[name]!['rbs'] += (row['rbs'] ?? 0); aggregated[name]!['stl'] += (row['stl'] ?? 0); aggregated[name]!['blk'] += (row['blk'] ?? 0); } for (var game in games) { String? mvp = game['mvp_name']; String? defRaw = game['top_def_name']; if (mvp != null && aggregated.containsKey(mvp)) { aggregated[mvp]!['mvp'] += 1; } if (defRaw != null) { String defName = defRaw.split(' (')[0].trim(); if (aggregated.containsKey(defName)) { aggregated[defName]!['def'] += 1; } } } return aggregated.values.toList(); } Map _calculateTeamTotals( List> players, int teamGames) { int tPts = 0, tAst = 0, tRbs = 0, tStl = 0, tBlk = 0, tMvp = 0, tDef = 0; for (var p in players) { tPts += (p['pts'] as int); tAst += (p['ast'] as int); tRbs += (p['rbs'] as int); tStl += (p['stl'] as int); tBlk += (p['blk'] as int); tMvp += (p['mvp'] as int); tDef += (p['def'] as int); } return { 'name': 'TOTAL EQUIPA', 'image_url': null, 'j': teamGames, 'pts': tPts, 'ast': tAst, 'rbs': tRbs, 'stl': tStl, 'blk': tBlk, 'mvp': tMvp, 'def': tDef, }; } Widget _buildStatsGrid( BuildContext context, List> players, Map teamTotals, Color bgColor, Color textColor) { return Container( color: Colors.transparent, width: double.infinity, child: SingleChildScrollView( scrollDirection: Axis.vertical, physics: const BouncingScrollPhysics(), child: SingleChildScrollView( scrollDirection: Axis.horizontal, physics: const ClampingScrollPhysics(), child: ConstrainedBox( constraints: BoxConstraints(minWidth: MediaQuery.of(context).size.width), child: DataTable( columnSpacing: 20 * context.sf, horizontalMargin: 16 * context.sf, headingRowColor: WidgetStateProperty.all( Theme.of(context).colorScheme.surface), dataRowMaxHeight: 60 * context.sf, dataRowMinHeight: 60 * context.sf, columns: [ DataColumn( label: Text('JOGADOR', style: TextStyle(color: textColor))), _buildSortableColumn(context, 'J', 'j', textColor), _buildSortableColumn(context, 'PTS', 'pts', textColor), _buildSortableColumn(context, 'AST', 'ast', textColor), _buildSortableColumn(context, 'RBS', 'rbs', textColor), _buildSortableColumn(context, 'STL', 'stl', textColor), _buildSortableColumn(context, 'BLK', 'blk', textColor), _buildSortableColumn(context, 'DEF 🛡️', 'def', textColor), _buildSortableColumn(context, 'MVP 🏆', 'mvp', textColor), ], rows: [ ...players.map((player) => DataRow(cells: [ DataCell( Row(children: [ ClipOval( child: Container( width: 30 * context.sf, height: 30 * context.sf, color: Colors.grey.withOpacity(0.2), child: (player['image_url'] != null && player['image_url'] .toString() .isNotEmpty) ? CachedNetworkImage( imageUrl: player['image_url'], fit: BoxFit.cover, fadeInDuration: Duration.zero, placeholder: (context, url) => Icon( Icons.person, size: 18 * context.sf, color: Colors.grey), errorWidget: (context, url, error) => Icon(Icons.person, size: 18 * context.sf, color: Colors.grey), ) : Icon(Icons.person, size: 18 * context.sf, color: Colors.grey), ), ), SizedBox(width: 10 * context.sf), Text(player['name'], style: TextStyle( fontWeight: FontWeight.bold, fontSize: 13 * context.sf, color: textColor)), ]), ), DataCell(Center( child: Text(player['j'].toString(), style: TextStyle(color: textColor)))), _buildStatCell(context, player['pts'], textColor, isHighlight: true), _buildStatCell(context, player['ast'], textColor), _buildStatCell(context, player['rbs'], textColor), _buildStatCell(context, player['stl'], textColor), _buildStatCell(context, player['blk'], textColor), _buildStatCell(context, player['def'], textColor, isBlue: true), _buildStatCell(context, player['mvp'], textColor, isGold: true), ])), DataRow( color: WidgetStateProperty.all( Theme.of(context).colorScheme.surface.withOpacity(0.5)), cells: [ DataCell(Text('TOTAL EQUIPA', style: TextStyle( fontWeight: FontWeight.w900, color: textColor, fontSize: 12 * context.sf))), DataCell(Center( child: Text(teamTotals['j'].toString(), style: TextStyle( fontWeight: FontWeight.bold, color: textColor)))), _buildStatCell(context, teamTotals['pts'], textColor, isHighlight: true), _buildStatCell(context, teamTotals['ast'], textColor), _buildStatCell(context, teamTotals['rbs'], textColor), _buildStatCell(context, teamTotals['stl'], textColor), _buildStatCell(context, teamTotals['blk'], textColor), _buildStatCell(context, teamTotals['def'], textColor, isBlue: true), _buildStatCell(context, teamTotals['mvp'], textColor, isGold: true), ], ), ], ), ), ), ), ); } DataColumn _buildSortableColumn( BuildContext context, String title, String sortKey, Color textColor) { return DataColumn( label: InkWell( onTap: () => setState(() { if (_sortColumn == sortKey) { _isAscending = !_isAscending; } else { _sortColumn = sortKey; _isAscending = false; } }), child: Row(children: [ Text(title, style: TextStyle( fontSize: 12 * context.sf, fontWeight: FontWeight.bold, color: textColor)), if (_sortColumn == sortKey) Icon( _isAscending ? Icons.arrow_drop_up : Icons.arrow_drop_down, size: 18 * context.sf, color: AppTheme.primaryRed), ]), ), ); } DataCell _buildStatCell(BuildContext context, int value, Color textColor, {bool isHighlight = false, bool isGold = false, bool isBlue = false}) { return DataCell(Center( child: Container( padding: EdgeInsets.symmetric( horizontal: 8 * context.sf, vertical: 4 * context.sf), decoration: BoxDecoration( color: isGold && value > 0 ? Colors.amber.withOpacity(0.2) : (isBlue && value > 0 ? Colors.blue.withOpacity(0.1) : Colors.transparent), borderRadius: BorderRadius.circular(6), ), child: Text( value == 0 ? "-" : value.toString(), style: TextStyle( fontWeight: (isHighlight || isGold || isBlue) ? FontWeight.w900 : FontWeight.w600, fontSize: 14 * context.sf, color: isGold && value > 0 ? Colors.orange.shade900 : (isBlue && value > 0 ? Colors.blue.shade800 : (isHighlight ? AppTheme.successGreen : textColor)), ), ), ), )); } void _showTeamSelector(BuildContext context) { showModalBottomSheet( context: context, backgroundColor: Theme.of(context).colorScheme.surface, builder: (context) => StreamBuilder>>( stream: _teamController.teamsStream, builder: (context, snapshot) { final teams = snapshot.data ?? []; return ListView.builder( itemCount: teams.length, itemBuilder: (context, i) { final team = teams[i]; final logoUrl = team['image_url']; return ListTile( leading: ClipOval( child: Container( width: 36 * context.sf, height: 36 * context.sf, color: AppTheme.primaryRed.withOpacity(0.1), child: (logoUrl != null && logoUrl.isNotEmpty) ? CachedNetworkImage( imageUrl: logoUrl, fit: BoxFit.cover, placeholder: (context, url) => Icon(Icons.shield, color: AppTheme.primaryRed, size: 20 * context.sf), errorWidget: (context, url, error) => Icon( Icons.shield, color: AppTheme.primaryRed, size: 20 * context.sf), ) : Icon(Icons.shield, color: AppTheme.primaryRed, size: 20 * context.sf), ), ), title: Text(team['name'], style: TextStyle( color: Theme.of(context).colorScheme.onSurface)), onTap: () async { setState(() { _selectedTeamId = team['id'].toString(); _selectedTeamName = team['name']; _selectedTeamLogo = logoUrl; }); await _saveSelectedTeamLocally(); if (context.mounted) Navigator.pop(context); }, ); }, ); }, ), ); } }