// lib/auth_provider.dart

import 'package:flutter/foundation.dart'; // Import for compute function
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'dart:convert';
import 'package:bcrypt/bcrypt.dart'; // Import bcrypt
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; // NEW: Import secure storage

import 'package:environment_monitoring_app/services/api_service.dart';
import 'package:environment_monitoring_app/services/base_api_service.dart';
import 'package:environment_monitoring_app/services/server_config_service.dart';
import 'package:environment_monitoring_app/services/retry_service.dart';
import 'package:environment_monitoring_app/services/user_preferences_service.dart';

class AuthProvider with ChangeNotifier {
  late final ApiService _apiService;
  late final DatabaseHelper _dbHelper;
  late final ServerConfigService _serverConfigService;
  late final RetryService _retryService;
  final UserPreferencesService _userPreferencesService = UserPreferencesService();

  // NEW: Initialize secure storage
  final _secureStorage = const FlutterSecureStorage();
  static const _passwordStorageKey = 'user_password';

  // --- Session & Profile State ---
  String? _jwtToken;
  String? _userEmail;
  Map<String, dynamic>? _profileData;
  bool get isLoggedIn => _jwtToken != null;
  String? get userEmail => _userEmail;
  Map<String, dynamic>? get profileData => _profileData;

  // --- App State ---
  bool _isLoading = true;
  bool _isFirstLogin = true;
  DateTime? _lastSyncTimestamp;
  bool get isLoading => _isLoading;
  bool get isFirstLogin => _isFirstLogin;
  DateTime? get lastSyncTimestamp => _lastSyncTimestamp;

  /// This flag indicates the session is confirmed expired and auto-relogin failed.
  /// The app should operate in offline mode until the user manually logs in again.
  bool _isSessionExpired = false;
  bool get isSessionExpired => _isSessionExpired;

  // --- Cached Master Data ---
  List<Map<String, dynamic>>? _allUsers;
  List<Map<String, dynamic>>? _tarballStations;
  List<Map<String, dynamic>>? _manualStations;
  List<Map<String, dynamic>>? _tarballClassifications;
  List<Map<String, dynamic>>? _riverManualStations;
  List<Map<String, dynamic>>? _riverTriennialStations;
  List<Map<String, dynamic>>? _departments;
  List<Map<String, dynamic>>? _companies;
  List<Map<String, dynamic>>? _positions;
  List<Map<String, dynamic>>? _airClients;
  List<Map<String, dynamic>>? _airManualStations;
  List<Map<String, dynamic>>? _states;
  List<Map<String, dynamic>>? _appSettings;
  // --- START: MODIFIED PARAMETER LIMITS PROPERTIES ---
  // The old generic list has been removed and replaced with three specific lists.
  List<Map<String, dynamic>>? _npeParameterLimits;
  List<Map<String, dynamic>>? _marineParameterLimits;
  List<Map<String, dynamic>>? _riverParameterLimits;
  // --- END: MODIFIED PARAMETER LIMITS PROPERTIES ---
  List<Map<String, dynamic>>? _apiConfigs;
  List<Map<String, dynamic>>? _ftpConfigs;
  List<Map<String, dynamic>>? _documents;
  List<Map<String, dynamic>>? _pendingRetries;

  // --- Getters for UI access ---
  List<Map<String, dynamic>>? get allUsers => _allUsers;
  List<Map<String, dynamic>>? get tarballStations => _tarballStations;
  List<Map<String, dynamic>>? get manualStations => _manualStations;
  List<Map<String, dynamic>>? get tarballClassifications => _tarballClassifications;
  List<Map<String, dynamic>>? get riverManualStations => _riverManualStations;
  List<Map<String, dynamic>>? get riverTriennialStations => _riverTriennialStations;
  List<Map<String, dynamic>>? get departments => _departments;
  List<Map<String, dynamic>>? get companies => _companies;
  List<Map<String, dynamic>>? get positions => _positions;
  List<Map<String, dynamic>>? get airClients => _airClients;
  List<Map<String, dynamic>>? get airManualStations => _airManualStations;
  List<Map<String, dynamic>>? get states => _states;
  List<Map<String, dynamic>>? get appSettings => _appSettings;
  // --- START: GETTERS FOR NEW PARAMETER LIMITS ---
  List<Map<String, dynamic>>? get npeParameterLimits => _npeParameterLimits;
  List<Map<String, dynamic>>? get marineParameterLimits => _marineParameterLimits;
  List<Map<String, dynamic>>? get riverParameterLimits => _riverParameterLimits;
  // --- END: GETTERS FOR NEW PARAMETER LIMITS ---
  List<Map<String, dynamic>>? get apiConfigs => _apiConfigs;
  List<Map<String, dynamic>>? get ftpConfigs => _ftpConfigs;
  List<Map<String, dynamic>>? get documents => _documents;
  List<Map<String, dynamic>>? get pendingRetries => _pendingRetries;

