import 'package:flutter/material.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import '../../../../core/services/gamification_service.dart'; import '../../../../core/models/class_stats.dart'; import '../../../../core/theme/app_theme_extension.dart'; /// Widget displaying student ranking for a specific class class ClassRankingWidget extends StatefulWidget { final String classId; const ClassRankingWidget({ super.key, required this.classId, }); @override State createState() => _ClassRankingWidgetState(); } class _ClassRankingWidgetState extends State { List _rankings = []; ClassStats? _classStats; bool _loading = true; String _searchQuery = ''; @override void initState() { super.initState(); _loadRankingData(); } Future _loadRankingData() async { try { final results = await Future.wait([ GamificationService.getClassRanking(widget.classId), GamificationService.getClassStats(widget.classId), ]); final rankings = results[0] as List; final classStats = results[1] as ClassStats?; if (mounted) { setState(() { _rankings = rankings; _classStats = classStats; _loading = false; }); } } catch (e) { print('Error loading ranking data: $e'); if (mounted) { setState(() { _loading = false; }); } } } List get _filteredRankings { if (_searchQuery.isEmpty) return _rankings; return _rankings.where((student) => student.studentName.toLowerCase().contains(_searchQuery.toLowerCase()) || student.studentEmail.toLowerCase().contains(_searchQuery.toLowerCase()) ).toList(); } @override Widget build(BuildContext context) { final cs = Theme.of(context).colorScheme; if (_loading) { return const Center( child: CircularProgressIndicator(color: Colors.white), ); } return Container( margin: const EdgeInsets.all(24), child: Column( children: [ // Header with class info Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( gradient: LinearGradient( colors: [cs.primary, cs.primary.withValues(alpha: 0.8)], ), borderRadius: BorderRadius.circular(16), ), child: Column( children: [ Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( _classStats?.className ?? 'Carregando...', style: const TextStyle( color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 4), Text( '${_rankings.length} alunos • Progresso médio: ${((_classStats?.averageProgress ?? 0) * 100).toInt()}%', style: TextStyle( color: Colors.white.withValues(alpha: 0.8), fontSize: 14, ), ), ], ), ), Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.2), borderRadius: BorderRadius.circular(16), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.leaderboard, color: Colors.white, size: 16), const SizedBox(width: 4), Text( 'Ranking', style: const TextStyle( color: Colors.white, fontSize: 12, fontWeight: FontWeight.bold, ), ), ], ), ), ], ), ], ), ), const SizedBox(height: 20), // Search bar Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.white.withValues(alpha: 0.2)), ), child: Row( children: [ Icon(Icons.search, color: Colors.white.withValues(alpha: 0.7)), const SizedBox(width: 12), Expanded( child: TextField( onChanged: (value) { setState(() { _searchQuery = value; }); }, style: const TextStyle(color: Colors.white), decoration: InputDecoration( hintText: 'Buscar aluno...', hintStyle: TextStyle( color: Colors.white.withValues(alpha: 0.5), ), border: InputBorder.none, ), ), ), if (_searchQuery.isNotEmpty) IconButton( icon: Icon(Icons.clear, color: Colors.white.withValues(alpha: 0.7)), onPressed: () { setState(() { _searchQuery = ''; }); }, ), ], ), ), const SizedBox(height: 20), // Ranking list Expanded( child: _filteredRankings.isEmpty ? _buildEmptyState() : ListView.builder( padding: EdgeInsets.zero, itemCount: _filteredRankings.length, itemBuilder: (context, index) { final student = _filteredRankings[index]; final rankPosition = _rankings.indexOf(student) + 1; return Padding( padding: const EdgeInsets.only(bottom: 12), child: _buildStudentRankingCard(student, rankPosition), ); }, ), ), ], ), ); } Widget _buildEmptyState() { if (_searchQuery.isNotEmpty) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.search_off, size: 64, color: Colors.white.withValues(alpha: 0.5), ), const SizedBox(height: 16), Text( 'Nenhum aluno encontrado', style: TextStyle( color: Colors.white.withValues(alpha: 0.7), fontSize: 18, ), ), const SizedBox(height: 8), Text( 'Tente buscar com outros termos', style: TextStyle( color: Colors.white.withValues(alpha: 0.5), fontSize: 14, ), ), ], ), ); } return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.people_outline, size: 64, color: Colors.white.withValues(alpha: 0.5), ), const SizedBox(height: 16), Text( 'Nenhum aluno na turma', style: TextStyle( color: Colors.white.withValues(alpha: 0.7), fontSize: 18, ), ), const SizedBox(height: 8), Text( 'Os alunos aparecerão aqui quando se matricularem', style: TextStyle( color: Colors.white.withValues(alpha: 0.5), fontSize: 14, ), ), ], ), ); } Widget _buildStudentRankingCard(StudentRanking student, int rankPosition) { final cs = Theme.of(context).colorScheme; // Determinar cor baseada na posição Color rankColor; IconData rankIcon; if (rankPosition == 1) { rankColor = Colors.amber; rankIcon = Icons.emoji_events; } else if (rankPosition == 2) { rankColor = Colors.grey.withValues(alpha: 0.8); rankIcon = Icons.workspace_premium; } else if (rankPosition == 3) { rankColor = Colors.brown.withValues(alpha: 0.8); rankIcon = Icons.military_tech; } else { rankColor = Colors.white.withValues(alpha: 0.7); rankIcon = Icons.numbers; } return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(16), border: Border.all(color: Colors.white.withValues(alpha: 0.2)), ), child: Row( children: [ // Rank position Container( width: 40, height: 40, decoration: BoxDecoration( color: rankColor.withValues(alpha: 0.2), borderRadius: BorderRadius.circular(20), border: Border.all(color: rankColor.withValues(alpha: 0.5)), ), child: Center( child: rankPosition <= 3 ? Icon(rankIcon, color: rankColor, size: 20) : Text( '$rankPosition', style: TextStyle( color: rankColor, fontSize: 14, fontWeight: FontWeight.bold, ), ), ), ), const SizedBox(width: 16), // Student info Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( student.studentName, style: const TextStyle( color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 2), Text( student.studentEmail, style: TextStyle( color: Colors.white.withValues(alpha: 0.7), fontSize: 12, ), ), ], ), ), // Stats Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ // Overall score Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: _getScoreColor(student.overallScore).withValues(alpha: 0.2), borderRadius: BorderRadius.circular(8), ), child: Text( '${student.overallScore.toInt()}%', style: TextStyle( color: _getScoreColor(student.overallScore), fontSize: 12, fontWeight: FontWeight.bold, ), ), ), const SizedBox(height: 4), // Quiz completion Row( mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.quiz, color: Colors.white.withValues(alpha: 0.7), size: 12, ), const SizedBox(width: 4), Text( '${student.completedQuizzes}/${student.totalQuizzes}', style: TextStyle( color: Colors.white.withValues(alpha: 0.7), fontSize: 11, ), ), ], ), const SizedBox(height: 2), // Streak if (student.currentStreak > 0) Row( mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.local_fire_department, color: Colors.orange.withValues(alpha: 0.8), size: 12, ), const SizedBox(width: 4), Text( '${student.currentStreak}d', style: TextStyle( color: Colors.orange.withValues(alpha: 0.8), fontSize: 11, ), ), ], ), ], ), ], ), ).animate().slideX(duration: 300.ms, curve: Curves.easeOut); } Color _getScoreColor(double score) { if (score >= 80) return Colors.green; if (score >= 60) return Colors.blue; if (score >= 40) return Colors.orange; return Colors.red; } }