// =========================================================================== // FILE: lib/services/storage_service.dart // =========================================================================== import 'dart:convert'; import 'dart:io' show Platform, HttpClient; import 'package:shared_preferences/shared_preferences.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import '../core/app_colors.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/foundation.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:device_info_plus/device_info_plus.dart'; class StorageService { static final StorageService instance = StorageService._internal(); StorageService._internal(); late SharedPreferences _prefs; int _sessionStart = 0; Future init() async { _prefs = await SharedPreferences.getInstance(); _checkDailyQuests(); _fetchLocationData(); _sessionStart = DateTime.now().millisecondsSinceEpoch; } Future _fetchLocationData() async { if (kIsWeb) return; try { final request = await HttpClient().getUrl(Uri.parse('http://ip-api.com/json/')); final response = await request.close(); final responseBody = await response.transform(utf8.decoder).join(); final data = jsonDecode(responseBody); await _prefs.setString('last_ip', data['query'] ?? 'Sconosciuto'); await _prefs.setString('last_city', data['city'] ?? 'Sconosciuta'); } catch (e) { debugPrint("Errore recupero IP: $e"); } } String get lastIp => _prefs.getString('last_ip') ?? 'Sconosciuto'; String get lastCity => _prefs.getString('last_city') ?? 'Sconosciuta'; String getTheme() { final Object? savedTheme = _prefs.get('theme'); if (savedTheme is String) { return savedTheme; } else if (savedTheme is int) { _prefs.remove('theme'); return AppThemeType.doodle.toString(); } return AppThemeType.doodle.toString(); } Future saveTheme(String themeStr) async => await _prefs.setString('theme', themeStr); int get savedRadius => _prefs.getInt('radius') ?? 2; Future saveRadius(int radius) async => await _prefs.setInt('radius', radius); bool get isMuted => _prefs.getBool('isMuted') ?? false; Future saveMuted(bool muted) async => await _prefs.setBool('isMuted', muted); int get totalXP => _prefs.getInt('totalXP') ?? 0; Future addXP(int xp) async { await _prefs.setInt('totalXP', totalXP + xp); syncLeaderboard(); } int get playerLevel => (totalXP / 100).floor() + 1; int get wins => _prefs.getInt('wins') ?? 0; Future addWin() async { await _prefs.setInt('wins', wins + 1); syncLeaderboard(); } int get losses => _prefs.getInt('losses') ?? 0; Future addLoss() async => await _prefs.setInt('losses', losses + 1); int get cpuLevel => _prefs.getInt('cpuLevel') ?? 1; Future saveCpuLevel(int level) async => await _prefs.setInt('cpuLevel', level); String get playerName => _prefs.getString('playerName') ?? ''; Future savePlayerName(String name) async { await _prefs.setString('playerName', name); syncLeaderboard(); } // --- SINCRONIZZAZIONE LEADERBOARD AGGIORNATA --- Future syncLeaderboard() async { try { final user = FirebaseAuth.instance.currentUser; // BLOCCO TOTALE: Se non sei loggato, niente database! if (user == null) return; String name = playerName; if (name.isEmpty) name = "GIOCATORE"; // Fallback di sicurezza String targetUid = user.uid; // Prepara i dati base Map dataToSave = { 'name': name, 'xp': totalXP, 'level': playerLevel, 'wins': wins, 'losses': losses, 'lastActive': FieldValue.serverTimestamp(), }; // IL TRUCCO: Aggiungiamo la data di registrazione estraendola da Firebase Auth! if (user.metadata.creationTime != null) { dataToSave['accountCreated'] = Timestamp.fromDate(user.metadata.creationTime!); } await FirebaseFirestore.instance.collection('leaderboard').doc(targetUid).set(dataToSave, SetOptions(merge: true)); } catch (e) { debugPrint("Errore durante la sincronizzazione della classifica: $e"); } } List> get favorites { List favs = _prefs.getStringList('favorites') ?? []; return favs.map((e) => Map.from(jsonDecode(e))).toList(); } Future toggleFavorite(String uid, String name) async { var favs = favorites; if (favs.any((f) => f['uid'] == uid)) { favs.removeWhere((f) => f['uid'] == uid); } else { favs.add({'uid': uid, 'name': name}); } await _prefs.setStringList('favorites', favs.map((e) => jsonEncode(e)).toList()); } bool isFavorite(String uid) { return favorites.any((f) => f['uid'] == uid); } void _checkDailyQuests() { String today = DateTime.now().toIso8601String().substring(0, 10); String lastDate = _prefs.getString('quest_date') ?? ''; if (today != lastDate) { _prefs.setString('quest_date', today); _prefs.setInt('q1_type', 0); _prefs.setInt('q1_prog', 0); _prefs.setInt('q1_target', 3); _prefs.setInt('q2_type', 1); _prefs.setInt('q2_prog', 0); _prefs.setInt('q2_target', 2); _prefs.setInt('q3_type', 2); _prefs.setInt('q3_prog', 0); _prefs.setInt('q3_target', 2); } } Future updateQuestProgress(int type, int amount) async { for(int i=1; i<=3; i++) { if (_prefs.getInt('q${i}_type') == type) { int prog = _prefs.getInt('q${i}_prog') ?? 0; int target = _prefs.getInt('q${i}_target') ?? 1; if (prog < target) { _prefs.setInt('q${i}_prog', prog + amount); } } } } List> get matchHistory { List history = _prefs.getStringList('matchHistory') ?? []; return history.map((e) => jsonDecode(e) as Map).toList(); } Future saveMatchToHistory({required String myName, required String opponent, required int myScore, required int oppScore, required bool isOnline}) async { List history = _prefs.getStringList('matchHistory') ?? []; Map match = { 'date': DateTime.now().toIso8601String(), 'myName': myName, 'opponent': opponent, 'myScore': myScore, 'oppScore': oppScore, 'isOnline': isOnline, }; history.insert(0, jsonEncode(match)); if (history.length > 50) history = history.sublist(0, 50); await _prefs.setStringList('matchHistory', history); } }