import 'package:flutter/material.dart'; import 'package:playmaker/classe/theme.dart'; import 'package:playmaker/grafico%20de%20pizza/grafico.dart'; import 'package:playmaker/pages/gamePage.dart'; import 'package:playmaker/pages/teamPage.dart'; import 'package:playmaker/controllers/team_controller.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import 'package:playmaker/pages/status_page.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../utils/size_extension.dart'; import 'settings_screen.dart'; class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); @override State createState() => _HomeScreenState(); } class _HomeScreenState extends State { int _selectedIndex = 0; final TeamController _teamController = TeamController(); String? _selectedTeamId; String _selectedTeamName = "Selecionar Equipa"; int _teamWins = 0; int _teamLosses = 0; int _teamDraws = 0; final _supabase = Supabase.instance.client; String? _avatarUrl; bool _isMemoryLoaded = false; // 馃憟 A vari谩vel m谩gica que impede o "piscar" inicial @override void initState() { super.initState(); _loadUserAvatar(); } // 馃憞 FUN脟脙O OTIMIZADA: Carrega da mem贸ria instantaneamente e atualiza em background Future _loadUserAvatar() async { // 1. L脢 DA MEM脫RIA R脕PIDA PRIMEIRO final prefs = await SharedPreferences.getInstance(); final savedUrl = prefs.getString('meu_avatar_guardado'); if (mounted) { setState(() { if (savedUrl != null) _avatarUrl = savedUrl; _isMemoryLoaded = true; // Avisa o ecr茫 que a mem贸ria j谩 respondeu! }); } // 2. VAI AO SUPABASE VERIFICAR SE TROCASTE DE FOTO final userId = _supabase.auth.currentUser?.id; if (userId == null) return; try { final data = await _supabase .from('profiles') .select('avatar_url') .eq('id', userId) .maybeSingle(); if (mounted && data != null && data['avatar_url'] != null) { final urlDoSupabase = data['avatar_url']; // Se a foto na base de dados for nova, ele guarda e atualiza! if (urlDoSupabase != savedUrl) { await prefs.setString('meu_avatar_guardado', urlDoSupabase); setState(() { _avatarUrl = urlDoSupabase; }); } } } catch (e) { debugPrint("Erro ao carregar avatar na Home: $e"); } } @override Widget build(BuildContext context) { final List pages = [ _buildHomeContent(context), const GamePage(), const TeamsPage(), const StatusPage(), ]; return Scaffold( backgroundColor: Theme.of(context).scaffoldBackgroundColor, appBar: AppBar( title: Text('PlayMaker', style: TextStyle(fontSize: 20 * context.sf, fontWeight: FontWeight.bold)), backgroundColor: AppTheme.primaryRed, foregroundColor: Colors.white, elevation: 0, leading: Padding( padding: EdgeInsets.all(10.0 * context.sf), child: InkWell( borderRadius: BorderRadius.circular(100), onTap: () async { await Navigator.push( context, MaterialPageRoute(builder: (context) => const SettingsScreen()), ); _loadUserAvatar(); }, // 馃憞 S脫 MOSTRA A IMAGEM OU O BONECO DEPOIS DE LER A MEM脫RIA child: !_isMemoryLoaded // Nos primeiros 0.05 segs, mostra s贸 o c铆rculo de fundo (sem boneco) ? CircleAvatar(backgroundColor: Colors.white.withOpacity(0.2)) // Depois da mem贸ria responder: : _avatarUrl != null && _avatarUrl!.isNotEmpty ? CachedNetworkImage( imageUrl: _avatarUrl!, fadeInDuration: Duration.zero, // Corta o atraso visual! imageBuilder: (context, imageProvider) => CircleAvatar( backgroundColor: Colors.white.withOpacity(0.2), backgroundImage: imageProvider, ), placeholder: (context, url) => CircleAvatar(backgroundColor: Colors.white.withOpacity(0.2)), errorWidget: (context, url, error) => CircleAvatar( backgroundColor: Colors.white.withOpacity(0.2), child: Icon(Icons.person, color: Colors.white, size: 20 * context.sf), ), ) // Se n茫o tiver foto nenhuma, a铆 sim mostra o boneco : CircleAvatar( backgroundColor: Colors.white.withOpacity(0.2), child: Icon(Icons.person, color: Colors.white, size: 20 * context.sf), ), ), ), ), body: IndexedStack( index: _selectedIndex, children: pages, ), bottomNavigationBar: NavigationBar( selectedIndex: _selectedIndex, onDestinationSelected: (index) => setState(() => _selectedIndex = index), backgroundColor: Theme.of(context).colorScheme.surface, surfaceTintColor: Theme.of(context).colorScheme.surfaceTint, elevation: 1, height: 70 * (context.sf < 1.2 ? context.sf : 1.2), destinations: const [ NavigationDestination(icon: Icon(Icons.home_outlined), selectedIcon: Icon(Icons.home_filled), label: 'Home'), NavigationDestination(icon: Icon(Icons.sports_soccer_outlined), selectedIcon: Icon(Icons.sports_soccer), label: 'Jogo'), NavigationDestination(icon: Icon(Icons.people_outline), selectedIcon: Icon(Icons.people), label: 'Equipas'), NavigationDestination(icon: Icon(Icons.insights_outlined), selectedIcon: Icon(Icons.insights), label: 'Status'), ], ), ); } void _showTeamSelector(BuildContext context) { showModalBottomSheet( context: context, backgroundColor: Theme.of(context).colorScheme.surface, shape: RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(20 * context.sf))), builder: (context) { return StreamBuilder>>( stream: _teamController.teamsStream, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) return const SizedBox(height: 200, child: Center(child: CircularProgressIndicator())); if (!snapshot.hasData || snapshot.data!.isEmpty) return SizedBox(height: 200 * context.sf, child: Center(child: Text("Nenhuma equipa criada.", style: TextStyle(color: Theme.of(context).colorScheme.onSurface)))); final teams = snapshot.data!; return ListView.builder( shrinkWrap: true, itemCount: teams.length, itemBuilder: (context, index) { final team = teams[index]; return ListTile( leading: const Icon(Icons.shield, color: AppTheme.primaryRed), title: Text(team['name'], style: TextStyle(color: Theme.of(context).colorScheme.onSurface, fontWeight: FontWeight.bold)), onTap: () { setState(() { _selectedTeamId = team['id'].toString(); _selectedTeamName = team['name']; _teamWins = int.tryParse(team['wins']?.toString() ?? '0') ?? 0; _teamLosses = int.tryParse(team['losses']?.toString() ?? '0') ?? 0; _teamDraws = int.tryParse(team['draws']?.toString() ?? '0') ?? 0; }); Navigator.pop(context); }, ); }, ); }, ); }, ); } Widget _buildHomeContent(BuildContext context) { final double wScreen = MediaQuery.of(context).size.width; final double cardHeight = wScreen * 0.5; final textColor = Theme.of(context).colorScheme.onSurface; return StreamBuilder>>( stream: _selectedTeamId != null ? _supabase.from('player_stats_with_names').stream(primaryKey: ['id']).eq('team_id', _selectedTeamId!) : const Stream.empty(), builder: (context, snapshot) { Map leaders = _calculateLeaders(snapshot.data ?? []); return SingleChildScrollView( child: Padding( padding: EdgeInsets.symmetric(horizontal: 22.0 * context.sf, vertical: 16.0 * context.sf), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ InkWell( onTap: () => _showTeamSelector(context), child: Container( padding: EdgeInsets.all(12 * context.sf), decoration: BoxDecoration( color: Theme.of(context).cardTheme.color, borderRadius: BorderRadius.circular(15 * context.sf), border: Border.all(color: Colors.grey.withOpacity(0.2)) ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row(children: [ 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), ], ), ), ), SizedBox(height: 20 * context.sf), SizedBox( height: cardHeight, child: Row( children: [ Expanded(child: _buildStatCard(context: context, title: 'Mais Pontos', playerName: leaders['pts_name'], statValue: leaders['pts_val'].toString(), statLabel: 'TOTAL', color: AppTheme.statPtsBg, isHighlighted: true)), SizedBox(width: 12 * context.sf), Expanded(child: _buildStatCard(context: context, title: 'Assist锚ncias', playerName: leaders['ast_name'], statValue: leaders['ast_val'].toString(), statLabel: 'TOTAL', color: AppTheme.statAstBg)), ], ), ), SizedBox(height: 12 * context.sf), SizedBox( height: cardHeight, child: Row( children: [ Expanded(child: _buildStatCard(context: context, title: 'Rebotes', playerName: leaders['rbs_name'], statValue: leaders['rbs_val'].toString(), statLabel: 'TOTAL', color: AppTheme.statRebBg)), SizedBox(width: 12 * context.sf), Expanded( child: PieChartCard( victories: _teamWins, defeats: _teamLosses, draws: _teamDraws, title: 'DESEMPENHO', subtitle: 'Temporada', backgroundColor: AppTheme.statPieBg, sf: context.sf ), ), ], ), ), SizedBox(height: 40 * context.sf), Text('Hist贸rico de Jogos', style: TextStyle(fontSize: 20 * context.sf, fontWeight: FontWeight.bold, color: textColor)), SizedBox(height: 16 * context.sf), _selectedTeamName == "Selecionar Equipa" ? Container( width: double.infinity, padding: EdgeInsets.all(24.0 * context.sf), decoration: BoxDecoration( color: Theme.of(context).cardTheme.color ?? Colors.white, borderRadius: BorderRadius.circular(16 * context.sf), border: Border.all(color: Colors.grey.withOpacity(0.1)), boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.04), blurRadius: 10, offset: const Offset(0, 4))], ), child: Column( children: [ Container( padding: EdgeInsets.all(18 * context.sf), decoration: BoxDecoration(color: AppTheme.primaryRed.withOpacity(0.08), shape: BoxShape.circle), child: Icon(Icons.shield_outlined, color: AppTheme.primaryRed, size: 42 * context.sf), ), SizedBox(height: 20 * context.sf), Text("Nenhuma Equipa Ativa", style: TextStyle(fontSize: 18 * context.sf, fontWeight: FontWeight.bold, color: textColor)), SizedBox(height: 8 * context.sf), Text( "Escolha uma equipa no seletor acima para ver as estat铆sticas e o hist贸rico.", textAlign: TextAlign.center, style: TextStyle(fontSize: 13 * context.sf, color: Colors.grey.shade600, height: 1.4), ), SizedBox(height: 24 * context.sf), SizedBox( width: double.infinity, height: 48 * context.sf, child: ElevatedButton.icon( onPressed: () => _showTeamSelector(context), style: ElevatedButton.styleFrom( backgroundColor: AppTheme.primaryRed, foregroundColor: Colors.white, elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10 * context.sf)), ), icon: Icon(Icons.touch_app, size: 20 * context.sf), label: Text("Selecionar Agora", style: TextStyle(fontSize: 15 * context.sf, fontWeight: FontWeight.bold)), ), ), ], ), ) : StreamBuilder>>( stream: _supabase.from('games').stream(primaryKey: ['id']).order('game_date', ascending: false), builder: (context, gameSnapshot) { if (gameSnapshot.hasError) return Text("Erro: ${gameSnapshot.error}", style: const TextStyle(color: Colors.red)); if (gameSnapshot.connectionState == ConnectionState.waiting) return const Center(child: CircularProgressIndicator()); final todosOsJogos = gameSnapshot.data ?? []; final gamesList = todosOsJogos.where((game) { String myT = game['my_team']?.toString() ?? ''; String oppT = game['opponent_team']?.toString() ?? ''; String status = game['status']?.toString() ?? ''; return (myT == _selectedTeamName || oppT == _selectedTeamName) && status == 'Terminado'; }).take(3).toList(); if (gamesList.isEmpty) { return Container( width: double.infinity, padding: EdgeInsets.all(20 * context.sf), decoration: BoxDecoration(color: Theme.of(context).cardTheme.color, borderRadius: BorderRadius.circular(14)), alignment: Alignment.center, child: const Text("Ainda n茫o h谩 jogos terminados.", style: TextStyle(color: Colors.grey)), ); } return Column( children: gamesList.map((game) { String dbMyTeam = game['my_team']?.toString() ?? ''; String dbOppTeam = game['opponent_team']?.toString() ?? ''; int dbMyScore = int.tryParse(game['my_score'].toString()) ?? 0; int dbOppScore = int.tryParse(game['opponent_score'].toString()) ?? 0; String opponent; int myScore; int oppScore; if (dbMyTeam == _selectedTeamName) { opponent = dbOppTeam; myScore = dbMyScore; oppScore = dbOppScore; } else { opponent = dbMyTeam; myScore = dbOppScore; oppScore = dbMyScore; } String rawDate = game['game_date']?.toString() ?? '---'; String date = rawDate.length >= 10 ? rawDate.substring(0, 10) : rawDate; String result = 'E'; if (myScore > oppScore) result = 'V'; if (myScore < oppScore) result = 'D'; return _buildGameHistoryCard( context: context, opponent: opponent, result: result, myScore: myScore, oppScore: oppScore, date: date, topPts: game['top_pts_name'] ?? '---', topAst: game['top_ast_name'] ?? '---', topRbs: game['top_rbs_name'] ?? '---', topDef: game['top_def_name'] ?? '---', mvp: game['mvp_name'] ?? '---', ); }).toList(), ); }, ), SizedBox(height: 20 * context.sf), ], ), ), ); }, ); } Map _calculateLeaders(List> data) { Map ptsMap = {}; Map astMap = {}; Map rbsMap = {}; Map namesMap = {}; for (var row in data) { String pid = row['member_id'].toString(); namesMap[pid] = row['player_name']?.toString() ?? "Desconhecido"; ptsMap[pid] = (ptsMap[pid] ?? 0) + (int.tryParse(row['pts']?.toString() ?? '0') ?? 0); astMap[pid] = (astMap[pid] ?? 0) + (int.tryParse(row['ast']?.toString() ?? '0') ?? 0); rbsMap[pid] = (rbsMap[pid] ?? 0) + (int.tryParse(row['rbs']?.toString() ?? '0') ?? 0); } if (ptsMap.isEmpty) return {'pts_name': '---', 'pts_val': 0, 'ast_name': '---', 'ast_val': 0, 'rbs_name': '---', 'rbs_val': 0}; String getBest(Map map) { var bestId = map.entries.reduce((a, b) => a.value > b.value ? a : b).key; return namesMap[bestId]!; } int getBestVal(Map map) => map.values.reduce((a, b) => a > b ? a : b); return {'pts_name': getBest(ptsMap), 'pts_val': getBestVal(ptsMap), 'ast_name': getBest(astMap), 'ast_val': getBestVal(astMap), 'rbs_name': getBest(rbsMap), 'rbs_val': getBestVal(rbsMap)}; } Widget _buildStatCard({required BuildContext context, required String title, required String playerName, required String statValue, required String statLabel, required Color color, bool isHighlighted = false}) { return Card( elevation: 4, margin: EdgeInsets.zero, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14), side: isHighlighted ? const BorderSide(color: AppTheme.warningAmber, width: 2) : BorderSide.none), child: Container( decoration: BoxDecoration(borderRadius: BorderRadius.circular(14), gradient: LinearGradient(begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [color.withOpacity(0.9), color])), child: LayoutBuilder( builder: (context, constraints) { final double ch = constraints.maxHeight; final double cw = constraints.maxWidth; return Padding( padding: EdgeInsets.all(cw * 0.06), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(title.toUpperCase(), style: TextStyle(fontSize: ch * 0.06, fontWeight: FontWeight.bold, color: Colors.white70), maxLines: 1, overflow: TextOverflow.ellipsis), SizedBox(height: ch * 0.011), SizedBox( width: double.infinity, child: FittedBox( fit: BoxFit.scaleDown, alignment: Alignment.centerLeft, child: Text(playerName, style: TextStyle(fontSize: ch * 0.08, fontWeight: FontWeight.bold, color: Colors.white)), ), ), const Spacer(), Center(child: FittedBox(fit: BoxFit.scaleDown, child: Text(statValue, style: TextStyle(fontSize: ch * 0.18, fontWeight: FontWeight.bold, color: Colors.white, height: 1.0)))), SizedBox(height: ch * 0.015), Center(child: Text(statLabel, style: TextStyle(fontSize: ch * 0.05, color: Colors.white70))), const Spacer(), Container( width: double.infinity, padding: EdgeInsets.symmetric(vertical: ch * 0.035), decoration: BoxDecoration(color: Colors.white24, borderRadius: BorderRadius.circular(ch * 0.03)), child: Center(child: Text('DETALHES', style: TextStyle(color: Colors.white, fontSize: ch * 0.05, fontWeight: FontWeight.bold))) ), ], ), ); } ), ), ); } Widget _buildGameHistoryCard({ required BuildContext context, required String opponent, required String result, required int myScore, required int oppScore, required String date, required String topPts, required String topAst, required String topRbs, required String topDef, required String mvp }) { bool isWin = result == 'V'; bool isDraw = result == 'E'; Color statusColor = isWin ? AppTheme.successGreen : (isDraw ? AppTheme.warningAmber : AppTheme.oppTeamRed); final bgColor = Theme.of(context).cardTheme.color; final textColor = Theme.of(context).colorScheme.onSurface; return Container( margin: EdgeInsets.only(bottom: 14 * context.sf), decoration: BoxDecoration( color: bgColor, borderRadius: BorderRadius.circular(16), border: Border.all(color: Colors.grey.withOpacity(0.1)), boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.04), blurRadius: 8, offset: const Offset(0, 4))], ), child: Column( children: [ Padding( padding: EdgeInsets.all(14 * context.sf), child: Row( children: [ Container( width: 36 * context.sf, height: 36 * context.sf, decoration: BoxDecoration(color: statusColor.withOpacity(0.15), shape: BoxShape.circle), child: Center(child: Text(result, style: TextStyle(color: statusColor, fontWeight: FontWeight.bold, fontSize: 16 * context.sf))), ), SizedBox(width: 14 * context.sf), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(date, style: TextStyle(fontSize: 11 * context.sf, color: Colors.grey, fontWeight: FontWeight.w600)), SizedBox(height: 6 * context.sf), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded(child: Text(_selectedTeamName == "Selecionar Equipa" ? "Minha Equipa" : _selectedTeamName, style: TextStyle(fontSize: 14 * context.sf, fontWeight: FontWeight.bold, color: textColor), maxLines: 1, overflow: TextOverflow.ellipsis)), Padding( padding: EdgeInsets.symmetric(horizontal: 8 * context.sf), child: Container( padding: EdgeInsets.symmetric(horizontal: 8 * context.sf, vertical: 4 * context.sf), decoration: BoxDecoration(color: Theme.of(context).colorScheme.onSurface.withOpacity(0.05), borderRadius: BorderRadius.circular(8)), child: Text('$myScore - $oppScore', style: TextStyle(fontSize: 15 * context.sf, fontWeight: FontWeight.w900, letterSpacing: 1.5, color: textColor)), ), ), Expanded(child: Text(opponent, style: TextStyle(fontSize: 14 * context.sf, fontWeight: FontWeight.bold, color: textColor), textAlign: TextAlign.right, maxLines: 1, overflow: TextOverflow.ellipsis)), ], ), ], ), ), ], ), ), Divider(height: 1, color: Colors.grey.withOpacity(0.1), thickness: 1.5), Container( width: double.infinity, padding: EdgeInsets.symmetric(horizontal: 16 * context.sf, vertical: 12 * context.sf), decoration: BoxDecoration(color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.only(bottomLeft: Radius.circular(16), bottomRight: Radius.circular(16))), child: Column( children: [ Row( children: [ Expanded(child: _buildGridStatRow(context, Icons.workspace_premium, Colors.amber.shade700, "MVP", mvp, isMvp: true)), Expanded(child: _buildGridStatRow(context, Icons.shield, Colors.deepOrange.shade700, "Defesa", topDef)), ], ), SizedBox(height: 8 * context.sf), Row( children: [ Expanded(child: _buildGridStatRow(context, Icons.bolt, Colors.blue.shade700, "Pontos", topPts)), Expanded(child: _buildGridStatRow(context, Icons.trending_up, Colors.purple.shade700, "Rebotes", topRbs)), ], ), SizedBox(height: 8 * context.sf), Row( children: [ Expanded(child: _buildGridStatRow(context, Icons.star, Colors.green.shade700, "Assists", topAst)), const Expanded(child: SizedBox()), ], ), ], ), ) ], ), ); } Widget _buildGridStatRow(BuildContext context, IconData icon, Color color, String label, String value, {bool isMvp = false}) { return Row( children: [ Icon(icon, size: 14 * context.sf, color: color), SizedBox(width: 4 * context.sf), Text('$label: ', style: TextStyle(fontSize: 11 * context.sf, color: Colors.grey, fontWeight: FontWeight.bold)), Expanded( child: Text( value, style: TextStyle( fontSize: 11 * context.sf, color: isMvp ? AppTheme.warningAmber : Theme.of(context).colorScheme.onSurface, fontWeight: FontWeight.bold ), maxLines: 1, overflow: TextOverflow.ellipsis ) ), ], ); } }