import 'dart:io'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:image_picker/image_picker.dart'; import 'package:path_provider/path_provider.dart'; import 'package:path/path.dart' as p; import 'package:environment_monitoring_app/auth_provider.dart'; import 'package:environment_monitoring_app/services/api_service.dart'; class ProfileScreen extends StatefulWidget { const ProfileScreen({super.key}); @override State createState() => _ProfileScreenState(); } class _ProfileScreenState extends State { // FIX: Removed direct instantiation of ApiService bool _isLoading = false; String _errorMessage = ''; File? _profileImageFile; // FIX: Use late initialization to retrieve the service instance. late ApiService _apiService; @override void initState() { super.initState(); // FIX: Retrieve the ApiService instance after the context is fully available. WidgetsBinding.instance.addPostFrameCallback((_) { _apiService = Provider.of(context, listen: false); _loadLocalProfileImage().then((_) { // If no profile data is available at all, trigger a refresh if (Provider.of(context, listen: false).profileData == null) { _refreshProfile(); } }); }); } /// Refreshes only the profile data using the dedicated provider method. Future _refreshProfile() async { setState(() { _isLoading = true; _errorMessage = ''; }); try { final auth = Provider.of(context, listen: false); // Call the efficient refreshProfile method instead of the full sync. await auth.refreshProfile(); // After syncing, reload the potentially new profile image await _loadLocalProfileImage(); } catch (e) { if (mounted) { setState(() { _errorMessage = 'An unexpected error occurred during sync: ${e.toString()}'; }); } } finally { if (mounted) { setState(() { _isLoading = false; }); } } } /// Loads the profile image from the local cache or downloads it if not present. Future _loadLocalProfileImage() async { final auth = Provider.of(context, listen: false); final String? serverImagePath = auth.profileData?['profile_picture']; if (serverImagePath != null && serverImagePath.isNotEmpty) { final String localFileName = p.basename(serverImagePath); final Directory appDocDir = await getApplicationDocumentsDirectory(); final String localFilePath = p.join(appDocDir.path, 'profile_pictures', localFileName); final File localFile = File(localFilePath); if (await localFile.exists()) { if (mounted) setState(() => _profileImageFile = localFile); } else { final String fullImageUrl = ApiService.imageBaseUrl + serverImagePath; // FIX: Use the injected _apiService instance final downloadedFile = await _apiService.downloadProfilePicture(fullImageUrl, localFilePath); if (downloadedFile != null && mounted) { setState(() => _profileImageFile = downloadedFile); } } } else { if (mounted) setState(() => _profileImageFile = null); } } /// Shows a modal bottom sheet for selecting an image source. Future _showImageSourceSelection() async { showModalBottomSheet( context: context, builder: (BuildContext context) { return SafeArea( child: Column( mainAxisSize: MainAxisSize.min, children: [ ListTile( leading: const Icon(Icons.camera_alt), title: const Text('Take a photo'), onTap: () { Navigator.pop(context); _pickAndUploadImage(ImageSource.camera); }, ), ListTile( leading: const Icon(Icons.photo_library), title: const Text('Choose from gallery'), onTap: () { Navigator.pop(context); _pickAndUploadImage(ImageSource.gallery); }, ), ], ), ); }, ); } /// Picks an image and initiates the upload process. Future _pickAndUploadImage(ImageSource source) async { final ImagePicker picker = ImagePicker(); final XFile? pickedFile = await picker.pickImage(source: source, imageQuality: 85, maxWidth: 1024); if (pickedFile != null) { setState(() => _isLoading = true); final File imageFile = File(pickedFile.path); // FIX: Use the injected _apiService instance final uploadResult = await _apiService.uploadProfilePicture(imageFile); if (mounted) { if (uploadResult['success']) { // After a successful upload, efficiently refresh only the profile data. await _refreshProfile(); _showSnackBar("Profile picture updated successfully.", isError: false); } else { setState(() { _errorMessage = uploadResult['message'] ?? 'Failed to upload profile picture.'; }); _showSnackBar(_errorMessage, isError: true); } setState(() => _isLoading = false); } } } void _showSnackBar(String message, {bool isError = false}) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(message), backgroundColor: isError ? Theme.of(context).colorScheme.error : Colors.green, ), ); } } @override Widget build(BuildContext context) { final auth = Provider.of(context); final profileData = auth.profileData; return Scaffold( appBar: AppBar( title: const Text("User Profile"), actions: [ IconButton( icon: const Icon(Icons.refresh), onPressed: _isLoading ? null : _refreshProfile, ), ], ), body: Column( children: [ Expanded( child: _isLoading && profileData == null ? const Center(child: CircularProgressIndicator()) : _errorMessage.isNotEmpty ? Center( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.error_outline, color: Theme.of(context).colorScheme.error, size: 50), const SizedBox(height: 16), Text( _errorMessage, textAlign: TextAlign.center, style: TextStyle(color: Theme.of(context).colorScheme.error, fontSize: 16), ), const SizedBox(height: 24), ElevatedButton( onPressed: _isLoading ? null : _refreshProfile, child: const Text('Retry'), ), ], ), ), ) : profileData == null ? const Center(child: Text('No profile data available.')) : RefreshIndicator( onRefresh: _refreshProfile, child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), padding: const EdgeInsets.all(24.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Center( child: GestureDetector( onTap: _isLoading ? null : _showImageSourceSelection, child: Stack( alignment: Alignment.bottomRight, children: [ CircleAvatar( radius: 60, backgroundColor: Theme.of(context).colorScheme.secondary, backgroundImage: _profileImageFile != null ? FileImage(_profileImageFile!) : null, child: _profileImageFile == null ? Icon(Icons.person, size: 80, color: Theme.of(context).colorScheme.onSecondary) : null, ), if (!_isLoading) Positioned( right: 0, bottom: 0, child: CircleAvatar( radius: 20, backgroundColor: Theme.of(context).primaryColor, child: Icon(Icons.edit, color: Theme.of(context).colorScheme.onPrimary, size: 20), ), ), ], ), ), ), const SizedBox(height: 32), _buildProfileSection(context, "Personal Information", [ _buildProfileDetail(context, "Username:", profileData['username']), _buildProfileDetail(context, "Email:", profileData['email']), _buildProfileDetail(context, "First Name:", profileData['first_name']), _buildProfileDetail(context, "Last Name:", profileData['last_name']), _buildProfileDetail(context, "Phone Number:", profileData['phone_number']), ]), const SizedBox(height: 24), _buildProfileSection(context, "Organizational Details", [ _buildProfileDetail(context, "Role:", profileData['role_name']), _buildProfileDetail(context, "Department:", profileData['department_name']), _buildProfileDetail(context, "Company:", profileData['company_name']), _buildProfileDetail(context, "Position:", profileData['position_name']), ]), const SizedBox(height: 24), _buildProfileSection(context, "Account Status", [ _buildProfileDetail(context, "Account Status:", profileData['account_status']), _buildProfileDetail(context, "Registered On:", profileData['date_registered']), ]), ], ), ), ), ), Padding( padding: const EdgeInsets.all(24.0), child: Center( child: ElevatedButton.icon( icon: const Icon(Icons.logout), label: const Text("Logout"), onPressed: () { showDialog( context: context, barrierDismissible: false, builder: (dialogContext) => AlertDialog( title: const Text("Confirm Logout"), content: const Text("Are you sure you want to log out?"), actions: [ TextButton( onPressed: () => Navigator.pop(dialogContext), child: const Text("Cancel"), ), ElevatedButton( onPressed: () { Navigator.pop(dialogContext); auth.logout(); Navigator.pushNamedAndRemoveUntil(context, '/', (route) => false); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.red[700], foregroundColor: Colors.white, ), child: const Text("Logout"), ), ], ), ); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.red[700], foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), ), ), ), ), ], ), ); } Widget _buildProfileSection(BuildContext context, String title, List details) { return Card( elevation: 4, margin: const EdgeInsets.symmetric(vertical: 8.0), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: Theme.of(context).textTheme.headlineSmall?.copyWith(fontWeight: FontWeight.bold, color: Theme.of(context).primaryColor), ), const Divider(height: 20, thickness: 1.5), ...details, ], ), ), ); } Widget _buildProfileDetail(BuildContext context, String label, dynamic value) { return Padding( padding: const EdgeInsets.symmetric(vertical: 8.0), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( flex: 2, child: Text( label, style: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold), ), ), const SizedBox(width: 16), Expanded( flex: 3, child: Text( value?.toString() ?? 'N/A', style: Theme.of(context).textTheme.bodyLarge, ), ), ], ), ); } }