// lib/screens/login.dart import 'dart:async'; // Import for TimeoutException import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; // Keep for potential future use, though not strictly necessary for the new logic import 'package:environment_monitoring_app/services/api_service.dart'; import 'package:environment_monitoring_app/services/user_preferences_service.dart'; import 'package:environment_monitoring_app/auth_provider.dart'; import 'package:environment_monitoring_app/home_page.dart'; class LoginScreen extends StatefulWidget { const LoginScreen({super.key}); @override State createState() => _LoginScreenState(); } class _LoginScreenState extends State { final _formKey = GlobalKey(); final TextEditingController _emailController = TextEditingController(); final TextEditingController _passwordController = TextEditingController(); bool _isLoading = false; String _errorMessage = ''; String _loadingMessage = ''; // To show dynamic status updates @override void dispose() { _emailController.dispose(); _passwordController.dispose(); super.dispose(); } Future _login() async { if (!_formKey.currentState!.validate()) { return; } setState(() { _isLoading = true; _errorMessage = ''; _loadingMessage = 'Authenticating...'; }); final auth = Provider.of(context, listen: false); final apiService = Provider.of(context, listen: false); final String email = _emailController.text.trim(); final String password = _passwordController.text.trim(); // --- START: MODIFIED Internet-First Strategy with Improved Fallback --- try { // --- Attempt 1: Online Login --- debugPrint("Login attempt: Trying online authentication..."); final Map result = await apiService .login(email, password) .timeout(const Duration(seconds: 10)); if (result['success'] == true) { // --- Online Success --- final String token = result['data']['token']; final Map profile = result['data']['profile']; await auth.login(token, profile, password); // --- FIRST TIME VS SUBSEQUENT LOGIN LOGIC --- if (auth.isFirstLogin) { // >> FIRST TIME: BLOCKING WAIT REQUIRED << // We must ensure configs are present before the user reaches the dashboard // so that the default routing (API/FTP) is set up correctly. setState(() { _loadingMessage = 'Setting up environment...\nFetching server configurations...'; }); try { debugPrint("First time login: Fetching initial configurations (Blocking)..."); // 1. Fetch raw config data from server (API & FTP tables) await apiService.fetchInitialConfigurations(); // 2. Apply default logic to "tick" the active servers // We await this so the DB is ready before navigation. await UserPreferencesService().applyAndSaveDefaultPreferencesIfNeeded(); // 3. Mark first login as done await auth.setIsFirstLogin(false); debugPrint("First time setup complete."); } catch (e) { debugPrint("Warning: Initial setup encountered an error: $e"); // Proceed anyway; user can sync manually later if needed. } } else { // >> SUBSEQUENT LOGIN: NO BLOCKING << // User wants fast access. We skip the 'await' for configs. // The app will rely on local data immediately. // The HomePage will handle background syncing later. debugPrint("Subsequent login: Skipping blocking config fetch. Using local data."); } if (!mounted) return; Navigator.of(context).pushReplacement( MaterialPageRoute(builder: (context) => const HomePage()), ); } else { // --- Online Failure (API Error) --- setState(() { _errorMessage = result['message'] ?? 'Invalid email or password.'; _isLoading = false; }); _showSnackBar(_errorMessage, isError: true); } } on TimeoutException catch (_) { // --- Online Failure (Timeout) --- debugPrint("Login attempt: Online request timed out after 10 seconds. Triggering offline fallback."); _showSnackBar("Slow connection detected. Trying offline login...", isError: true); await _attemptOfflineLogin(auth, email, password); } catch (e) { // --- Online Failure (Other Network Error, e.g., SocketException) --- debugPrint("Login attempt: Network error ($e). Triggering offline fallback immediately."); _showSnackBar("Connection failed. Trying offline login...", isError: true); // FIX: Removed the unreliable connectivity check. Treat all exceptions here as a reason to try offline. await _attemptOfflineLogin(auth, email, password); } } /// Helper function to perform offline validation and update UI. Future _attemptOfflineLogin(AuthProvider auth, String email, String password) async { setState(() { _loadingMessage = 'Verifying offline credentials...'; }); final bool offlineSuccess = await auth.loginOffline(email, password); if (mounted) { setState(() { _isLoading = false; }); if (offlineSuccess) { Navigator.of(context).pushReplacement( MaterialPageRoute(builder: (context) => const HomePage()), ); } else { setState(() { _errorMessage = "Offline login failed. Check credentials or connect to internet to sync."; }); _showSnackBar(_errorMessage, isError: true); } } } void _showSnackBar(String message, {bool isError = false}) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(message), backgroundColor: isError ? Theme.of(context).colorScheme.error : null, ), ); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Login'), centerTitle: true, ), body: Center( child: SingleChildScrollView( padding: const EdgeInsets.all(16.0), child: Form( key: _formKey, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( "PSTW MMS", style: Theme.of(context).textTheme.headlineMedium?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: 32), // --- MODIFICATION START --- // Replaced the original `Center(child: Image.asset(...))` // with this `Container` to make the icon circular and add a shadow. Container( width: 130, height: 130, decoration: BoxDecoration( shape: BoxShape.circle, color: Colors.white, // Background color in case icon has transparency boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.2), // Shadow color spreadRadius: 2, // How far the shadow spreads blurRadius: 8, // The blurriness of the shadow offset: const Offset(0, 4), // Position of shadow (x, y) ), ], image: const DecorationImage( image: AssetImage('assets/icon_4_512x512.png'), fit: BoxFit.cover, // Ensures the image fills the circle ), ), ), // --- MODIFICATION END --- const SizedBox(height: 48), TextFormField( controller: _emailController, decoration: const InputDecoration(labelText: "Email"), keyboardType: TextInputType.emailAddress, validator: (val) => val == null || val.isEmpty ? "Enter your email" : null, ), const SizedBox(height: 16), TextFormField( controller: _passwordController, decoration: const InputDecoration(labelText: "Password"), obscureText: true, validator: (val) => val == null || val.length < 6 ? "Minimum 6 characters" : null, ), const SizedBox(height: 24), _isLoading ? Column( children: [ const CircularProgressIndicator(), const SizedBox(height: 16), Text( _loadingMessage, style: const TextStyle(fontSize: 12, color: Colors.grey), textAlign: TextAlign.center, ), ], ) : ElevatedButton( onPressed: _login, style: ElevatedButton.styleFrom( minimumSize: const Size(double.infinity, 50), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), ), child: const Text( 'Login', style: TextStyle(fontSize: 18), ), ), if (_errorMessage.isNotEmpty) Padding( padding: const EdgeInsets.only(top: 12), child: Text( _errorMessage, style: TextStyle(color: Theme.of(context).colorScheme.error), textAlign: TextAlign.center, ), ), const SizedBox(height: 15), TextButton( onPressed: () { Navigator.pushNamed(context, '/forgot-password'); }, child: const Text('Forgot Password?'), ), TextButton( onPressed: () { Navigator.pushNamed(context, '/register'); }, child: const Text('Don\'t have an account? Register'), ), ], ), ), ), ), ); } }