  // --- SharedPreferences Keys ---
  static const String tokenKey = 'jwt_token';
  static const String userEmailKey = 'user_email';
  static const String profileDataKey = 'user_profile_data';
  static const String lastSyncTimestampKey = 'last_sync_timestamp';
  static const String isFirstLoginKey = 'is_first_login';
  static const String lastOnlineLoginKey = 'last_online_login';

  AuthProvider({
    required ApiService apiService,
    required DatabaseHelper dbHelper,
    required ServerConfigService serverConfigService,
    required RetryService retryService,
  })  : _apiService = apiService,
        _dbHelper = dbHelper,
        _serverConfigService = serverConfigService,
        _retryService = retryService {
    debugPrint('AuthProvider: Initializing...');
    _loadSessionAndSyncData();
  }

  Future<bool> isConnected() async {
    final connectivityResult = await Connectivity().checkConnectivity();
    return !connectivityResult.contains(ConnectivityResult.none);
  }

  Future<void> _loadSessionAndSyncData() async {
    _isLoading = true;
    notifyListeners();

    final prefs = await SharedPreferences.getInstance();
    _jwtToken = prefs.getString(tokenKey);
    _userEmail = prefs.getString(userEmailKey);
    _isFirstLogin = prefs.getBool(isFirstLoginKey) ?? true;

    final activeApiConfig = await _serverConfigService.getActiveApiConfig();
    if (activeApiConfig == null) {
      debugPrint("AuthProvider: No active API config found. Setting default bootstrap URL.");
      final initialConfig = {
        'api_config_id': 0,
        'config_name': 'Default Server',
        'api_url': 'https://mms-apiv4.pstw.com.my/v1',
      };
      await _serverConfigService.setActiveApiConfig(initialConfig);
    }

    final lastSyncString = prefs.getString(lastSyncTimestampKey);
    if (lastSyncString != null) {
      try {
        _lastSyncTimestamp = DateTime.parse(lastSyncString);
      } catch (e) {
        debugPrint("Error parsing last sync timestamp: $e");
        prefs.remove(lastSyncTimestampKey);
      }
    }

    await _loadDataFromCache();

    if (_jwtToken != null) {
      debugPrint('AuthProvider: Session loaded.');
      await _userPreferencesService.applyAndSaveDefaultPreferencesIfNeeded();
      // Sync logic moved to checkAndTransitionToOnlineSession to handle transitions correctly
    } else {
      debugPrint('AuthProvider: No active session. App is in offline mode.');
    }

    _isLoading = false;
    notifyListeners();
  }

