import 'dart:io'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import 'package:image_cropper/image_cropper.dart'; import 'package:playmaker/screens/team_stats_page.dart'; import 'package:playmaker/classe/theme.dart'; import '../controllers/team_controller.dart'; import '../models/team_model.dart'; import '../utils/size_extension.dart'; class TeamsPage extends StatefulWidget { const TeamsPage({super.key}); @override State createState() => _TeamsPageState(); } class _TeamsPageState extends State { final TeamController controller = TeamController(); final TextEditingController _searchController = TextEditingController(); String _selectedSeason = 'Todas'; String _currentSort = 'Recentes'; String _searchQuery = ''; @override void dispose() { _searchController.dispose(); super.dispose(); } void _showFilterDialog(BuildContext context) { showDialog( context: context, builder: (context) { return StatefulBuilder( builder: (context, setModalState) { return AlertDialog( backgroundColor: Theme.of(context).colorScheme.surface, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20 * context.sf)), title: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text("Filtros de pesquisa", style: TextStyle(color: Theme.of(context).colorScheme.onSurface, fontSize: 18 * context.sf, fontWeight: FontWeight.bold)), IconButton( icon: Icon(Icons.close, color: Colors.grey, size: 20 * context.sf), onPressed: () => Navigator.pop(context), ) ], ), content: Column( mainAxisSize: MainAxisSize.min, children: [ Divider(color: Colors.grey.withOpacity(0.2)), SizedBox(height: 16 * context.sf), Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: _buildPopupColumn( title: "TEMPORADA", options: ['Todas', '2023/24', '2024/25', '2025/26'], currentValue: _selectedSeason, onSelect: (val) { setState(() => _selectedSeason = val); setModalState(() {}); }, ), ), SizedBox(width: 20 * context.sf), Expanded( child: _buildPopupColumn( title: "ORDENAR POR", options: ['Recentes', 'Nome', 'Tamanho'], currentValue: _currentSort, onSelect: (val) { setState(() => _currentSort = val); setModalState(() {}); }, ), ), ], ), ], ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text("CONCLUÍDO", style: TextStyle(color: AppTheme.primaryRed, fontWeight: FontWeight.bold, fontSize: 14 * context.sf)), ), ], ); }, ); }, ); } Widget _buildPopupColumn({required String title, required List options, required String currentValue, required Function(String) onSelect}) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(title, style: TextStyle(color: Colors.grey, fontSize: 11 * context.sf, fontWeight: FontWeight.bold)), SizedBox(height: 12 * context.sf), ...options.map((opt) { final isSelected = currentValue == opt; return InkWell( onTap: () => onSelect(opt), child: Padding( padding: EdgeInsets.symmetric(vertical: 8.0 * context.sf), child: Text( opt, style: TextStyle( color: isSelected ? AppTheme.primaryRed : Theme.of(context).colorScheme.onSurface.withOpacity(0.7), fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, fontSize: 14 * context.sf, ), ), ), ); }).toList(), ], ); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Theme.of(context).scaffoldBackgroundColor, appBar: AppBar( title: Text("Minhas Equipas", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20 * context.sf)), backgroundColor: Theme.of(context).scaffoldBackgroundColor, elevation: 0, actions: [ IconButton( icon: Icon(Icons.filter_list, color: AppTheme.primaryRed, size: 24 * context.sf), onPressed: () => _showFilterDialog(context), ), ], ), body: Column( children: [ _buildSearchBar(), Expanded(child: _buildTeamsList()), ], ), floatingActionButton: FloatingActionButton( heroTag: 'add_team_btn', backgroundColor: AppTheme.primaryRed, child: Icon(Icons.add, color: Colors.white, size: 24 * context.sf), onPressed: () => _showCreateDialog(context), ), ); } Widget _buildSearchBar() { return Padding( padding: EdgeInsets.all(16.0 * context.sf), child: TextField( controller: _searchController, onChanged: (v) => setState(() => _searchQuery = v.toLowerCase()), style: TextStyle(fontSize: 16 * context.sf, color: Theme.of(context).colorScheme.onSurface), decoration: InputDecoration( hintText: 'Pesquisar equipa...', hintStyle: TextStyle(fontSize: 16 * context.sf, color: Colors.grey), prefixIcon: Icon(Icons.search, color: AppTheme.primaryRed, size: 22 * context.sf), filled: true, fillColor: Theme.of(context).colorScheme.surface, border: OutlineInputBorder(borderRadius: BorderRadius.circular(15 * context.sf), borderSide: BorderSide.none), ), ), ); } // 👇 AGORA USA FUTUREBUILDER E É MUITO MAIS RÁPIDO 👇 Widget _buildTeamsList() { return FutureBuilder>>( future: controller.getTeamsWithStats(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) return Center(child: CircularProgressIndicator(color: AppTheme.primaryRed)); if (!snapshot.hasData || snapshot.data!.isEmpty) return Center(child: Text("Nenhuma equipa encontrada.", style: TextStyle(fontSize: 16 * context.sf, color: Theme.of(context).colorScheme.onSurface))); var data = List>.from(snapshot.data!); if (_selectedSeason != 'Todas') data = data.where((t) => t['season'] == _selectedSeason).toList(); if (_searchQuery.isNotEmpty) data = data.where((t) => t['name'].toString().toLowerCase().contains(_searchQuery)).toList(); data.sort((a, b) { bool favA = a['is_favorite'] ?? false; bool favB = b['is_favorite'] ?? false; if (favA && !favB) return -1; if (!favA && favB) return 1; if (_currentSort == 'Nome') return a['name'].toString().compareTo(b['name'].toString()); else return (b['created_at'] ?? '').toString().compareTo((a['created_at'] ?? '').toString()); }); return RefreshIndicator( color: AppTheme.primaryRed, onRefresh: () async => setState(() {}), // Puxa para baixo para recarregar child: ListView.builder( padding: EdgeInsets.symmetric(horizontal: 16 * context.sf), itemCount: data.length, itemBuilder: (context, index) { final team = Team.fromMap(data[index]); return GestureDetector( onTap: () => Navigator.push(context, MaterialPageRoute(builder: (context) => TeamStatsPage(team: team))).then((_) => setState(() {})), child: TeamCard( team: team, controller: controller, onFavoriteTap: () async { await controller.toggleFavorite(team.id, team.isFavorite); setState(() {}); // Atualiza a estrela na hora }, onDelete: () => setState(() {}), // Atualiza a lista quando apaga sf: context.sf, ), ); }, ), ); }, ); } void _showCreateDialog(BuildContext context) { showDialog( context: context, builder: (context) => CreateTeamDialog( sf: context.sf, onConfirm: (name, season, imageFile) async { await controller.createTeam(name, season, imageFile); setState(() {}); // 👇 Atualiza a lista quando acaba de criar a equipa! } ), ); } } // --- TEAM CARD --- class TeamCard extends StatelessWidget { final Team team; final TeamController controller; final VoidCallback onFavoriteTap; final VoidCallback onDelete; // 👇 Avisa o pai quando é apagado final double sf; const TeamCard({ super.key, required this.team, required this.controller, required this.onFavoriteTap, required this.onDelete, required this.sf, }); @override Widget build(BuildContext context) { final bgColor = Theme.of(context).cardTheme.color ?? Theme.of(context).colorScheme.surface; final textColor = Theme.of(context).colorScheme.onSurface; return Container( margin: EdgeInsets.only(bottom: 12 * sf), decoration: BoxDecoration( color: bgColor, borderRadius: BorderRadius.circular(15 * sf), border: Border.all(color: Colors.grey.withOpacity(0.15)), boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10 * sf)] ), child: Material( color: Colors.transparent, borderRadius: BorderRadius.circular(15 * sf), child: ListTile( contentPadding: EdgeInsets.symmetric(horizontal: 16 * sf, vertical: 8 * sf), leading: Stack( clipBehavior: Clip.none, children: [ CircleAvatar( radius: 28 * sf, backgroundColor: Colors.grey.withOpacity(0.2), backgroundImage: (team.imageUrl.isNotEmpty && team.imageUrl.startsWith('http')) ? NetworkImage(team.imageUrl) : null, child: (team.imageUrl.isEmpty || !team.imageUrl.startsWith('http')) ? Text( team.imageUrl.isEmpty ? "🏀" : team.imageUrl, style: TextStyle(fontSize: 24 * sf), ) : null, ), Positioned( left: -15 * sf, top: -10 * sf, child: IconButton( icon: Icon( team.isFavorite ? Icons.star : Icons.star_border, color: team.isFavorite ? AppTheme.warningAmber : Theme.of(context).colorScheme.onSurface.withOpacity(0.2), size: 28 * sf, shadows: [Shadow(color: Colors.black.withOpacity(team.isFavorite ? 0.3 : 0.1), blurRadius: 4 * sf)], ), onPressed: onFavoriteTap, ), ), ], ), title: Text( team.name, style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16 * sf, color: textColor), overflow: TextOverflow.ellipsis, ), subtitle: Padding( padding: EdgeInsets.only(top: 6.0 * sf), child: Row( children: [ Icon(Icons.groups_outlined, size: 16 * sf, color: Colors.grey), SizedBox(width: 4 * sf), // 👇 ESTATÍSTICA MUITO MAIS LEVE. LÊ O VALOR DIRETAMENTE! 👇 Text( "${team.playerCount} Jogs.", style: TextStyle( color: team.playerCount > 0 ? AppTheme.successGreen : AppTheme.warningAmber, fontWeight: FontWeight.bold, fontSize: 13 * sf, ), ), SizedBox(width: 8 * sf), Expanded( child: Text("| ${team.season}", style: TextStyle(color: Colors.grey, fontSize: 13 * sf), overflow: TextOverflow.ellipsis), ), ], ), ), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ IconButton( tooltip: 'Ver Estatísticas', icon: Icon(Icons.bar_chart_rounded, color: Colors.blue, size: 24 * sf), onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (context) => TeamStatsPage(team: team))).then((_) => onDelete()), // Atualiza se algo mudou ), IconButton( tooltip: 'Eliminar Equipa', icon: Icon(Icons.delete_outline, color: AppTheme.primaryRed, size: 24 * sf), onPressed: () => _confirmDelete(context, sf, bgColor, textColor), ), ], ), ), ), ); } void _confirmDelete(BuildContext context, double sf, Color cardColor, Color textColor) { showDialog( context: context, builder: (ctx) => AlertDialog( backgroundColor: cardColor, surfaceTintColor: Colors.transparent, title: Text('Eliminar Equipa?', style: TextStyle(fontSize: 18 * sf, fontWeight: FontWeight.bold, color: textColor)), content: Text('Tens a certeza que queres eliminar "${team.name}"?', style: TextStyle(fontSize: 14 * sf, color: textColor)), actions: [ TextButton( onPressed: () => Navigator.pop(ctx), child: Text('Cancelar', style: TextStyle(fontSize: 14 * sf, color: Colors.grey)), ), TextButton( onPressed: () { // ⚡ 1. FECHA LOGO O POP-UP! Navigator.pop(ctx); // ⚡ 2. AVISA O PAI PARA ESCONDER A EQUIPA DO ECRÃ NA HORA! onDelete(); // 3. APAGA NO FUNDO (Sem o utilizador ficar à espera) controller.deleteTeam(team.id).catchError((e) { if (context.mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Erro ao eliminar: $e'), backgroundColor: Colors.red)); }); }, child: Text('Eliminar', style: TextStyle(color: AppTheme.primaryRed, fontSize: 14 * sf)), ), ], ), ); } } // --- DIALOG DE CRIAÇÃO (COM CROPPER E ESCUDO) --- class CreateTeamDialog extends StatefulWidget { final Function(String name, String season, File? imageFile) onConfirm; final double sf; const CreateTeamDialog({super.key, required this.onConfirm, required this.sf}); @override State createState() => _CreateTeamDialogState(); } class _CreateTeamDialogState extends State { final TextEditingController _nameController = TextEditingController(); String _selectedSeason = '2024/25'; File? _selectedImage; bool _isLoading = false; bool _isPickerActive = false; // 👇 ESCUDO ANTI-DUPLO-CLIQUE Future _pickImage() async { if (_isPickerActive) return; setState(() => _isPickerActive = true); try { final ImagePicker picker = ImagePicker(); final XFile? pickedFile = await picker.pickImage(source: ImageSource.gallery); if (pickedFile != null) { // 👇 USA O CROPPER QUE CONFIGURASTE PARA AS CARAS CroppedFile? croppedFile = await ImageCropper().cropImage( sourcePath: pickedFile.path, aspectRatio: const CropAspectRatio(ratioX: 1, ratioY: 1), uiSettings: [ AndroidUiSettings( toolbarTitle: 'Recortar Logo', toolbarColor: AppTheme.primaryRed, toolbarWidgetColor: Colors.white, initAspectRatio: CropAspectRatioPreset.square, lockAspectRatio: true, hideBottomControls: true, ), IOSUiSettings(title: 'Recortar Logo', aspectRatioLockEnabled: true, resetButtonHidden: true), ], ); if (croppedFile != null && mounted) { setState(() { _selectedImage = File(croppedFile.path); }); } } } finally { if (mounted) setState(() => _isPickerActive = false); } } @override Widget build(BuildContext context) { return AlertDialog( backgroundColor: Theme.of(context).colorScheme.surface, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15 * widget.sf)), title: Text('Nova Equipa', style: TextStyle(fontSize: 18 * widget.sf, fontWeight: FontWeight.bold, color: Theme.of(context).colorScheme.onSurface)), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ GestureDetector( onTap: _pickImage, child: Stack( children: [ CircleAvatar( radius: 40 * widget.sf, backgroundColor: Theme.of(context).colorScheme.onSurface.withOpacity(0.05), backgroundImage: _selectedImage != null ? FileImage(_selectedImage!) : null, child: _selectedImage == null ? Icon(Icons.add_photo_alternate_outlined, size: 30 * widget.sf, color: Colors.grey) : null, ), if (_selectedImage == null) Positioned( bottom: 0, right: 0, child: Container( padding: EdgeInsets.all(4 * widget.sf), decoration: const BoxDecoration(color: AppTheme.primaryRed, shape: BoxShape.circle), child: Icon(Icons.add, color: Colors.white, size: 16 * widget.sf), ), ), ], ), ), SizedBox(height: 10 * widget.sf), Text("Logótipo (Opcional)", style: TextStyle(fontSize: 12 * widget.sf, color: Colors.grey)), SizedBox(height: 20 * widget.sf), TextField(controller: _nameController, style: TextStyle(fontSize: 14 * widget.sf, color: Theme.of(context).colorScheme.onSurface), decoration: InputDecoration(labelText: 'Nome da Equipa', labelStyle: TextStyle(fontSize: 14 * widget.sf)), textCapitalization: TextCapitalization.words), SizedBox(height: 15 * widget.sf), DropdownButtonFormField( dropdownColor: Theme.of(context).colorScheme.surface, value: _selectedSeason, decoration: InputDecoration(labelText: 'Temporada', labelStyle: TextStyle(fontSize: 14 * widget.sf)), style: TextStyle(fontSize: 14 * widget.sf, color: Theme.of(context).colorScheme.onSurface), items: ['2023/24', '2024/25', '2025/26'].map((s) => DropdownMenuItem(value: s, child: Text(s))).toList(), onChanged: (val) => setState(() => _selectedSeason = val!), ), ], ), ), actions: [ TextButton(onPressed: () => Navigator.pop(context), child: Text('Cancelar', style: TextStyle(fontSize: 14 * widget.sf, color: Colors.grey))), ElevatedButton( style: ElevatedButton.styleFrom(backgroundColor: AppTheme.primaryRed, padding: EdgeInsets.symmetric(horizontal: 16 * widget.sf, vertical: 10 * widget.sf)), onPressed: _isLoading ? null : () async { if (_nameController.text.trim().isNotEmpty) { setState(() => _isLoading = true); await widget.onConfirm(_nameController.text.trim(), _selectedSeason, _selectedImage); if (context.mounted) Navigator.pop(context); } }, child: _isLoading ? SizedBox(width: 16 * widget.sf, height: 16 * widget.sf, child: const CircularProgressIndicator(color: Colors.white, strokeWidth: 2)) : Text('Criar', style: TextStyle(color: Colors.white, fontSize: 14 * widget.sf)), ), ], ); } }