import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:image_picker/image_picker.dart'; import 'package:intl/intl.dart'; import 'package:logger/logger.dart'; import 'package:marco/helpers/services/storage/local_storage.dart'; import 'package:marco/helpers/services/auth_service.dart'; import 'package:marco/helpers/services/api_endpoints.dart'; final Logger logger = Logger(); class ApiService { static const Duration timeout = Duration(seconds: 10); static const bool enableLogs = true; // ===== Helpers ===== static Future _getToken() async { final token = await LocalStorage.getJwtToken(); if (token == null && enableLogs) { logger.w("No JWT token found. Please log in."); } return token; } static Map _headers(String token) => { 'Content-Type': 'application/json', 'Authorization': 'Bearer $token', }; static void _log(String message) { if (enableLogs) logger.i(message); } static dynamic _parseResponse(http.Response response, {String label = ''}) { _log("$label Response: ${response.body}"); try { final json = jsonDecode(response.body); if (response.statusCode == 200 && json['success'] == true) { return json['data']; } _log("API Error [$label]: ${json['message'] ?? 'Unknown error'}"); } catch (e) { _log("Response parsing error [$label]: $e"); } return null; } static Future _getRequest(String endpoint, {Map? queryParams, bool hasRetried = false}) async { String? token = await _getToken(); if (token == null) return null; Uri uri = Uri.parse("${ApiEndpoints.baseUrl}$endpoint") .replace(queryParameters: queryParams); _log("GET $uri"); try { http.Response response = await http.get(uri, headers: _headers(token)).timeout(timeout); if (response.statusCode == 401 && !hasRetried) { _log("Unauthorized. Attempting token refresh..."); bool refreshed = await AuthService.refreshToken(); if (refreshed) { token = await _getToken(); if (token != null) { return await _getRequest(endpoint, queryParams: queryParams, hasRetried: true); } } _log("Token refresh failed."); } return response; } catch (e) { _log("HTTP GET Exception: $e"); return null; } } static Future _postRequest( String endpoint, dynamic body) async { String? token = await _getToken(); if (token == null) return null; final uri = Uri.parse("${ApiEndpoints.baseUrl}$endpoint"); _log("POST $uri"); _log("Headers: ${_headers(token)}"); _log("Body: $body"); try { final response = await http .post(uri, headers: _headers(token), body: jsonEncode(body)) .timeout(timeout); _log("Response Status: ${response.statusCode}"); return response; } catch (e) { _log("HTTP POST Exception: $e"); return null; } } // ===== Attendence Screen API Calls ===== static Future?> getProjects() async { final response = await _getRequest(ApiEndpoints.getProjects); return response != null ? _parseResponse(response, label: 'Projects') : null; } static Future?> getEmployeesByProject(String projectId) async { final response = await _getRequest(ApiEndpoints.getEmployeesByProject, queryParams: {"projectId": projectId}); return response != null ? _parseResponse(response, label: 'Employees') : null; } static Future?> getAttendanceLogs(String projectId, {DateTime? dateFrom, DateTime? dateTo}) async { final query = { "projectId": projectId, if (dateFrom != null) "dateFrom": DateFormat('yyyy-MM-dd').format(dateFrom), if (dateTo != null) "dateTo": DateFormat('yyyy-MM-dd').format(dateTo), }; final response = await _getRequest(ApiEndpoints.getAttendanceLogs, queryParams: query); return response != null ? _parseResponse(response, label: 'Attendance Logs') : null; } static Future?> getAttendanceLogView(String id) async { final response = await _getRequest("${ApiEndpoints.getAttendanceLogView}/$id"); return response != null ? _parseResponse(response, label: 'Log Details') : null; } static Future?> getRegularizationLogs(String projectId) async { final response = await _getRequest(ApiEndpoints.getRegularizationLogs, queryParams: {"projectId": projectId}); return response != null ? _parseResponse(response, label: 'Regularization Logs') : null; } // ===== Upload Attendance Image ===== static Future uploadAttendanceImage( String id, String employeeId, XFile? imageFile, double latitude, double longitude, { required String imageName, required String projectId, String comment = "", required int action, bool imageCapture = true, String? markTime, // <-- Optional markTime parameter }) async { final now = DateTime.now(); final body = { "id": id, "employeeId": employeeId, "projectId": projectId, "markTime": markTime ?? DateFormat('hh:mm a').format(now), "comment": comment, "action": action, "date": DateFormat('yyyy-MM-dd').format(now), if (imageCapture) "latitude": '$latitude', if (imageCapture) "longitude": '$longitude', }; if (imageCapture && imageFile != null) { try { final bytes = await imageFile.readAsBytes(); final base64Image = base64Encode(bytes); final fileSize = await imageFile.length(); final contentType = "image/${imageFile.path.split('.').last}"; body["image"] = { "fileName": imageName, "contentType": contentType, "fileSize": fileSize, "description": "Employee attendance photo", "base64Data": base64Image, }; } catch (e) { _log("Image encoding error: $e"); return false; } } final response = await _postRequest(ApiEndpoints.uploadAttendanceImage, body); if (response == null) return false; final json = jsonDecode(response.body); if (response.statusCode == 200 && json['success'] == true) { return true; } else { _log("Failed to upload image: ${json['message'] ?? 'Unknown error'}"); } return false; } // ===== Utilities ===== static String generateImageName(String employeeId, int count) { final now = DateTime.now(); final dateStr = DateFormat('yyyyMMdd_HHmmss').format(now); final imageNumber = count.toString().padLeft(3, '0'); return "${employeeId}_${dateStr}_$imageNumber.jpg"; } // ===== Employee Screen API Calls ===== static Future?> getAllEmployeesByProject( String projectId) async { if (projectId.isEmpty) { throw ArgumentError('projectId must not be empty'); } final String endpoint = "${ApiEndpoints.getAllEmployeesByProject}/$projectId"; final response = await _getRequest(endpoint); return response != null ? _parseResponse(response, label: 'Employees by Project') : null; } static Future?> getAllEmployees() async { final response = await _getRequest(ApiEndpoints.getAllEmployees); return response != null ? _parseResponse(response, label: 'All Employees') : null; } static Future?> getRoles() async { final response = await _getRequest(ApiEndpoints.getRoles); return response != null ? _parseResponse(response, label: 'All Employees') : null; } static Future createEmployee({ required String firstName, required String lastName, required String phoneNumber, required String gender, required String jobRoleId, }) async { final body = { "firstName": firstName, "lastName": lastName, "phoneNumber": phoneNumber, "gender": gender, "jobRoleId": jobRoleId, }; // Make the API request final response = await _postRequest(ApiEndpoints.createEmployee, body); if (response == null) { _log("Error: No response from server."); return false; } final json = jsonDecode(response.body); if (response.statusCode == 200) { if (json['success'] == true) { return true; } else { _log( "Failed to create employee: ${json['message'] ?? 'Unknown error'}"); return false; } } else { _log( "Failed to create employee. Status code: ${response.statusCode}, Response: ${json['message'] ?? 'No message'}"); return false; } } // ===== Daily Tasks API Calls ===== static Future?> getDailyTasks(String projectId, {DateTime? dateFrom, DateTime? dateTo}) async { final query = { "projectId": projectId, if (dateFrom != null) "dateFrom": DateFormat('yyyy-MM-dd').format(dateFrom), if (dateTo != null) "dateTo": DateFormat('yyyy-MM-dd').format(dateTo), }; final response = await _getRequest(ApiEndpoints.getDailyTask, queryParams: query); return response != null ? _parseResponse(response, label: 'Daily Tasks') : null; } static Future reportTask({ required String id, required int completedTask, required String comment, required List> checkList, }) async { final body = { "id": id, "completedTask": completedTask, "comment": comment, "reportedDate": DateTime.now().toUtc().toIso8601String(), "checkList": checkList, }; final response = await _postRequest(ApiEndpoints.reportTask, body); if (response == null) { _log("Error: No response from server."); return false; } final json = jsonDecode(response.body); if (response.statusCode == 200 && json['success'] == true) { return true; } else { _log("Failed to report task: ${json['message'] ?? 'Unknown error'}"); return false; } } static Future commentTask({ required String id, required String comment, }) async { final body = { "taskAllocationId": id, "comment": comment, "commentDate": DateTime.now().toUtc().toIso8601String(), }; final response = await _postRequest(ApiEndpoints.commentTask, body); if (response == null) { _log("Error: No response from server."); return false; } final json = jsonDecode(response.body); if (response.statusCode == 200 && json['success'] == true) { return true; } else { _log("Failed to report task: ${json['message'] ?? 'Unknown error'}"); return false; } } }