  /// Checks if the session is offline and attempts to transition to an online session by performing a silent re-login.
  /// Returns true if a successful transition occurred.
  Future<bool> checkAndTransitionToOnlineSession() async {
    // Condition 1: Check connectivity
    if (!(await isConnected())) {
      debugPrint("AuthProvider: No internet connection. Skipping transition check.");
      return false;
    }

    // Condition 2: Check if currently in an offline session state
    final bool inOfflineSession = _jwtToken != null && _jwtToken!.startsWith("offline-session-");
    if (!inOfflineSession) {
      // Already online or logged out, no transition needed.
      // If online, trigger a normal sync to ensure data freshness on connection restoration.
      if(_jwtToken != null) {
        debugPrint("AuthProvider: Session is already online. Triggering standard sync.");
        // FIX: Add try-catch to prevent unhandled exceptions from crashing the app during background syncs.
        try {
          await syncAllData();
        } catch (e) {
          debugPrint("AuthProvider: Background sync failed silently on transition check: $e");
        }
      }
      return false;
    }

    // FIX: Read password from secure storage instead of temporary variable.
    final String? password = await _secureStorage.read(key: _passwordStorageKey);
    if (password == null || _userEmail == null) {
      debugPrint("AuthProvider: In offline session, but no password in secure storage for auto-relogin. Manual login required.");
      return false;
    }

    debugPrint("AuthProvider: Internet detected in offline session. Attempting silent re-login for $_userEmail...");

    try {
      final result = await _apiService.login(_userEmail!, password);

      if (result['success'] == true) {
        debugPrint("AuthProvider: Silent re-login successful. Transitioning to online session.");
        final String token = result['data']['token'];
        final Map<String, dynamic> profile = result['data']['profile'];

        // Use existing login method to set up session and trigger sync.
        await login(token, profile, password);

        notifyListeners(); // Ensure UI updates after state change
        return true;
      } else {
        // Silent login failed (e.g., password changed on another device).
        // Keep user in offline mode for now. They will need to log out and log back in manually.
        debugPrint("AuthProvider: Silent re-login failed: ${result['message']}");
        return false;
      }
    } catch (e) {
      debugPrint("AuthProvider: Silent re-login exception: $e");
      return false;
    }
  }

  /// ⚡️ Orchestrates session validation and silent re-authentication.
  /// This should be called whenever the app gains an internet connection.
  Future<void> validateAndRefreshSession() async {
    if (!(await isConnected())) {
      debugPrint('AuthProvider: No connection, skipping session validation.');
      return;
    }

    if (_isSessionExpired) {
      debugPrint('AuthProvider: Session is marked as expired, manual login required.');
      return;
    }

    // Don't attempt validation if the user is not logged in or is in a temporary offline session.
    if (_jwtToken == null || _jwtToken!.startsWith("offline-session-")) {
      return;
    }

    try {
      await _apiService.validateToken();
      debugPrint('AuthProvider: Session token is valid.');
    } on SessionExpiredException {
      debugPrint('AuthProvider: Session validation failed (token expired). Attempting silent re-login.');
      final bool reauthenticated = await attemptSilentRelogin();

      if (!reauthenticated) {
        debugPrint('AuthProvider: Silent re-login failed. Switching to session-expired offline mode.');
        _isSessionExpired = true;
        notifyListeners();
        // You can optionally show a one-time notification here.
      } else {
        debugPrint('AuthProvider: Silent re-login successful. Session restored.');
      }
    } catch (e) {
      debugPrint('AuthProvider: An error occurred during session validation: $e');
    }
  }

  /// Attempts to silently re-login to get a new token.
  /// This can be called when a 401 Unauthorized error is detected.
  Future<bool> attemptSilentRelogin() async {
    if (!(await isConnected())) {
      debugPrint("AuthProvider: No internet for silent relogin.");
      return false;
    }

    // FIX: Read password from secure storage.
    final String? password = await _secureStorage.read(key: _passwordStorageKey);
    if (password == null || _userEmail == null) {
      debugPrint("AuthProvider: No cached credentials in secure storage for silent relogin.");
      return false;
    }

    debugPrint("AuthProvider: Session may be expired. Attempting silent re-login for $_userEmail...");
    try {
      final result = await _apiService.login(_userEmail!, password);
      if (result['success'] == true) {
        debugPrint("AuthProvider: Silent re-login successful.");
        final String token = result['data']['token'];
        final Map<String, dynamic> profile = result['data']['profile'];
        await login(token, profile, password);
        _isSessionExpired = false; // Explicitly mark session as valid again.
        notifyListeners();
        return true;
      } else {
        debugPrint("AuthProvider: Silent re-login failed: ${result['message']}");
        return false;
      }
    } catch (e) {
      debugPrint("AuthProvider: Silent re-login exception: $e");
      return false;
    }
  }

