import 'dart:async'; import 'dart:convert'; import 'dart:developer'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:http/http.dart' as http; import 'package:onufitness/constants/api_endpoints.dart'; import 'package:image_picker/image_picker.dart'; import 'package:file_picker/file_picker.dart'; import 'package:onufitness/controller/update_acces_token_controller.dart'; import 'package:onufitness/routes/route_constant.dart'; import 'package:onufitness/services/local_storage_services/shared_services.dart'; import 'package:http_parser/http_parser.dart'; import 'package:mime/mime.dart'; import 'package:onufitness/services/notification_services/notification_service.dart'; class ApiBase { static void handleAccountDeleted() { Get.defaultDialog( title: "Account Deactivated", middleText: "Your account has been deactivated by the administrator. You will now be logged out.", textConfirm: "OK", confirmTextColor: Colors.white, barrierDismissible: false, onConfirm: () async { await SharedServices.logout(); await NotificationService().deleteDeviceToken(); Get.offNamedUntil(RouteConstant.loginFirstScreen, (route) => false); }, ); } //............................................................................. static url({required String extendedURL}) { log("${ApiUrl.baseUrl}$extendedURL"); return Uri.parse("${ApiUrl.baseUrl}$extendedURL"); } //............................................................................. static Future getRequest({ required String extendedURL, String? token, bool sendHeaders = true, }) async { var client = http.Client(); Map newHeaders = {}; Map contentType = {'Content-Type': 'application/json'}; newHeaders.addAll(contentType); // Add the Authorization header if needed if (sendHeaders && SharedServices.isLoggedIn()) { newHeaders.addAll({'Authorization': token ?? SharedServices.userAuth()}); } log("URL : ${ApiUrl.baseUrl}$extendedURL"); http.Response response = await client.get( url(extendedURL: extendedURL.trim()), headers: sendHeaders ? newHeaders : null, ); // It Token Expired, then api call for update the token..................... if (response.statusCode == 401) { var isSuccess = await UpdateAccesTokenController.updateAccessToken(); if (isSuccess) { return getRequest( extendedURL: extendedURL, sendHeaders: sendHeaders, token: token, ); } } // ✅Handle account deleted (403) if (response.statusCode == 403) { handleAccountDeleted(); } return response; } //............................................................................. static Future postRequest({ required String extendedURL, required Object body, String? token, bool sendHeaders = true, }) async { var client = http.Client(); Map newHeaders = {}; Map contentType = {'Content-Type': 'application/json'}; newHeaders.addAll(contentType); // Add the Authorization header if needed if (sendHeaders && SharedServices.isLoggedIn()) { newHeaders.addAll({'Authorization': token ?? SharedServices.userAuth()}); } log("URL : ${ApiUrl.baseUrl}$extendedURL"); http.Response response = await client.post( url(extendedURL: extendedURL), headers: newHeaders, // Always send headers for POST requests body: jsonEncode(body), ); // It Token Expired, then api call for update the token..................... // 401 is the status code of UnAuthorize user................................ if (response.statusCode == 401) { var isSuccess = await UpdateAccesTokenController.updateAccessToken(); if (isSuccess) { return postRequest( extendedURL: extendedURL, body: body, sendHeaders: sendHeaders, token: token, ); } } // ✅Handle account deleted (403) if (response.statusCode == 403) { handleAccountDeleted(); } return response; } //............................................................................. static Future putRequest({ required String extendedURL, required Object body, String? token, bool sendHeaders = true, }) async { var client = http.Client(); Map newHeaders = {}; Map contentType = {'Content-Type': 'application/json'}; newHeaders.addAll(contentType); // Add the Authorization header if needed if (sendHeaders && SharedServices.isLoggedIn()) { newHeaders.addAll({'Authorization': token ?? SharedServices.userAuth()}); } log("URL : ${ApiUrl.baseUrl}$extendedURL"); http.Response response = await client.put( url(extendedURL: extendedURL), headers: newHeaders, // Always send headers for PUT requests body: jsonEncode(body), ); // It Token Expired, then api call for update the token..................... // 401 is the status code of UnAuthorize user................................ if (response.statusCode == 401) { var isSuccess = await UpdateAccesTokenController.updateAccessToken(); if (isSuccess) { return putRequest( extendedURL: extendedURL, body: body, sendHeaders: sendHeaders, token: token, ); } } // ✅Handle account deleted (403) if (response.statusCode == 403) { handleAccountDeleted(); } return response; } //............................................................................. static Future patchRequest({ required String extendedURL, Object? body, String? token, bool sendHeaders = true, }) async { var client = http.Client(); Map newHeaders = {}; Map contentType = {'Content-Type': 'application/json'}; newHeaders.addAll(contentType); // Add the Authorization header if needed if (sendHeaders && SharedServices.isLoggedIn()) { newHeaders.addAll({'Authorization': token ?? SharedServices.userAuth()}); } log("URL : ${ApiUrl.baseUrl}$extendedURL"); http.Response response = await client.patch( url(extendedURL: extendedURL), headers: newHeaders, // Always send headers for Patch requests body: jsonEncode(body), ); // It Token Expired, then api call for update the token..................... // 401 is the status code of UnAuthorize user................................ if (response.statusCode == 401) { var isSuccess = await UpdateAccesTokenController.updateAccessToken(); if (isSuccess) { return patchRequest( extendedURL: extendedURL, body: body, sendHeaders: sendHeaders, token: token, ); } } // ✅Handle account deleted (403) if (response.statusCode == 403) { handleAccountDeleted(); } return response; } //............................................................................. static Future deleteRequest({ required String extendedURL, required Object body, String? token, bool sendHeaders = true, }) async { var client = http.Client(); Map newHeaders = {}; Map contentType = {'Content-Type': 'application/json'}; newHeaders.addAll(contentType); // Add the Authorization header if needed if (sendHeaders && SharedServices.isLoggedIn()) { newHeaders.addAll({'Authorization': token ?? SharedServices.userAuth()}); } log("URL : ${ApiUrl.baseUrl}$extendedURL"); http.Response response = await client.delete( url(extendedURL: extendedURL), headers: newHeaders, // Always send headers for Delete requests body: jsonEncode(body), ); // It Token Expired, then api call for update the token..................... // 401 is the status code of UnAuthorize user................................ if (response.statusCode == 401) { var isSuccess = await UpdateAccesTokenController.updateAccessToken(); if (isSuccess) { return deleteRequest( extendedURL: extendedURL, body: body, sendHeaders: sendHeaders, token: token, ); } } // ✅Handle account deleted (403) if (response.statusCode == 403) { handleAccountDeleted(); } return response; } //............................................................................. static Future postSingleFileRequest({ required String extendedURL, required XFile file, Map? body, String? token, bool sendHeaders = true, required String fileNameKey, }) async { try { final uri = url(extendedURL: extendedURL); var request = http.MultipartRequest('POST', uri); Map newHeaders = {}; if (sendHeaders && SharedServices.isLoggedIn()) { newHeaders['Authorization'] = token ?? SharedServices.userAuth(); } request.headers.addAll(newHeaders); if (body != null) { request.fields.addAll( body.map((key, value) => MapEntry(key, value.toString())), ); } request.files.add( await http.MultipartFile.fromPath(fileNameKey, file.path), ); var streamedResponse = await request.send(); var response = await http.Response.fromStream(streamedResponse); log('Upload response status: ${response.statusCode}'); log('Upload response body: ${response.body}'); if (response.statusCode == 401) { var isSuccess = await UpdateAccesTokenController.updateAccessToken(); if (isSuccess) { return postSingleFileRequest( extendedURL: extendedURL, body: body, sendHeaders: sendHeaders, token: token, file: file, fileNameKey: fileNameKey, ); } } // ✅Handle account deleted (403) if (response.statusCode == 403) { handleAccountDeleted(); } return response; } catch (e, stackTrace) { log("Error uploading image: $e"); log("StackTrace: $stackTrace"); return http.Response('Error uploading image', 500); } } //............................................................................. static Future postMultipleFiles({ required String extendedURL, required List files, Map? body, required String fileNameKey, String? token, bool sendHeaders = true, }) async { try { var request = http.MultipartRequest( 'POST', url(extendedURL: extendedURL), ); Map newHeaders = {}; if (sendHeaders && SharedServices.isLoggedIn()) { newHeaders['Authorization'] = token ?? SharedServices.userAuth(); } request.headers.addAll(newHeaders); // Add body fields if any if (body != null) { request.fields.addAll( body.map((key, value) => MapEntry(key, value.toString())), ); } // 🔽 Add each file with proper MIME type for (var file in files) { final mimeType = lookupMimeType(file.path) ?? 'application/octet-stream'; final mimeSplit = mimeType.split('/'); request.files.add( await http.MultipartFile.fromPath( fileNameKey, file.path, contentType: MediaType(mimeSplit[0], mimeSplit[1]), filename: file.name, ), ); log("Added file: ${file.name}, MimeType: $mimeType"); } var streamedResponse = await request.send(); var response = await http.Response.fromStream(streamedResponse); if (response.statusCode == 401) { var isSuccess = await UpdateAccesTokenController.updateAccessToken(); if (isSuccess) { return postMultipleFiles( extendedURL: extendedURL, body: body, sendHeaders: sendHeaders, token: token, files: files, fileNameKey: fileNameKey, ); } } // ✅Handle account deleted (403) if (response.statusCode == 403) { handleAccountDeleted(); } return response; } catch (e, stackTrace) { log("Error uploading files: $e"); log("StackTrace: $stackTrace"); return http.Response('Error uploading files', 500); } } //.........post Video Image and Documents with Mime Type.......................................................................................................... static Future postAnyFileWithMimeType({ required String extendedURL, XFile? file, // Made file optional Map? body, String? token, bool sendHeaders = true, String? fileNameKey, // Made fileNameKey optional since file is optional }) async { try { final uri = url(extendedURL: extendedURL); var request = http.MultipartRequest('POST', uri); Map newHeaders = {}; if (sendHeaders && SharedServices.isLoggedIn()) { newHeaders['Authorization'] = token ?? SharedServices.userAuth(); } request.headers.addAll(newHeaders); if (body != null) { request.fields.addAll( body.map((key, value) => MapEntry(key, value.toString())), ); } // ✅ Only add file if it's provided........................................... if (file != null && fileNameKey != null) { final mimeType = lookupMimeType(file.path) ?? 'application/octet-stream'; final mimeSplit = mimeType.split('/'); request.files.add( await http.MultipartFile.fromPath( fileNameKey, file.path, contentType: MediaType(mimeSplit[0], mimeSplit[1]), filename: file.name, ), ); log("File Name : ${file.name}"); log("Mime Type : $mimeType"); } var streamedResponse = await request.send(); var response = await http.Response.fromStream(streamedResponse); log('Upload response status: ${response.statusCode}'); log('Upload response body: ${response.body}'); if (response.statusCode == 401) { var isSuccess = await UpdateAccesTokenController.updateAccessToken(); if (isSuccess) { return await postAnyFileWithMimeType( extendedURL: extendedURL, body: body, sendHeaders: sendHeaders, token: token, file: file, fileNameKey: fileNameKey, ); } } // ✅Handle account deleted (403) if (response.statusCode == 403) { handleAccountDeleted(); } return response; } catch (e, stackTrace) { log("Error uploading: $e"); log("StackTrace: $stackTrace"); return http.Response('Error uploading', 500); } } //............................................................................ // NEW METHOD: Post with multiple files having different field names //............................................................................ static Future postMultipleIndivisualFiles({ required String extendedURL, Map? body, Map? files, // Map of fieldName -> file (PlatformFile or XFile) String? token, bool sendHeaders = true, }) async { try { var request = http.MultipartRequest( 'POST', url(extendedURL: extendedURL), ); Map newHeaders = {}; if (sendHeaders && SharedServices.isLoggedIn()) { newHeaders['Authorization'] = token ?? SharedServices.userAuth(); } request.headers.addAll(newHeaders); // Add body fields if any if (body != null) { request.fields.addAll( body.map((key, value) => MapEntry(key, value.toString())), ); } // Add files with different field names if (files != null) { for (var entry in files.entries) { final fieldName = entry.key; final file = entry.value; if (file != null) { String filePath; String fileName; // Handle both PlatformFile and XFile if (file is PlatformFile) { filePath = file.path!; fileName = file.name; } else if (file is XFile) { filePath = file.path; fileName = file.name; } else { log("Unsupported file type for field: $fieldName"); continue; } final mimeType = lookupMimeType(filePath) ?? 'application/octet-stream'; final mimeSplit = mimeType.split('/'); request.files.add( await http.MultipartFile.fromPath( fieldName, filePath, contentType: MediaType(mimeSplit[0], mimeSplit[1]), filename: fileName, ), ); log( "Added file: $fileName to field: $fieldName, MimeType: $mimeType", ); } } } var streamedResponse = await request.send(); var response = await http.Response.fromStream(streamedResponse); log('Upload response status: ${response.statusCode}'); log('Upload response body: ${response.body}'); if (response.statusCode == 401) { var isSuccess = await UpdateAccesTokenController.updateAccessToken(); if (isSuccess) { return postMultipleIndivisualFiles( extendedURL: extendedURL, body: body, files: files, sendHeaders: sendHeaders, token: token, ); } } // ✅Handle account deleted (403) if (response.statusCode == 403) { handleAccountDeleted(); } return response; } catch (e, stackTrace) { log("Error uploading files: $e"); log("StackTrace: $stackTrace"); return http.Response('Error uploading files', 500); } } // Update File Patch Request............................................................................... static Future patchAnyFileWithMimeType({ required String extendedURL, XFile? file, // Made file optional Map? body, String? token, bool sendHeaders = true, String? fileNameKey, // Made fileNameKey optional since file is optional }) async { try { final uri = url(extendedURL: extendedURL); var request = http.MultipartRequest('PATCH', uri); Map newHeaders = {}; if (sendHeaders && SharedServices.isLoggedIn()) { newHeaders['Authorization'] = token ?? SharedServices.userAuth(); } request.headers.addAll(newHeaders); if (body != null) { request.fields.addAll( body.map((key, value) => MapEntry(key, value.toString())), ); } // ✅ Only add file if it's provided........................................... if (file != null && fileNameKey != null) { final mimeType = lookupMimeType(file.path) ?? 'application/octet-stream'; final mimeSplit = mimeType.split('/'); request.files.add( await http.MultipartFile.fromPath( fileNameKey, file.path, contentType: MediaType(mimeSplit[0], mimeSplit[1]), filename: file.name, ), ); log("File Name : ${file.name}"); log("Mime Type : $mimeType"); } var streamedResponse = await request.send(); var response = await http.Response.fromStream(streamedResponse); if (response.statusCode == 401) { var isSuccess = await UpdateAccesTokenController.updateAccessToken(); if (isSuccess) { return await postAnyFileWithMimeType( extendedURL: extendedURL, body: body, sendHeaders: sendHeaders, token: token, file: file, fileNameKey: fileNameKey, ); } } // ✅Handle account deleted (403) if (response.statusCode == 403) { handleAccountDeleted(); } return response; } catch (e, stackTrace) { log("Error uploading: $e"); log("StackTrace: $stackTrace"); return http.Response('Error uploading', 500); } } }