import 'dart:convert'; import 'package:intl/intl.dart'; import 'package:muaawana/models/Promo_code_model.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'dart:developer'; import 'dart:io'; import 'package:http/http.dart' as http; import 'package:http_parser/http_parser.dart'; import 'package:timezone/data/latest.dart' as tz; import 'package:timezone/timezone.dart' as tz; import '../models/user_request/Service_request_response.dart'; import '../models/utils/api_response.dart'; import '../utils/utils_singleton.dart'; const url = 'server.muaawana.com'; class ServiceRequestService { String token = "InvalidToken"; 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> getCodePromo(String code) async { errorToken = false; statusCode = -1; await getToken(); return await http.get( Uri.https(url, '/api/v1/admin_promo_code_controller/get_by_code', {'code': code}), 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 jsonData = json.decode(data.body); final result = PromoCodeModel.fromJson(jsonData); log("hereeeee service"); return APIResponse( statusCode: statusCode, data: result); } return APIResponse( statusCode: statusCode, errorMessage: 'Please check your info'); }).catchError((_, stack) { log(stack.toString()); return APIResponse( statusCode: statusCode, errorMessage: 'no internet connection'); }); } Future> sendServiceRequest( List files, int addressId, String dateTime, String description, String priority, int? productId, double serviceDuration, int serviceId, bool withProduct, String address, double lat, double long, int? promoCodeId, String paymentMethod, int quantity, String customerPhoneNumber) async { errorToken = false; statusCode = -1; await getToken(); final request = http.MultipartRequest( 'POST', Uri.https(url, '/api/v1/user_request/add')); Map headers = { 'Authorization': 'Bearer $token', 'Accept': 'application/json', "Content-type": "multipart/form-data", }; request.headers.addAll(headers); for (var file in files) { // Create a new multipart file from the file object var multipartFile = await http.MultipartFile( 'file', file.readAsBytes().asStream(), file.lengthSync(), filename: file.path.split("/").last, contentType: MediaType("image", file.path.split(".").last), ); // Add the multipart file to the request request.files.add(multipartFile); } Map query = { // "addressid": addressId.toString(), "formattedAddress": address, "lat": long.toString(), "lng": lat.toString(), "description": description, "date": dateTime.toString(), "prioritys": priority, "productid": productId != null ? productId.toString() : '', "serviceduration": serviceDuration.toString(), "serviceid": serviceId.toString(), "withproduct": withProduct.toString(), 'paymentMethod': paymentMethod, 'promoCodeId': promoCodeId != null ? promoCodeId.toString() : '', 'quantity': quantity.toString(), 'customerphonenumber': customerPhoneNumber }; //print(query); request.fields.addAll(query); http.Response response = await http.Response.fromStream(await request.send()); if (response.statusCode == 200 || response.statusCode == 201) { final jsonData = json.decode(response.body); final result = ServiceRequestResponse.fromJson(jsonData); return APIResponse(statusCode: response.statusCode, data: result); } else { //print(response.statusCode); //print(response.body); //print("please Check"); return APIResponse( statusCode: response.statusCode, errorMessage: 'Please check your info', ); } } Future requestProduct( String dateTime, String priority, int productId, double duration, bool withInstallation, String address, double lat, double lng, String paymentMethod, ) async { errorToken = false; statusCode = -1; await getToken(); var request = http.MultipartRequest( "POST", Uri.https(url, '/api/v1/user_product_request/add')); Map headers = { 'Authorization': 'Bearer $token', 'Accept': 'application/json', "Content-type": "multipart/form-data", }; request.headers.addAll(headers); request.fields['date'] = dateTime; request.fields['prioritys'] = priority; request.fields['productid'] = productId.toString(); request.fields['serviceduration'] = duration.toString(); request.fields['withinstallation'] = withInstallation.toString(); request.fields['formattedAddress'] = address; request.fields['lat'] = lng.toString(); request.fields['lng'] = lat.toString(); // request.fields['paymentMethod'] = "CASH"; // Tbarek allah ye hamdi, allah aalik request.fields['paymentMethod'] = paymentMethod; http.Response response = await http.Response.fromStream(await request.send()); statusCode = response.statusCode; if (response.statusCode == 200 || response.statusCode == 201) { return APIResponse(statusCode: statusCode); } else { return APIResponse(statusCode: statusCode, errorMessage: response.body); } } Future> updateServiceRequest( int requestId, List files, int addressId, String dateTime, String description, String priority, int? productId, double serviceDuration, int serviceId, bool withProduct, String address, double lat, double long, int? promoCodeId, String paymentMethod, int quantity, String phoneNumber) async { errorToken = false; statusCode = -1; await getToken(); final request = http.MultipartRequest( 'PATCH', Uri.https(url, '/api/v1/user_request/update_request/$requestId'), ); Map headers = { 'Authorization': 'Bearer $token', 'Accept': 'application/json', "Content-type": "multipart/form-data", }; request.headers.addAll(headers); for (var file in files) { // Create a new multipart file from the file object var multipartFile = await http.MultipartFile( 'file', file.readAsBytes().asStream(), file.lengthSync(), filename: file.path.split("/").last, contentType: MediaType("image", file.path.split(".").last), ); // Add the multipart file to the request request.files.add(multipartFile); } Map query = { // "addressid": addressId.toString(), "formattedAddress": address, "lat": long.toString(), "lng": lat.toString(), "description": description, "date": dateTime.toString(), "prioritys": priority, "productid": productId != null ? productId.toString() : '', "serviceduration": serviceDuration.toString(), "serviceid": serviceId.toString(), "withproduct": withProduct.toString(), 'paymentMethod': paymentMethod, 'promoCodeId': promoCodeId != null ? promoCodeId.toString() : '', 'quantity': quantity.toString(), 'customerphonenumber': phoneNumber }; //print(query); request.fields.addAll(query); http.Response response = await http.Response.fromStream(await request.send()); if (response.statusCode == 200 || response.statusCode == 201) { //print("upload file success"); log(response.body); final jsonData = json.decode(response.body); final result = ServiceRequestResponse.fromJson(jsonData); return APIResponse(statusCode: response.statusCode, data: result); } else { //print(response.statusCode); //print(response.body); //print("please Check"); return APIResponse( statusCode: response.statusCode, errorMessage: 'Please check your info', ); } } /// Returns `true`/`false` as answered by the server, or `null` when /// availability could not be determined (bad date, HTTP error, no network). /// Callers must not treat `null` as "reserved". Future checkTimeAvailability(String tiMe, String daTe) async { errorToken = false; statusCode = -1; await getToken(); try { tz.initializeTimeZones(); DateFormat inputFormat = DateFormat('dd-MM-yyyy h:mm aa'); DateTime dateTimeObj = inputFormat.parse('$daTe $tiMe'); Duration timeZoneOffset = dateTimeObj.timeZoneOffset; String timeZoneOffsetString = dateTimeObj.timeZoneName; String offsetSign = timeZoneOffset.isNegative ? '-' : '+'; String hours = timeZoneOffset.inHours.abs().toString().padLeft(2, '0'); String minutes = (timeZoneOffset.inMinutes.abs() % 60).toString().padLeft(2, '0'); String formattedOffset = 'GMT$offsetSign$hours$minutes'; String formattedDate = DateFormat('EEE MMM dd yyyy HH:mm:ss').format(dateTimeObj); // Mirrors JS `Date.toString()`, e.g. // "Wed Jul 29 2026 02:00:00 GMT+0100 (CET)". `formattedOffset` already // carries the "GMT" prefix, so it must not be prepended a second time. String finalFormattedDate = '$formattedDate $formattedOffset ($timeZoneOffsetString)'; log('check_time_availability request date : $finalFormattedDate'); final data = await http.get( Uri.https(url, '/api/v1/user_request/check_time_availability', {'date': finalFormattedDate}), headers: { 'Authorization': 'Bearer $token', 'Content-type': 'application/json', 'Accept': 'application/json' }, ); statusCode = data.statusCode; if (data.statusCode == 200 || data.statusCode == 201) { final jsonData = json.decode(data.body); log('check_time_availability response : $jsonData'); return jsonData is bool ? jsonData : null; } log('check_time_availability failed : ' '${data.statusCode} -> ${data.body}'); if (data.statusCode == 401 || data.statusCode == 403) { // The JWT carries no roles claim (sub/created/exp only), so the role // the server resolves from `sub` is only visible via the profile. log('check_time_availability token : ${_jwtDebugInfo(token)}, ' 'profile role : ${util.user?.role?.role}'); } return null; } catch (e, stack) { log('check_time_availability error : $e\n$stack'); return null; } } } /// Diagnostics only: decodes the (unverified) JWT payload so an auth failure /// can be told apart from an authorization failure in the logs. String _jwtDebugInfo(String jwt) { try { final parts = jwt.split('.'); if (parts.length != 3) return 'not a JWT -> "$jwt"'; final payload = json .decode(utf8.decode(base64Url.decode(base64Url.normalize(parts[1])))); final exp = payload['exp']; final expiry = exp is int ? DateTime.fromMillisecondsSinceEpoch(exp * 1000) : null; return 'sub=${payload['sub']}, exp=$expiry, ' 'expired=${expiry != null ? DateTime.now().isAfter(expiry) : 'unknown'}, ' 'roles=${payload['roles'] ?? payload['authorities'] ?? payload['scope']}'; } catch (e) { return 'could not decode ($e)'; } }