  Future<void> syncAllData({bool forceRefresh = false}) async {
    if (!(await isConnected())) {
      debugPrint("AuthProvider: Device is OFFLINE. Skipping sync.");
      return;
    }

    // Proactively check if session is already marked as expired
    if (_isSessionExpired) {
      debugPrint("AuthProvider: Skipping sync, session is expired. Manual login required.");
      throw Exception('Session expired. Please log in again to sync.');
    }

    if (_jwtToken == null || _jwtToken!.startsWith("offline-session-")) {
      debugPrint("AuthProvider: Skipping sync, session is offline or null.");
      return;
    }

    try {
      debugPrint("AuthProvider: Device is ONLINE. Starting delta sync.");
      final prefs = await SharedPreferences.getInstance();
      final String? lastSync = forceRefresh ? null : prefs.getString(lastSyncTimestampKey);

      final result = await _apiService.syncAllData(lastSyncTimestamp: lastSync);

      if (result['success']) {
        debugPrint("AuthProvider: Delta sync successful.");
        final newSyncTimestamp = DateTime.now().toUtc().toIso8601String();
        await prefs.setString(lastSyncTimestampKey, newSyncTimestamp);
        _lastSyncTimestamp = DateTime.parse(newSyncTimestamp);

        if (_isFirstLogin) {
          await setIsFirstLogin(false);
          debugPrint("AuthProvider: First successful sync complete. isFirstLogin flag set to false.");
        }

        await _loadDataFromCache();
        notifyListeners();
      } else {
        debugPrint("AuthProvider: Delta sync failed logically. Message: ${result['message']}");
        // We throw an exception here so the UI can report a failure.
        throw Exception('Data sync failed. Please check the logs.');
      }
    } on SessionExpiredException {
      debugPrint("AuthProvider: Session expired during sync. Attempting silent re-login...");
      final bool reauthenticated = await attemptSilentRelogin();
      if (reauthenticated) {
        debugPrint("AuthProvider: Re-login successful. Retrying sync...");
        await syncAllData(forceRefresh: forceRefresh); // Retry the sync
      } else {
        debugPrint("AuthProvider: Re-login failed after session expired during sync. Switching to offline mode.");
        _isSessionExpired = true;
        notifyListeners();
        throw Exception('Session expired. App is now in offline mode.');
      }
    } catch (e) {
      debugPrint("AuthProvider: A general error occurred during sync: $e");
      // Re-throw the exception so the UI can display it.
      rethrow;
    }
  }

  // --- START: NEW METHOD FOR REGISTRATION SCREEN ---
  Future<void> syncRegistrationData() async {
    if (!(await isConnected())) {
      debugPrint("AuthProvider: Device is OFFLINE. Skipping registration data sync.");
      return;
    }

    debugPrint("AuthProvider: Fetching data for registration screen...");
    final result = await _apiService.syncRegistrationData();

    if (result['success']) {
      await _loadDataFromCache(); // Reload data from DB into the provider
      notifyListeners();       // Notify the UI to rebuild
      debugPrint("AuthProvider: Registration data loaded and UI notified.");
    } else {
      debugPrint("AuthProvider: Registration data sync failed.");
    }
  }
  // --- END: NEW METHOD FOR REGISTRATION SCREEN ---

  Future<void> refreshProfile() async {
    if (!(await isConnected())) {
      debugPrint("AuthProvider: Device is OFFLINE. Skipping profile refresh.");
      return;
    }
    if (_isSessionExpired || _jwtToken == null || _jwtToken!.startsWith("offline-session-")) {
      debugPrint("AuthProvider: Skipping profile refresh, session is offline, expired, or null.");
      return;
    }

    final result = await _apiService.refreshProfile();
    if (result['success']) {
      await setProfileData(result['data']);
    }
  }

  Future<void> proactiveTokenRefresh() async {
    if (!(await isConnected())) {
      debugPrint('AuthProvider: No connection, skipping proactive token refresh.');
      return;
    }

    final prefs = await SharedPreferences.getInstance();
    final lastOnlineLoginString = prefs.getString(lastOnlineLoginKey);

    if (lastOnlineLoginString == null) {
      debugPrint('AuthProvider: No last online login timestamp found, skipping proactive refresh.');
      return; // Never logged in online, nothing to refresh.
    }

    try {
      final lastOnlineLogin = DateTime.parse(lastOnlineLoginString);
      if (DateTime.now().difference(lastOnlineLogin).inHours >= 24) {
        debugPrint('AuthProvider: Session is older than 24 hours. Attempting proactive silent re-login.');
        await attemptSilentRelogin();
      } else {
        debugPrint('AuthProvider: Session is fresh (< 24 hours old). No proactive refresh needed.');
      }
    } catch (e) {
      debugPrint('AuthProvider: Error during proactive token refresh check: $e');
    }
  }

