// =========================================================================== // FILE: lib/ui/profile/profile_screen.dart // =========================================================================== import 'dart:ui'; import 'dart:math'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../../core/theme_manager.dart'; import '../../core/app_colors.dart'; import '../../services/storage_service.dart'; import '../../widgets/painters.dart'; import '../../widgets/cyber_border.dart'; class ProfileScreen extends StatefulWidget { const ProfileScreen({super.key}); @override State createState() => _ProfileScreenState(); } class _ProfileScreenState extends State { final TextEditingController _nameController = TextEditingController(); final TextEditingController _passController = TextEditingController(); bool _isLoading = false; bool _obscurePassword = true; String _errorMessage = ""; List _nameSuggestions = []; bool _isGhostMode = false; late User _currentUser; @override void initState() { super.initState(); _currentUser = FirebaseAuth.instance.currentUser!; _loadGhostMode(); } @override void dispose() { _nameController.dispose(); _passController.dispose(); super.dispose(); } Future _loadGhostMode() async { try { var doc = await FirebaseFirestore.instance.collection('leaderboard').doc(_currentUser.uid).get(); if (doc.exists && doc.data()!.containsKey('isGhost')) { setState(() { _isGhostMode = doc.data()!['isGhost']; }); } } catch (e) { debugPrint("Errore caricamento Ghost Mode: $e"); } } Future _toggleGhostMode(bool value) async { setState(() => _isGhostMode = value); try { await FirebaseFirestore.instance.collection('leaderboard').doc(_currentUser.uid).set( {'isGhost': value}, SetOptions(merge: true) ); } catch (e) { debugPrint("Errore salvataggio Ghost Mode: $e"); } } String _getPlayerTitle(int level) { if (level < 10) return "Principiante"; if (level < 20) return "Apprendista"; if (level < 40) return "Sfidante"; if (level < 60) return "Tattico dell'Arena"; if (level < 80) return "Maestro dei Quadrati"; if (level < 100) return "Gran Maestro"; if (level < 130) return "Campione della Griglia"; if (level < 160) return "Entità Digitale"; if (level < 200) return "Oracolo del Codice"; return "Leggenda Suprema"; } Future _handleRegistration() async { final name = _nameController.text.trim().toUpperCase(); final password = _passController.text.trim(); setState(() { _errorMessage = ""; _nameSuggestions.clear(); _isLoading = true; }); if (name.isEmpty || password.isEmpty) { setState(() { _errorMessage = "Compila tutti i campi!"; _isLoading = false; }); return; } if (password.length < 6) { setState(() { _errorMessage = "Password troppo corta (min. 6 caratteri)"; _isLoading = false; }); return; } try { // 1. Controllo univocità del nome var existingUser = await FirebaseFirestore.instance.collection('leaderboard').where('name', isEqualTo: name).get(); if (existingUser.docs.isNotEmpty && existingUser.docs.first.id != _currentUser.uid) { // Nome già preso, generiamo suggerimenti List suggestions = []; int attempts = 0; final rand = Random(); while(suggestions.length < 3 && attempts < 15) { String candidate = "$name${rand.nextInt(99) + 1}"; var check = await FirebaseFirestore.instance.collection('leaderboard').where('name', isEqualTo: candidate).get(); if (check.docs.isEmpty && !suggestions.contains(candidate)) { suggestions.add(candidate); } attempts++; } setState(() { _errorMessage = "Nome già in uso! Scegline un altro:"; _nameSuggestions = suggestions; _isLoading = false; }); return; } // 2. Registrazione sicura final fakeEmail = "${name.replaceAll(' ', '')}@tetraq.game".toLowerCase(); if (_currentUser.isAnonymous) { final credential = EmailAuthProvider.credential(email: fakeEmail, password: password); await _currentUser.linkWithCredential(credential); } await StorageService.instance.savePlayerName(name); await StorageService.instance.syncLeaderboard(); setState(() { _isLoading = false; }); if (mounted) ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Account Protetto con Successo!"), backgroundColor: Colors.green)); } on FirebaseAuthException catch (e) { String msg = "Errore di connessione."; if (e.code == 'email-already-in-use' || e.code == 'credential-already-in-use') msg = "Utente già registrato. Se sei tu, fai il login."; setState(() { _errorMessage = msg; _isLoading = false; }); } catch (e) { setState(() { _errorMessage = "Errore: $e"; _isLoading = false; }); } } Future _deleteAccount() async { bool confirm = await showDialog( context: context, builder: (ctx) => AlertDialog( backgroundColor: Colors.black87, title: const Text("ATTENZIONE", style: TextStyle(color: Colors.redAccent, fontWeight: FontWeight.bold)), content: const Text("Stai per eliminare definitivamente il tuo profilo, i tuoi XP e le statistiche.\nL'operazione è irreversibile.\n\nVuoi procedere?", style: TextStyle(color: Colors.white)), actions: [ TextButton(onPressed: () => Navigator.pop(ctx, false), child: const Text("ANNULLA", style: TextStyle(color: Colors.grey))), ElevatedButton( style: ElevatedButton.styleFrom(backgroundColor: Colors.redAccent), onPressed: () => Navigator.pop(ctx, true), child: const Text("SÌ, ELIMINA", style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)), ), ], ) ) ?? false; if (!confirm) return; setState(() => _isLoading = true); try { // 1. Elimina record da Firestore await FirebaseFirestore.instance.collection('leaderboard').doc(_currentUser.uid).delete(); // 2. Elimina l'utente Auth await _currentUser.delete(); // 3. Pulisci i dati locali sensibili final prefs = await SharedPreferences.getInstance(); await prefs.remove('totalXP'); await prefs.remove('wins'); await prefs.remove('losses'); await prefs.remove('cpuLevel'); await prefs.remove('playerName'); await prefs.remove('favorites'); // 4. Ricrea un anonimo pulito e torna alla Home await FirebaseAuth.instance.signInAnonymously(); await StorageService.instance.init(); if (mounted) { Navigator.of(context).popUntil((route) => route.isFirst); } } on FirebaseAuthException catch (e) { setState(() => _isLoading = false); if (e.code == 'requires-recent-login') { ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Per sicurezza, riavvia l'app prima di eliminare l'account."), backgroundColor: Colors.redAccent)); } else { ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("Errore: ${e.message}"), backgroundColor: Colors.redAccent)); } } } @override Widget build(BuildContext context) { final themeManager = context.watch(); final theme = themeManager.currentColors; final themeType = themeManager.currentThemeType; Color inkColor = const Color(0xFF111122); int wins = StorageService.instance.wins; int losses = StorageService.instance.losses; int totalGames = wins + losses; double winRate = totalGames > 0 ? (wins / totalGames) * 100 : 0.0; int level = StorageService.instance.playerLevel; String title = _getPlayerTitle(level); String playerName = StorageService.instance.playerName; if (playerName.isEmpty) playerName = "GUEST"; bool isAnon = _currentUser.isAnonymous; return Scaffold( backgroundColor: theme.background, appBar: AppBar( title: Text("PROFILO GIOCATORE", style: getSharedTextStyle(themeType, TextStyle(color: themeType == AppThemeType.doodle ? inkColor : theme.text, fontWeight: FontWeight.w900, letterSpacing: 1.5))), backgroundColor: Colors.transparent, elevation: 0, iconTheme: IconThemeData(color: themeType == AppThemeType.doodle ? inkColor : theme.text), ), body: Stack( children: [ if (themeType == AppThemeType.doodle) Positioned.fill(child: CustomPaint(painter: FullScreenGridPainter(Colors.blue.withOpacity(0.15)))), SingleChildScrollView( physics: const BouncingScrollPhysics(), padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // --- SEZIONE 1: IDENTITÀ --- Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: themeType == AppThemeType.doodle ? Colors.white : theme.text.withOpacity(0.05), borderRadius: BorderRadius.circular(20), border: Border.all(color: themeType == AppThemeType.doodle ? inkColor : theme.playerBlue.withOpacity(0.3), width: 2), boxShadow: themeType == AppThemeType.doodle ? [BoxShadow(color: inkColor.withOpacity(0.8), offset: const Offset(4, 4))] : [], ), child: Column( children: [ CircleAvatar(radius: 40, backgroundColor: theme.playerBlue.withOpacity(0.2), child: Icon(Icons.person, size: 45, color: theme.playerBlue)), const SizedBox(height: 15), Text(playerName, style: getSharedTextStyle(themeType, TextStyle(fontSize: 28, fontWeight: FontWeight.w900, color: themeType == AppThemeType.doodle ? inkColor : theme.text))), const SizedBox(height: 5), Text(title, style: getSharedTextStyle(themeType, TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: theme.playerRed))), const SizedBox(height: 15), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(isAnon ? Icons.warning_amber_rounded : Icons.verified_user, color: isAnon ? Colors.orange : Colors.green, size: 18), const SizedBox(width: 5), Text(isAnon ? "Account non protetto" : "Account protetto sul Cloud", style: getSharedTextStyle(themeType, TextStyle(color: isAnon ? Colors.orange : Colors.green, fontWeight: FontWeight.bold, fontSize: 12))), ], ) ], ), ), const SizedBox(height: 20), // --- SEZIONE 2: STATISTICHE AVANZATE --- Row( children: [ Expanded(child: _buildStatCard("Vittorie", "$wins", Icons.emoji_events, Colors.amber, theme, themeType)), const SizedBox(width: 15), Expanded(child: _buildStatCard("Sconfitte", "$losses", Icons.sentiment_very_dissatisfied, theme.playerRed, theme, themeType)), ], ), const SizedBox(height: 15), _buildStatCard("Win Rate Globale", "${winRate.toStringAsFixed(1)}%", Icons.pie_chart, theme.playerBlue, theme, themeType, isWide: true), const SizedBox(height: 25), // --- SEZIONE 3: REGISTRAZIONE (Solo se anonimo) --- if (isAnon) ...[ Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.orange.withOpacity(0.1), borderRadius: BorderRadius.circular(20), border: Border.all(color: Colors.orange, width: 2), ), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text("Metti al sicuro i tuoi progressi!", textAlign: TextAlign.center, style: getSharedTextStyle(themeType, TextStyle(color: themeType == AppThemeType.doodle ? inkColor : Colors.white, fontWeight: FontWeight.bold, fontSize: 16))), const SizedBox(height: 15), TextField( controller: _nameController, textCapitalization: TextCapitalization.characters, textAlign: TextAlign.center, maxLength: 8, style: getSharedTextStyle(themeType, TextStyle(color: themeType == AppThemeType.doodle ? inkColor : Colors.white, fontSize: 20, fontWeight: FontWeight.bold)), decoration: InputDecoration(hintText: "Scegli un Nome", hintStyle: TextStyle(color: Colors.grey.withOpacity(0.6)), filled: true, fillColor: Colors.black12, counterText: "", border: OutlineInputBorder(borderRadius: BorderRadius.circular(15), borderSide: BorderSide.none)), ), const SizedBox(height: 10), TextField( controller: _passController, obscureText: _obscurePassword, textAlign: TextAlign.center, maxLength: 20, style: getSharedTextStyle(themeType, TextStyle(color: themeType == AppThemeType.doodle ? inkColor : Colors.white, fontSize: 20, fontWeight: FontWeight.bold)), decoration: InputDecoration( hintText: "Scegli Password", hintStyle: TextStyle(color: Colors.grey.withOpacity(0.6)), filled: true, fillColor: Colors.black12, counterText: "", border: OutlineInputBorder(borderRadius: BorderRadius.circular(15), borderSide: BorderSide.none), suffixIcon: IconButton(icon: Icon(_obscurePassword ? Icons.visibility : Icons.visibility_off, color: Colors.grey), onPressed: () => setState(() => _obscurePassword = !_obscurePassword)), ), ), if (_errorMessage.isNotEmpty) ...[ const SizedBox(height: 10), Text(_errorMessage, textAlign: TextAlign.center, style: const TextStyle(color: Colors.redAccent, fontWeight: FontWeight.bold)), ], if (_nameSuggestions.isNotEmpty) ...[ const SizedBox(height: 10), Wrap( spacing: 8, alignment: WrapAlignment.center, children: _nameSuggestions.map((s) => ActionChip( label: Text(s, style: const TextStyle(fontWeight: FontWeight.bold)), backgroundColor: theme.playerBlue.withOpacity(0.2), side: BorderSide(color: theme.playerBlue), onPressed: () { _nameController.text = s; _handleRegistration(); }, )).toList(), ) ], const SizedBox(height: 15), _isLoading ? const Center(child: CircularProgressIndicator(color: Colors.orange)) : ElevatedButton( style: ElevatedButton.styleFrom(backgroundColor: Colors.orange, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 15), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15))), onPressed: _handleRegistration, child: Text("SALVA PROFILO", style: getSharedTextStyle(themeType, const TextStyle(fontWeight: FontWeight.w900, letterSpacing: 1.5))), ) ], ), ), const SizedBox(height: 25), ], // --- SEZIONE 4: IMPOSTAZIONI PRIVACY --- Text("PRIVACY", style: getSharedTextStyle(themeType, TextStyle(fontSize: 14, fontWeight: FontWeight.w900, color: themeType == AppThemeType.doodle ? inkColor.withOpacity(0.6) : theme.text.withOpacity(0.5), letterSpacing: 1.5))), const SizedBox(height: 10), SwitchListTile( contentPadding: EdgeInsets.zero, activeColor: theme.playerBlue, title: Text("Modalità Fantasma", style: getSharedTextStyle(themeType, TextStyle(color: themeType == AppThemeType.doodle ? inkColor : theme.text, fontWeight: FontWeight.bold))), subtitle: Text("Nessuno ti vedrà online o potrà invitarti.", style: TextStyle(color: themeType == AppThemeType.doodle ? inkColor.withOpacity(0.6) : theme.text.withOpacity(0.5), fontSize: 12)), value: _isGhostMode, onChanged: _toggleGhostMode, ), const Divider(), // --- SEZIONE 5: GESTIONE PREFERITI --- const SizedBox(height: 15), Text("AMICI PREFERITI", style: getSharedTextStyle(themeType, TextStyle(fontSize: 14, fontWeight: FontWeight.w900, color: themeType == AppThemeType.doodle ? inkColor.withOpacity(0.6) : theme.text.withOpacity(0.5), letterSpacing: 1.5))), const SizedBox(height: 10), _buildFavoritesList(theme, themeType, inkColor), const SizedBox(height: 40), // --- SEZIONE 6: DANGER ZONE --- OutlinedButton.icon( style: OutlinedButton.styleFrom( foregroundColor: Colors.redAccent, side: const BorderSide(color: Colors.redAccent, width: 2), padding: const EdgeInsets.symmetric(vertical: 15), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)), ), icon: const Icon(Icons.delete_forever), label: Text("ELIMINA PROFILO", style: getSharedTextStyle(themeType, const TextStyle(fontWeight: FontWeight.w900, letterSpacing: 1.5))), onPressed: _deleteAccount, ), const SizedBox(height: 30), ], ), ), if (_isLoading && !isAnon) Positioned.fill(child: Container(color: Colors.black54, child: const Center(child: CircularProgressIndicator(color: Colors.redAccent)))), ], ), ); } Widget _buildStatCard(String title, String value, IconData icon, Color color, ThemeColors theme, AppThemeType themeType, {bool isWide = false}) { Color inkColor = const Color(0xFF111122); return Container( padding: const EdgeInsets.all(15), decoration: BoxDecoration( color: themeType == AppThemeType.doodle ? Colors.white : theme.text.withOpacity(0.05), borderRadius: BorderRadius.circular(15), border: Border.all(color: themeType == AppThemeType.doodle ? inkColor : color.withOpacity(0.3), width: 1.5), boxShadow: themeType == AppThemeType.doodle ? [BoxShadow(color: inkColor.withOpacity(0.8), offset: const Offset(3, 3))] : [], ), child: Column( crossAxisAlignment: isWide ? CrossAxisAlignment.center : CrossAxisAlignment.start, children: [ Row( mainAxisSize: MainAxisSize.min, children: [ Icon(icon, color: color, size: 20), const SizedBox(width: 8), Text(title, style: getSharedTextStyle(themeType, TextStyle(color: themeType == AppThemeType.doodle ? inkColor.withOpacity(0.6) : theme.text.withOpacity(0.6), fontSize: 11, fontWeight: FontWeight.bold))), ], ), const SizedBox(height: 10), Center( child: Text(value, style: getSharedTextStyle(themeType, TextStyle(fontSize: 24, fontWeight: FontWeight.w900, color: themeType == AppThemeType.doodle ? inkColor : theme.text))), ), ], ), ); } Widget _buildFavoritesList(ThemeColors theme, AppThemeType themeType, Color inkColor) { final favs = StorageService.instance.favorites; if (favs.isEmpty) { return Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration(borderRadius: BorderRadius.circular(15), border: Border.all(color: Colors.grey.withOpacity(0.3), style: BorderStyle.solid)), child: Center(child: Text("Nessun amico salvato.", style: TextStyle(color: themeType == AppThemeType.doodle ? inkColor.withOpacity(0.5) : theme.text.withOpacity(0.5)))), ); } return ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: favs.length, itemBuilder: (ctx, i) { return Container( margin: const EdgeInsets.only(bottom: 8), decoration: BoxDecoration( color: themeType == AppThemeType.doodle ? Colors.white : theme.text.withOpacity(0.02), borderRadius: BorderRadius.circular(12), border: Border.all(color: themeType == AppThemeType.doodle ? inkColor.withOpacity(0.2) : theme.text.withOpacity(0.1)), ), child: ListTile( leading: Icon(Icons.star, color: Colors.amber.shade600), title: Text(favs[i]['name']!, style: getSharedTextStyle(themeType, TextStyle(color: themeType == AppThemeType.doodle ? inkColor : theme.text, fontWeight: FontWeight.bold))), trailing: IconButton( icon: const Icon(Icons.close, color: Colors.redAccent), onPressed: () async { await StorageService.instance.toggleFavorite(favs[i]['uid']!, favs[i]['name']!); setState(() {}); }, ), ), ); }, ); } } class FullScreenGridPainter extends CustomPainter { final Color gridColor; FullScreenGridPainter(this.gridColor); @override void paint(Canvas canvas, Size size) { final Paint paperGridPaint = Paint()..color = gridColor..strokeWidth = 1.0..style = PaintingStyle.stroke; double paperStep = 20.0; for (double i = 0; i <= size.width; i += paperStep) canvas.drawLine(Offset(i, 0), Offset(i, size.height), paperGridPaint); for (double i = 0; i <= size.height; i += paperStep) canvas.drawLine(Offset(0, i), Offset(size.width, i), paperGridPaint); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => false; }