// ignore_for_file: non_constant_identifier_names import 'dart:convert'; import 'dart:developer'; import 'dart:io'; import 'package:wanisdriver/services/push_notification_service.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:http/http.dart' as http; import 'package:http_parser/http_parser.dart'; import '../Provider/utils_singleton.dart'; import '../config/api_response.dart'; import '../config/constants/app_constant.dart'; import '../config/constants/consts.dart'; import '../models/login_request_model.dart'; import '../models/users.dart'; import '../response/login_response.dart'; class AuthServices { String token = ''; int statusCode = -1; bool errorToken = false; Future getToken() async { final prefs = await SharedPreferences.getInstance(); if (prefs.getString('token') == null || prefs.getString('token') == '') { prefs.setString('token', 'InvalidToken'); } if (prefs.getString('token') != null) { token = prefs.getString('token')!; } return token.toString(); } Future> signIn( String email, String password, String? deviceId, String? deviceType, String? ip) async { // Initialize SharedPreferences instance only once. final prefs = await SharedPreferences.getInstance(); errorToken = false; statusCode = -1; // Retrieve or fetch FCM token efficiently String fcmToken = prefs.getString("FCM token") ?? await _getOrFetchFCMToken(prefs); log("FCM Token: $fcmToken"); // Construct the login request model final userLogin = LoginRequestModel( email: email, password: password, deviceId: deviceId, deviceType: deviceType, ip: ip, tokendevice: fcmToken, ); if (url == null || url.isEmpty) { return APIResponse( statusCode: 500, errorMessage: "URL for sign-in cannot be null or empty"); } // Attempt to parse URI with error handling late Uri uri; try { uri = Uri.https(url, '/api/v1/auth/signin'); } catch (e) { log("Error parsing URL: $e"); return APIResponse( statusCode: 500, errorMessage: "Invalid URL for sign-in"); } try { // Define headers as a constant to improve performance const headers = { 'Content-type': 'application/json', 'Accept': 'application/json', }; // Send HTTP request final response = await http.post( uri, headers: headers, body: jsonEncode(userLogin.toJson()), ); statusCode = response.statusCode; if (statusCode == 200 || statusCode == 201) { final jsonData = json.decode(utf8.decode(response.bodyBytes)); LoginResponse loginResponse = LoginResponse.fromJson(jsonData); // Ensure roles are authorized if (loginResponse.roles == "ROLE_SUPPLIER" || loginResponse.roles == "ROLE_ADMIN") { return APIResponse( statusCode: 401, errorMessage: "Unauthorized Access"); } // Batch update SharedPreferences for better performance await Future.wait([ prefs.setString('token', loginResponse.token!), prefs.setString('refreshToken', loginResponse.refreshtoken!), prefs.setString('Email', email), prefs.setString('Password', password), prefs.setString('Role', loginResponse.roles!), prefs.setBool("isLoggedIn", true), ]); return APIResponse(statusCode: statusCode, data: loginResponse); } // Log error details and return failure response await _logErrorDetails(response); return APIResponse( statusCode: statusCode, errorMessage: response.body); } catch (err, stacktrace) { log("-------> Sign In Error : $err"); await logError( errorCode: "500", errorMessage: "Caught error : $err", path: '/api/v1/auth/signin'); return APIResponse( statusCode: 500, errorMessage: 'No internet connection'); } } // Function to retrieve or fetch the FCM token Future _getOrFetchFCMToken(SharedPreferences prefs) async { String? fcmToken = prefs.getString("FCM token"); if (fcmToken == null || fcmToken.isEmpty) { await PushNotificationService().getFCMToken(); fcmToken = prefs.getString("FCM token") ?? "InvalidToken"; } return fcmToken; } // Function to log error details Future _logErrorDetails(http.Response response) async { await logError( errorCode: response.statusCode.toString(), errorMessage: response.body, path: '/api/v1/auth/signin', ); } Future logError( {String? comment, required String errorCode, required String errorMessage, required String path}) async { errorToken = false; statusCode = -1; await getToken(); final Map query = { "clientid": "283386", "comment": comment ?? "No comment provided", "errorcode": errorCode, "errormessage": errorMessage, "path": path, "source": "MOBILE_APP", "useremail": utils.user?.email ?? "email@email.com", // [email@email.com] this email is for user not logged in "username": utils.user?.name ?? "anonymous user", "userphonenumber": utils.user?.phoneNumber ?? "00000000", }; return await http.post( Uri.https(AppString.reportLink, 'api/v1/servererror/new_error', query), headers: { 'Content-type': 'application/json', 'Accept': 'application/json' }, ).then((data) async { statusCode = data.statusCode; if (data.statusCode == 200 || data.statusCode == 201) { return APIResponse(statusCode: statusCode); } return APIResponse( statusCode: statusCode, errorMessage: 'Please check your info'); }).catchError((_) { return APIResponse( statusCode: statusCode, errorMessage: 'no internet connection'); }); } Future> getCurrentUser() async { errorToken = false; statusCode = -1; await getToken(); return await http.get( Uri.https(url, '/api/v1/user/current_user'), headers: { 'Authorization': 'Bearer $token', 'Content-type': 'application/json', 'Accept': 'application/json' }, ).then((data) async { statusCode = data.statusCode; if (data.statusCode == 200 || data.statusCode == 201) { // final codeUnits = data.body.codeUnits; // var str = const Utf8Decoder().convert(codeUnits); final jsonData = json.decode(data.body); log("Token: $token"); users profile = users.fromJson(jsonData); log(profile.toJson().toString()); return APIResponse(statusCode: statusCode, data: profile); } Future.microtask(() async => await logError( errorCode: statusCode.toString(), errorMessage: data.body, path: '/api/v1/user/current_user')); return APIResponse( statusCode: statusCode, errorMessage: 'Please check your info'); }).catchError((err, stacktrace) { log("-------> fetch profile Error :"); log(err.toString()); Future.microtask(() async => await logError( errorCode: statusCode.toString(), errorMessage: "Caught error : $err", path: '/api/v1/user/current_user')); log("stacktrace : $stacktrace"); return APIResponse( statusCode: statusCode, errorMessage: 'no internet connection', ); }); } Future updateProfilePicture(File image, int userId) async { // Replace with your base URL // Replace with your token retrieval logic // Prepare multipart request log('2'); await getToken(); final request = http.MultipartRequest( 'PATCH', Uri.https(url, '/api/v1/user/update_user_profile_image'), ); // Set headers final Map headers = { 'Authorization': 'Bearer $token', 'Accept': 'application/json', 'Content-type': 'multipart/form-data', }; request.headers.addAll(headers); // Prepare multipart file final multipartFile = http.MultipartFile( 'file', image.readAsBytes().asStream(), image.lengthSync(), filename: image.uri.pathSegments.last, contentType: MediaType( 'image', image.uri.pathSegments.last.split('.').last, ), ); request.files.add(multipartFile); // Send request final response = await request.send(); final http.Response httpResponse = await http.Response.fromStream(response); // Handle response if (httpResponse.statusCode == 200 || httpResponse.statusCode == 201) { // Success log("update profile image${httpResponse.body}"); return APIResponse( statusCode: httpResponse.statusCode, data: jsonDecode(httpResponse.body)); } else { // Log and return error log(httpResponse.statusCode.toString()); return APIResponse( statusCode: httpResponse.statusCode, errorMessage: 'Please check your info', ); } } Future updateProfile( {String? name, String? phonenumber, String? email,String? password}) async { await getToken(); // Create request headers final Map headers = { 'Authorization': 'Bearer $token', 'Accept': 'application/json', 'Content-Type': 'application/json', }; // Create request body final Map body = { 'name': name, 'phonenumber': phonenumber, 'email': email, 'password':password, }; print("body++++++++++++++++++++++++ $body"); // Create URI final uri = Uri.https(url, '/api/v1/user/update_current_user_profile_data'); try { // Send PATCH request final response = await http.patch( uri, headers: headers, body: jsonEncode(body), ); // Handle response if (response.statusCode == 200 || response.statusCode == 201) { log("Update profile response: ${response.body}"); return APIResponse( statusCode: response.statusCode, data: jsonDecode(response.body), ); } else { log("Error: ${response.statusCode}"); return APIResponse( statusCode: response.statusCode, errorMessage: 'Please check your info', ); } } catch (e) { log("Exception: $e"); return APIResponse( statusCode: 500, errorMessage: 'An unexpected error occurred', ); } } Future> refreshToken() async { final prefs = await SharedPreferences.getInstance(); String? refreshToken = prefs.getString('refreshToken'); String? token = prefs.getString('token'); if (refreshToken == null) { return APIResponse( errorMessage: 'No refresh token available', ); } final uri = Uri.https( url, '/api/v1/auth/refreshtoken', ); final response = await http.post( uri, headers: { 'Content-type': 'application/json', 'Accept': 'application/json', }, body: json.encode({'refreshToken': refreshToken,'token': token}), ); APIResponse apiResponse = APIResponse(); apiResponse.statusCode = response.statusCode; apiResponse.dataJson = response.body; if (response.statusCode == 200) { final data = json.decode(response.body); String newToken = data['token']; String newRefreshToken = data['refreshToken']; // Save the new tokens prefs.setString('token', newToken); prefs.setString('refreshToken', newRefreshToken); apiResponse.data = newToken; } else { apiResponse.errorMessage = 'Failed to refresh token'; } return apiResponse; } Future fetchAgoraToken(String channelName, int uid) async { // IMPORTANT: Replace with your server's actual URL const String serverUrl = "https://$url/api/agora/token"; final urlres = Uri.parse('$serverUrl?channelName=$channelName&uid=$uid'); // 1. Get your user's authentication token // This is the token that proves the user is logged into YOUR app. final String? userAuthToken = utils.token??"InvalidToken"; // 2. Create the headers map final Map headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer $userAuthToken', // Add the token here }; try { // 3. Pass the headers to the http.get call final response = await http.get(urlres, headers: headers); if (response.statusCode == 200) { if (response.body.isNotEmpty) { final body = jsonDecode(response.body); // if backend included a token field, we're already signed in if (body is Map && body.containsKey('token')) { final token = body['token'] as String; return APIResponse(statusCode: response.statusCode, data: token); } } return APIResponse( statusCode: response.statusCode, errorMessage: 'Please check your info', ); } else { print("⛔️ Failed to fetch token. Status code: ${response.statusCode}"); print("⛔️ Response body: ${response.body}"); return APIResponse( statusCode: response.statusCode, errorMessage: 'Please check your info', ); } } catch (e) { print("⛔️ Error fetching token: $e"); return APIResponse( statusCode: 500, errorMessage: 'Please check your info', ); } } }