  Future<void> _loadDataFromCache() async {
    final prefs = await SharedPreferences.getInstance();
    final profileJson = prefs.getString(profileDataKey);
    if (profileJson != null) {
      _profileData = jsonDecode(profileJson);
    } else {
      _profileData = await _dbHelper.loadProfile();
    }
    _allUsers = await _dbHelper.loadUsers();
    _tarballStations = await _dbHelper.loadTarballStations();
    _manualStations = await _dbHelper.loadManualStations();
    _tarballClassifications = await _dbHelper.loadTarballClassifications();
    _riverManualStations = await _dbHelper.loadRiverManualStations();
    _riverTriennialStations = await _dbHelper.loadRiverTriennialStations();
    _departments = await _dbHelper.loadDepartments();
    _companies = await _dbHelper.loadCompanies();
    _positions = await _dbHelper.loadPositions();
    _airClients = await _dbHelper.loadAirClients();
    _airManualStations = await _dbHelper.loadAirManualStations();
    _states = await _dbHelper.loadStates();
    _appSettings = await _dbHelper.loadAppSettings();

    // --- START: LOAD DATA FROM NEW PARAMETER LIMIT TABLES ---
    _npeParameterLimits = await _dbHelper.loadNpeParameterLimits();
    _marineParameterLimits = await _dbHelper.loadMarineParameterLimits();
    _riverParameterLimits = await _dbHelper.loadRiverParameterLimits();
    // --- END: LOAD DATA FROM NEW PARAMETER LIMIT TABLES ---

    _documents = await _dbHelper.loadDocuments();
    _apiConfigs = await _dbHelper.loadApiConfigs();
    _ftpConfigs = await _dbHelper.loadFtpConfigs();
    _pendingRetries = await _retryService.getPendingTasks();
    debugPrint("AuthProvider: All master data loaded from local DB cache.");
  }

  Future<void> refreshPendingTasks() async {
    _pendingRetries = await _retryService.getPendingTasks();
    notifyListeners();
  }

  Future<void> login(String token, Map<String, dynamic> profile, String password) async {
    _jwtToken = token;
    _userEmail = profile['email'];

    // FIX: Save password to secure storage instead of in-memory variable.
    await _secureStorage.write(key: _passwordStorageKey, value: password);

    final Map<String, dynamic> profileWithToken = Map.from(profile);
    profileWithToken['token'] = token;
    _profileData = profileWithToken;

    final prefs = await SharedPreferences.getInstance();
    await prefs.setString(tokenKey, token);
    await prefs.setString(userEmailKey, _userEmail!);
    await prefs.setString(profileDataKey, jsonEncode(_profileData));
    await prefs.setString(lastOnlineLoginKey, DateTime.now().toIso8601String());
    await _dbHelper.saveProfile(_profileData!);

    try {
      debugPrint("AuthProvider: Hashing and caching password for offline login.");
      final String hashedPassword = await compute(hashPassword, password);
      await _dbHelper.upsertUserWithCredentials(
        profile: profile,
        passwordHash: hashedPassword,
      );
      debugPrint("AuthProvider: Credentials cached successfully.");
    } catch (e) {
      debugPrint("AuthProvider: Failed to cache password hash: $e");
    }

    debugPrint('AuthProvider: Login successful. Session and profile persisted.');
    await _userPreferencesService.applyAndSaveDefaultPreferencesIfNeeded();
    await syncAllData(forceRefresh: true);
  }

