import 'dart:convert'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:go_router/go_router.dart'; import 'package:http/http.dart' as http; import 'package:shared_preferences/shared_preferences.dart'; import '../../config/environment.dart'; import '../../customAppBar/toastHelper.dart'; import '../../logger.dart'; import '../../router/app_router.dart'; import 'SessionManager.dart'; import 'web_session_storage.dart'; class TokenService { static const postTokenKey = 'post_token'; static const preTokenKey = 'pre_token'; static const _secureStorage = FlutterSecureStorage( iOptions: IOSOptions( accessibility: KeychainAccessibility.first_unlock_this_device, ), ); /// Web: tokens live only in [sessionStorage] (tab-scoped). Mobile: secure storage. static Future saveTokens({String? postToken, String? preToken}) async { if (kIsWeb) { if (postToken != null) { WebSessionStorage.write(postTokenKey, postToken); } if (preToken != null) { WebSessionStorage.write(preTokenKey, preToken); } } else { if (postToken != null) { await _secureStorage.write(key: postTokenKey, value: postToken); } if (preToken != null) { await _secureStorage.write(key: preTokenKey, value: preToken); } } } static Future getPostToken() async { if (kIsWeb) { return WebSessionStorage.read(postTokenKey); } return _secureStorage.read(key: postTokenKey); } static Future getPreToken() async { if (kIsWeb) { return WebSessionStorage.read(preTokenKey); } return _secureStorage.read(key: preTokenKey); } /// Clears auth tokens. On web, clears entire [sessionStorage] (logout / session end). static Future clearTokens() async { if (kIsWeb) { WebSessionStorage.clearAll(); } else { await _secureStorage.delete(key: postTokenKey); await _secureStorage.delete(key: preTokenKey); } } static Future hasValidToken() async { final post = await getPostToken(); final pre = await getPreToken(); return (post != null && post.isNotEmpty) || (pre != null && pre.isNotEmpty); } /// Post token → home; pre-only → enrollment ([empDetails]). static Future defaultRouteForStoredTokens() async { final post = await getPostToken(); if (post != null && post.isNotEmpty) return '/home'; final pre = await getPreToken(); if (pre != null && pre.isNotEmpty) return '/empDetails'; return '/login'; } /// Clears auth tokens that older web builds stored in SharedPreferences (localStorage). /// Call once at startup so they cannot be mistaken for a valid session. static Future clearLegacyWebAuthFromSharedPreferences() async { if (!kIsWeb) return; final p = await SharedPreferences.getInstance(); await p.remove(postTokenKey); await p.remove(preTokenKey); } Future checkLoginPin() async { try { final SharedPreferences prefs = await SharedPreferences.getInstance(); final empMobileNo = prefs.getString('empMobileNo'); final empEmailid = prefs.getString('empEmailid'); var params = {}; if (empMobileNo != null && empMobileNo.isNotEmpty) { params = {'mobile_number': empMobileNo}; } else if (empEmailid != null && empEmailid.isNotEmpty) { params = {'email_id': empEmailid}; } final response = await http.post( Uri.parse(Environment.apiUrlEnrollment + 'checkMpin'), body: json.encode(params), headers: { 'Content-Type': 'application/json', 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', }, ); if (response.statusCode == 200) { Map data = json.decode(response.body); if (data['status'] == 'success') { if (data['data'] != null) { prefs.setString('mpinText', data['data']); } if (data['Mpin'] != null) { prefs.setString('mpin', data['Mpin']); } if (data['is_biometric_enabled'] != null) { prefs.setString( 'is_biometric_enabled', data['is_biometric_enabled']); } if (data['is_mpin_skipped'] != null) { prefs.setString('is_mpin_skipped', data['is_mpin_skipped']); return data['is_mpin_skipped'].toString(); } } } else { logDebug('checkLoginPin API failed: ${response.statusCode}'); return null; } } catch (e) { logDebug('checkLoginPin error: $e'); return null; } return null; } void _goAfterLogout(BuildContext context, String location) { if (context.mounted && GoRouter.maybeOf(context) != null) { context.go(location); } else { navigateAppTo(location); } } Future logout(BuildContext context) async { final SharedPreferences prefs = await SharedPreferences.getInstance(); String? mobileNumber = prefs.getString('empMobileNo'); String? mailID = prefs.getString('empEmailid'); final postToken = await getPostToken(); final hasPostToken = postToken != null && postToken.isNotEmpty; String? isMpinSkipped; if (!kIsWeb && hasPostToken) { isMpinSkipped = await checkLoginPin(); } logDebug('isMpinSkipped $isMpinSkipped hasPostToken $hasPostToken'); await SessionManager().clear(); if (kIsWeb) { await prefs.clear(); _goAfterLogout(context, '/login'); } else { if (mobileNumber != null) { await prefs.setString('empMobileNo', mobileNumber); } if (mailID != null) { await prefs.setString('empEmailid', mailID); } if (isMpinSkipped != null) { await prefs.setString('is_mpin_skipped', isMpinSkipped); } if (hasPostToken && isMpinSkipped != null && isMpinSkipped == '0') { _goAfterLogout(context, '/pinPage'); } else { _goAfterLogout(context, '/login'); } } } }