import 'package:flutter/material.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_storage/firebase_storage.dart'; import 'package:image_picker/image_picker.dart'; import 'dart:io'; import 'package:kzeduca_app/models/app_models.dart' as app_models; import 'package:kzeduca_app/models/transaction.dart' as txn; import 'package:kzeduca_app/services/hive_service.dart'; enum UserStateStatus { idle, loading, loaded, error, } class UserStateService extends ChangeNotifier { final FirebaseFirestore _db = FirebaseFirestore.instance; final FirebaseAuth _auth = FirebaseAuth.instance; final FirebaseStorage _storage = FirebaseStorage.instance; final String _appId = 'default-kzeduca-app'; app_models.AppUser? _currentUser; UserStateStatus _status = UserStateStatus.idle; String? _errorMessage; app_models.AppUser? get currentUser => _currentUser; UserStateStatus get status => _status; String? get errorMessage => _errorMessage; FirebaseAuth get auth => _auth; UserStateService() { _auth.authStateChanges().listen((user) { if (user == null) { _currentUser = null; _status = UserStateStatus.idle; // NOTA: A notificação aqui é síncrona, o que é geralmente seguro no construtor notifyListeners(); } else { // Inicia a busca de dados quando o estado de autenticação muda fetchUserData(user.uid); } }); } CollectionReference get _usersCollection { return _db.collection('artifacts').doc(_appId).collection('users'); } // MÉTODO AUXILIAR PARA NOTIFICAÇÃO SEGURA (CORREÇÃO DO BUG setState during build) void _safeNotifyListeners() { // Garante que a notificação é adiada para a próxima "micropassagem" // do loop de eventos, evitando o erro durante a fase de build. if (hasListeners) { Future.microtask(notifyListeners); } } // ---------------------- // MÉTODO: fetchUserData // ---------------------- Future fetchUserData(String userId) async { _status = UserStateStatus.loading; _safeNotifyListeners(); // Notificação inicial try { final userDoc = await _usersCollection.doc(userId).get(); if (userDoc.exists) { final data = userDoc.data(); if (data != null) { _currentUser = app_models.AppUser.fromMap(data as Map); _status = UserStateStatus.loaded; } else { _status = UserStateStatus.error; _errorMessage = 'User data is empty.'; } } else { // Lida com a criação e atribui o utilizador imediatamente await createUserDocument(_auth.currentUser!, 'Utilizador'); _status = UserStateStatus.loaded; } } catch (e) { _status = UserStateStatus.error; _errorMessage = 'Failed to fetch user data: $e'; } finally { // Notificação final (agora segura) _safeNotifyListeners(); } } // ---------------------- // MÉTODO: updateBalance // ---------------------- Future updateBalance(txn.Transaction transaction) async { if (_currentUser == null) { _errorMessage = 'No user logged in.'; _status = UserStateStatus.error; _safeNotifyListeners(); return; } final newBalance = transaction.type == txn.TransactionType.income ? _currentUser!.currentBalance + transaction.amount : _currentUser!.currentBalance - transaction.amount; try { await _usersCollection.doc(_currentUser!.uid).update({ 'currentBalance': newBalance, }); _currentUser = _currentUser!.copyWith(currentBalance: newBalance); _status = UserStateStatus.loaded; } catch (e) { _status = UserStateStatus.error; _errorMessage = 'Failed to update user balance: $e'; } finally { _safeNotifyListeners(); } } // ---------------------- // MÉTODO: createUserDocument // ---------------------- Future createUserDocument(User firebaseUser, String username) async { final userDocRef = _usersCollection.doc(firebaseUser.uid); final userDoc = await userDocRef.get(); if (!userDoc.exists) { final newUser = app_models.AppUser( uid: firebaseUser.uid, username: username, email: firebaseUser.email, currentBalance: 0.0, ); await userDocRef.set(newUser.toMap()); _currentUser = newUser; _status = UserStateStatus.loaded; _safeNotifyListeners(); // Notificação segura } } // ---------------------- // MÉTODO: recalculateBalanceFromHive // ---------------------- Future recalculateBalanceFromHive() async { final hiveService = HiveService(); final transactions = await hiveService.getTransactions(); double balance = 0.0; for (final t in transactions) { if (t.type == txn.TransactionType.income) { balance += t.amount; } else { balance -= t.amount; } } if (_currentUser != null) { _currentUser = _currentUser!.copyWith(currentBalance: balance); _safeNotifyListeners(); // Notificação segura } } // ---------------------- // MÉTODO: updateUserProfile // ---------------------- Future updateUserProfile({ String? username, String? email, XFile? profileImage, }) async { if (_currentUser == null) return; _status = UserStateStatus.loading; _safeNotifyListeners(); try { final updatedData = {}; if (username != null) { updatedData['username'] = username; } if (email != null) { updatedData['email'] = email; } // Lógica para fazer o upload da imagem de perfil if (profileImage != null) { final storageRef = _storage .ref('artifacts') .child(_appId) .child('users') .child(_currentUser!.uid) .child('profileImage.jpg'); await storageRef.putFile(File(profileImage.path)); final imageUrl = await storageRef.getDownloadURL(); updatedData['profileImage'] = imageUrl; } if (updatedData.isNotEmpty) { await _usersCollection.doc(_currentUser!.uid).update(updatedData); } // Atualiza o objeto localmente para refletir as mudanças _currentUser = _currentUser!.copyWith( username: username ?? _currentUser!.username, email: email ?? _currentUser!.email, profileImage: updatedData['profileImage'] ?? _currentUser!.profileImage, ); _status = UserStateStatus.loaded; } catch (e) { _errorMessage = 'Erro ao atualizar perfil: $e'; _status = UserStateStatus.error; } finally { _safeNotifyListeners(); } } // ---------------------- // MÉTODO: logout // ---------------------- void logout() { _auth.signOut(); _currentUser = null; _status = UserStateStatus.idle; notifyListeners(); // A notificação síncrona é segura no logout } // O método loadUser não precisa de lógica, pois fetchUserData já é chamado pelo listener. Future loadUser() async {} }