  Future<bool> loginOffline(String email, String password) async {
    debugPrint("AuthProvider: Attempting offline login for user $email.");

    try {
      // 1. Retrieve stored hash from the local database based on email.
      final String? storedHash = await _dbHelper.getUserPasswordHashByEmail(email);

      if (storedHash == null || storedHash.isEmpty) {
        debugPrint("AuthProvider DEBUG: Offline login failed for $email because NO HASH was found in local storage.");
        return false;
      }

      // 2. Verify the provided password against the stored hash.
      debugPrint("AuthProvider: Verifying password against stored hash...");
      final bool passwordMatches = await compute(verifyPassword, CheckPasswordParams(password, storedHash));

      if (passwordMatches) {
        debugPrint("AuthProvider: Offline password verification successful.");
        // 3. Load profile data from local storage.
        final Map<String, dynamic>? cachedProfile = await _dbHelper.loadProfileByEmail(email);
        if (cachedProfile == null) {
          debugPrint("AuthProvider DEBUG: Offline login failed because profile data was missing, even though password matched.");
          return false;
        }

        // 4. Initialize session state from cached profile data.
        _jwtToken = "offline-session-${DateTime.now().millisecondsSinceEpoch}";
        _userEmail = email;

        // FIX: Save password to secure storage for future auto-relogin.
        await _secureStorage.write(key: _passwordStorageKey, value: password);

        final Map<String, dynamic> profileWithToken = Map.from(cachedProfile);
        profileWithToken['token'] = _jwtToken;
        _profileData = profileWithToken;

        final prefs = await SharedPreferences.getInstance();
        await prefs.setString(tokenKey, _jwtToken!);
        await prefs.setString(userEmailKey, _userEmail!);
        await prefs.setString(profileDataKey, jsonEncode(_profileData));

        await _loadDataFromCache();
        notifyListeners();
        return true;
      } else {
        debugPrint("AuthProvider DEBUG: Offline login failed because password did not match stored hash (Hash Mismatch).");
        return false;
      }
    } catch (e) {
      debugPrint("AuthProvider DEBUG: An unexpected error occurred during offline login process: $e");
      return false;
    }
  }

  Future<void> setProfileData(Map<String, dynamic> data) async {
    final String? existingToken = _profileData?['token'];
    _profileData = data;
    if (_profileData != null && existingToken != null) {
      _profileData!['token'] = existingToken;
    }

    final prefs = await SharedPreferences.getInstance();
    await prefs.setString(profileDataKey, jsonEncode(_profileData));
    await _dbHelper.saveProfile(_profileData!); // Also save to local DB
    notifyListeners();
  }

  Future<void> setIsFirstLogin(bool value) async {
    _isFirstLogin = value;
    final prefs = await SharedPreferences.getInstance();
    await prefs.setBool(isFirstLoginKey, value);
    notifyListeners();
  }

  Future<void> logout() async {
    debugPrint('AuthProvider: Initiating logout...');
    _jwtToken = null;
    _userEmail = null;
    _profileData = null;
    _lastSyncTimestamp = null;
    _isFirstLogin = true;
    _isSessionExpired = false; // Reset session expired flag on logout

    // FIX: Clear password from secure storage on logout.
    await _secureStorage.delete(key: _passwordStorageKey);

    _allUsers = null;
    _tarballStations = null;
    _manualStations = null;
    _tarballClassifications = null;
    _riverManualStations = null;
    _riverTriennialStations = null;
    _departments = null;
    _companies = null;
    _positions = null;
    _airClients = null;
    _airManualStations = null;
    _states = null;
    _appSettings = null;

    // --- START: Clear new parameter limit lists ---
    _npeParameterLimits = null;
    _marineParameterLimits = null;
    _riverParameterLimits = null;
    // --- END: Clear new parameter limit lists ---

    _documents = null;
    _apiConfigs = null;
    _ftpConfigs = null;
    _pendingRetries = null;

    final prefs = await SharedPreferences.getInstance();
    await prefs.remove(tokenKey);
    await prefs.remove(userEmailKey);
    await prefs.remove(profileDataKey);
    await prefs.remove(lastSyncTimestampKey);
    await prefs.remove(lastOnlineLoginKey);
    await prefs.remove('default_preferences_saved');
    await prefs.setBool(isFirstLoginKey, true);

    debugPrint('AuthProvider: All session and cached data cleared.');
    notifyListeners();
  }

  Future<Map<String, dynamic>> resetPassword(String email) {
    return _apiService.post('auth/forgot-password', {'email': email});
  }
}

class CheckPasswordParams {
  final String password;
  final String hash;
  CheckPasswordParams(this.password, this.hash);
}

String hashPassword(String password) {
  return BCrypt.hashpw(password, BCrypt.gensalt());
}

bool verifyPassword(CheckPasswordParams params) {
  return BCrypt.checkpw(params.password, params.hash);
}