import 'dart:convert'; import 'dart:developer'; import 'package:wanisdriver/Provider/utils_singleton.dart'; import 'package:wanisdriver/config/constants/app_constant.dart'; import 'package:wanisdriver/services/push_notification_service.dart'; import 'package:http/http.dart' as http; import 'package:shared_preferences/shared_preferences.dart'; import '../config/api_response.dart'; import 'package:wanisdriver/models/Ride.dart'; import '../models/RideUpdateRequest.dart'; class RideService { int statusCode = -1; bool errorToken = false; SharedPreferences? prefs; static final Map>> _rideCache = {}; static final Map>> _rideStatusCache = {}; Future> get_my_latest_ride_by_status(String status) async { print('→ about to fetch ride #$status from: https://$url/api/ride/get_my_latest_ride_by_status?status=$status'); try { var uri = Uri.https( url, '/api/ride/get_my_latest_ride_by_status', {'status': status}, ); final response = await http.get( uri, headers: { 'Authorization': 'Bearer ${utils.token}', 'Content-Type': 'application/json', 'Accept': 'application/json', }, ); print('status code : ${response.statusCode}'); print('status body : ${response.body}'); if (response.statusCode == 200 || response.statusCode == 201) { // ✅ CORE FIX: Check if the response body is NOT empty before trying to parse it. if (response.body.isNotEmpty) { final data = json.decode(response.body); Ride ride = Ride.fromJson(data); log("ride : ${ride.toString()}"); log("data : ${data.toString()}"); return APIResponse(statusCode: response.statusCode, data: ride); } else { // The request was successful, but the server returned no data (no ongoing ride). // Return a successful response with null data. return APIResponse(statusCode: response.statusCode, data: null); } } else { log('Failed to load ride data. Status code: ${response.statusCode}'); return APIResponse( statusCode: response.statusCode, errorMessage: 'Failed to load ride data. Status code: ${response.statusCode}'); } } on FormatException catch (e) { // Catch the specific error for malformed JSON. log('Error occurred (FormatException): Server response was not valid JSON. $e'); return APIResponse(errorMessage: 'The server returned an invalid response.'); } catch (e) { // Catch all other errors (network issues, etc.) log('Error occurred 01: $e'); return APIResponse(errorMessage: e.toString()); } } Future> get_my_latest_ride_not_rated() async { final endpoint = '/api/ride/get_my_latest_not_rated_ride'; print('→ about to fetch ride # from: https://$url$endpoint'); try { var uri = Uri.https(url, endpoint); final response = await http.get( uri, headers: { 'Authorization': 'Bearer ${utils.token}', 'Accept': 'application/json', }, ); print('status code: ${response.statusCode}'); print('response body: ${response.body}'); if (response.statusCode == 200 || response.statusCode == 201) { if (response.body.trim().isEmpty) { log("🚫 No ride found — empty body from API"); return APIResponse(statusCode: response.statusCode); } try { final data = json.decode(response.body); Ride ride = Ride.fromJson(data); log("✅ ride: ${ride.toString()}"); return APIResponse(statusCode: response.statusCode, data: ride); } catch (e) { log('❌ JSON parsing error: $e'); return APIResponse( statusCode: response.statusCode, errorMessage: 'Failed to parse ride data: $e', ); } } else { log('❌ Failed to load ride data. Status code: ${response.statusCode}'); return APIResponse( statusCode: response.statusCode, errorMessage: 'Failed to load ride data. Status code: ${response.statusCode}', ); } } catch (e) { log('❌ Network error: $e'); return APIResponse(errorMessage: 'Network error: $e'); } } Future>> fetchPendingRides({ int page = 0, int size = 5, }) async { errorToken = false; statusCode = -1; final queryParams = { 'page': page.toString(), 'size': size.toString(), }; final response = await http.get( Uri.https(url, '/api/ride/get_last_pending_rides_pg', queryParams), headers: { 'Authorization': 'Bearer ${utils.token}', 'Content-Type': 'application/json', 'Accept': 'application/json', }, ); final apiResponse = APIResponse>() ..statusCode = response.statusCode ..dataJson = response.body; if (response.statusCode == 200) { final data = json.decode(response.body); final rides = (data['result'] as List) .map((item) => Ride.fromJson(item)) .toList(); apiResponse ..data = rides ..totalPages = data['totalPages']; } else if (response.statusCode == 401) { errorToken = true; apiResponse.errorMessage = 'Unauthorized: Invalid or expired token'; } else { apiResponse.errorMessage = 'Failed to load pending rides: ${response.reasonPhrase}'; } return apiResponse; } Future rate_user(int rating,int userid) async { try { // Ensure token is fetched before making the request var uri = Uri.https(url, '/api/v1/rating/rate_user/$userid', {'rate': rating.toString()},); // Make the GET request final response = await http.post( uri, headers: { 'Authorization': 'Bearer ${utils.token}', 'Accept': 'application/json', }, ); print('status code : ${response.statusCode}'); print('status body : ${response.body}'); if (response.statusCode == 200|| response.statusCode == 201) { return APIResponse(statusCode: response.statusCode, data:true ); } else { log('Failed to load user data. Status code: ${response.statusCode}'); return APIResponse(statusCode: response.statusCode,errorMessage: 'Failed to load user data. Status code: ${response.statusCode}'); } } catch (e) { log('Error occurred 03: $e'); return APIResponse(errorMessage: e.toString()); } } Future>> fetchRides( int page, int size, { String? date, String? status, String? type, }) async { errorToken = false; statusCode = -1; final queryParams = { 'page': page.toString(), 'size': size.toString(), if (date != null) 'date': date, if (status != null) 'status': status, if (type != null) 'type': type, }; final response = await http.get( Uri.https(url, '/api/ride/all_my_rides_driver', queryParams), headers: { 'Authorization': 'Bearer ${utils.token}', 'Content-Type': 'application/json', 'Accept': 'application/json', }, ); final apiResponse = APIResponse>() ..statusCode = response.statusCode ..dataJson = response.body; if (response.statusCode == 200) { final data = json.decode(response.body); final rides = (data['result'] as List) .map((item) => Ride.fromJson(item)) .toList(); apiResponse ..data = rides ..totalPages = data['totalPages']; } else if (response.statusCode == 401) { errorToken = true; apiResponse.errorMessage = 'Unauthorized: Invalid or expired token'; } else { apiResponse.errorMessage = 'Failed to load rides: ${response.reasonPhrase}'; } return apiResponse; } Future> confirmPayment({ required int rideId, required String paymentType, }) async { try { final queryParams = { 'type':paymentType.toString() }; final response = await http.patch( Uri.https(url, '/api/ride/confirm_payment/$rideId', queryParams), headers: { 'Authorization': 'Bearer ${utils.token}', 'Content-Type': 'application/json', 'Accept': 'application/json', }, ); if (response.statusCode == 200) { final jsonData = jsonDecode(response.body); return APIResponse(data: true, statusCode: 200); } else { final jsonData = jsonDecode(response.body); return APIResponse( data: false, errorMessage: jsonData['message'] ?? 'Payment confirmation failed.', statusCode: response.statusCode, ); } } catch (e) { log(e.toString()); return APIResponse( data: false, errorMessage: 'An error occurred: $e', statusCode: null, ); } } Future _getOrFetchFCMToken(SharedPreferences prefs) async { await PushNotificationService().getFCMToken(); String fcmToken = prefs.getString("FCM token") ?? "InvalidToken"; return fcmToken; } Future> acceptRide(int rideId) async { final prefs = await SharedPreferences.getInstance(); errorToken = false; statusCode = -1; String fcmToken = prefs.getString("FCM token") ?? await _getOrFetchFCMToken(prefs); final Map query = { "fcmToken": fcmToken, // Format date as string }; log("acceptRide ---> $rideId"); final response = await http.post( Uri.https(url, '/api/ride/$rideId/accept', query), headers: { 'Authorization': 'Bearer ${utils.token}', 'Content-Type': 'application/json', 'Accept': 'application/json', }, ); log("acceptRide ---> ${response.body}"); log("acceptRide ---> ${response.statusCode}"); final apiResponse = APIResponse() ..statusCode = response.statusCode ..dataJson = response.body; log("acceptRide ---> $apiResponse"); if (response.statusCode == 200 || response.statusCode == 201) { log("acceptRide ---> response.statusCode == 200 || response.statusCode == 201"); apiResponse.data = response.body; } else if (response.statusCode == 401) { errorToken = true; apiResponse.errorMessage = 'Unauthorized: Invalid or expired token'; } else { apiResponse.errorMessage = 'Failed to accept ride: ${response.reasonPhrase}'; } return apiResponse; } Future> declineRide(int rideId) async { errorToken = false; statusCode = -1; final response = await http.post( Uri.https(url, '/api/ride/$rideId/decline'), headers: { 'Authorization': 'Bearer ${utils.token}', 'Content-Type': 'application/json', 'Accept': 'application/json', }, ); final apiResponse = APIResponse() ..statusCode = response.statusCode ..dataJson = response.body; if (response.statusCode == 200 || response.statusCode == 201) { apiResponse.data = response.body; } else if (response.statusCode == 401) { errorToken = true; apiResponse.errorMessage = 'Unauthorized: Invalid or expired token'; } else { apiResponse.errorMessage = 'Failed to accept ride: ${response.reasonPhrase}'; } return apiResponse; } Future>> fetchridebystatus(int page, int size, bool isCorporate, {String? date,required List status,required String type}) async { errorToken = false; statusCode = -1; Map queryParams; if(date==null) { queryParams = {'page': page.toString(), 'size': size.toString(),'isCorporate': isCorporate.toString(),'status':status,'type':type}; }else{ queryParams = {'page': page.toString(), 'size': size.toString(),'isCorporate': isCorporate.toString(),'date':date,'status':status,'type':type}; } final response = await http.get( Uri.https(url, '/api/ride/all_my_rides_by_status_for_driver',queryParams), headers: { 'Authorization': 'Bearer ${utils.token}', 'Content-type': 'application/json', 'Accept': 'application/json' }, ); APIResponse> apiResponse = APIResponse>(); apiResponse.statusCode = response.statusCode; apiResponse.dataJson = response.body; if (response.statusCode == 200) { final data = json.decode(response.body); final rides = (data['result'] as List) .map((item) => Ride.fromJson(item)) .toList(); apiResponse.data = rides; apiResponse.totalPages = data['totalPages']; } else if (response.statusCode == 401) { errorToken = true; apiResponse.errorMessage = 'Unauthorized: Invalid or expired token'; } else { apiResponse.errorMessage = 'Failed to load rides: ${response.reasonPhrase}'; } return apiResponse; } Future> fetchbyid(int rideId) async { // Check if there's already an ongoing request for this ride ID if (_rideCache.containsKey(rideId)) { print('→ waiting for existing request for ride #$rideId'); return await _rideCache[rideId]!; } print('→ about to fetch ride #$rideId from: https://$url/api/ride/ride/$rideId'); // Create the future and cache it immediately final future = _performFetchById(rideId); _rideCache[rideId] = future; // Wait for completion and clean up cache final result = await future; _rideCache.remove(rideId); return result; } Future> _performFetchById(int rideId) async { try { // Ensure token is fetched before making the request var uri = Uri.https(url, '/api/ride/ride/$rideId'); // Make the GET request final response = await http.get( uri, headers: { 'Authorization': 'Bearer ${utils.token}', 'Content-Type': 'application/json', 'Accept': 'application/json', }, ); print('status code : ${response.statusCode}'); print('status body : ${response.body}'); if (response.statusCode == 200 || response.statusCode == 201) { // Decode the JSON response final data = json.decode(response.body); // Process the user data return APIResponse(statusCode: response.statusCode, data: Ride.fromJson(data)); } else { log('Failed to load user data. Status code: ${response.statusCode}'); return APIResponse(statusCode: response.statusCode, errorMessage: 'Failed to load user data. Status code: ${response.statusCode}'); } } catch (e) { log('Error occurred 04: $e'); return APIResponse(errorMessage: e.toString()); } } // Optional: Methods to clear cache if needed static void clearRideCache() { _rideCache.clear(); _rideStatusCache.clear(); } // Optional: Method to clear specific ride from cache static void clearRideFromCache(int rideId) { _rideCache.remove(rideId); } // Optional: Method to clear specific status from cache static void clearStatusFromCache(String status) { _rideStatusCache.remove(status); } Future> accept_ride_driver(int rideId) async { print('→ about to fetch ride #$rideId from: https://$url/api/ride/accept_ride_driver/$rideId'); try { // Ensure token is fetched before making the request var uri = Uri.https(url, '/api/ride/accept_ride_driver/$rideId'); // Make the GET request final response = await http.put( uri, headers: { 'Authorization': 'Bearer ${utils.token}', 'Content-Type': 'application/json', 'Accept': 'application/json', }, ); print('status code : ${response.statusCode}'); print('status body : ${response.body}'); if (response.statusCode == 200|| response.statusCode == 201) { return APIResponse(statusCode: response.statusCode, data: true); } else { log('Failed to load user data. Status code: ${response.statusCode}'); return APIResponse(statusCode: response.statusCode,errorMessage: 'Failed to load user data. Status code: ${response.statusCode}',data: false); } } catch (e) { log('Error occurred 04: $e'); return APIResponse(errorMessage: e.toString(),data: false); } } Future> accept_ride_driver_manual(int rideId) async { print('→ about to fetch ride #$rideId from: https://$url/api/ride/accept_ride_driver_manual/$rideId'); try { // Ensure token is fetched before making the request var uri = Uri.https(url, '/api/ride/accept_ride_driver_manual/$rideId'); // Make the GET request final response = await http.put( uri, headers: { 'Authorization': 'Bearer ${utils.token}', 'Content-Type': 'application/json', 'Accept': 'application/json', }, ); print('status code : ${response.statusCode}'); print('status body : ${response.body}'); if (response.statusCode == 200|| response.statusCode == 201) { return APIResponse(statusCode: response.statusCode, data: true); } else { log('Failed to load user data. Status code: ${response.statusCode}'); return APIResponse(statusCode: response.statusCode,errorMessage: 'Failed to load user data. Status code: ${response.statusCode}',data: false); } } catch (e) { log('Error occurred 04: $e'); return APIResponse(errorMessage: e.toString(),data: false); } } Future> send_update_ride(RideUpdateRequest request) async { print('→ about to fetch ride #${request.rideId} from: https://$url/api/ride/update_ride/'); try { // Ensure token is fetched before making the request var uri = Uri.https(url, '/api/ride/update_ride'); // Make the GET request final response = await http.post( uri, headers: { 'Authorization': 'Bearer ${utils.token}', 'Content-Type': 'application/json', 'Accept': 'application/json', }, body: jsonEncode(request.toJson()), ); print('status code : ${response.statusCode}'); print('status body : ${response.body}'); if (response.statusCode == 200|| response.statusCode == 201) { return APIResponse(statusCode: response.statusCode, data: true); } else { log('Failed to load user data. Status code: ${response.statusCode}'); return APIResponse(statusCode: response.statusCode,errorMessage: 'Failed to load user data. Status code: ${response.statusCode}',data: false); } } catch (e) { log('Error occurred 05: $e'); return APIResponse(errorMessage: e.toString(),data: false); } } Future> cancel_ride_driver(int rideId) async { print('→ about to fetch ride #$rideId from: https://$url/api/ride/cancel_ride_driver/$rideId'); try { // Ensure token is fetched before making the request var uri = Uri.https(url, '/api/ride/cancel_ride_driver/$rideId'); // Make the GET request final response = await http.put( uri, headers: { 'Authorization': 'Bearer ${utils.token}', 'Content-Type': 'application/json', 'Accept': 'application/json', }, ); print('status code : ${response.statusCode}'); print('status body : ${response.body}'); if (response.statusCode == 200|| response.statusCode == 201) { return APIResponse(statusCode: response.statusCode, data: true); } else { log('Failed to load user data. Status code: ${response.statusCode}'); return APIResponse(statusCode: response.statusCode,errorMessage: 'Failed to load user data. Status code: ${response.statusCode}',data: false); } } catch (e) { log('Error occurred 06: $e'); return APIResponse(errorMessage: e.toString(),data: false); } } Future> updateraduice(double raduice) async { final prefs = await SharedPreferences.getInstance(); print('→ about to fetch ride #$raduice from: https://$url/api/v1/driver/update_reduice'); try { final params = { 'reduice': '$raduice', }; // Ensure token is fetched before making the request var uri = Uri.https(url, '/api/v1/driver/update_reduice',params); // Make the GET request final response = await http.patch( uri, headers: { 'Authorization': 'Bearer ${utils.token}', 'Content-Type': 'application/json', 'Accept': 'application/json', }, ); print('status code : ${response.statusCode}'); print('status body : ${response.body}'); if (response.statusCode == 200|| response.statusCode == 201) { await prefs.setDouble("driverraduice",raduice); return APIResponse(statusCode: response.statusCode, data: true); } else { log('Failed to load user data. Status code: ${response.statusCode}'); return APIResponse(statusCode: response.statusCode,errorMessage: 'Failed to load user data. Status code: ${response.statusCode}',data: false); } } catch (e) { log('Error occurred 07: $e'); return APIResponse(errorMessage: e.toString(),data: false); } } }