import 'dart:core'; import 'dart:math'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/common_models/data_points.dart'; import 'package:hmg_patient_app_new/core/utils/date_util.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart' show Utils; import 'package:hmg_patient_app_new/features/lab/lab_repo.dart'; import 'package:hmg_patient_app_new/features/lab/models/resp_models/lab_result.dart'; import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_results/lab_result_details.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:intl/intl.dart' show DateFormat; import 'package:logger/logger.dart'; class LabViewModel extends ChangeNotifier { bool isLabOrdersLoading = false; bool isLabResultsLoading = false; LabRepo labRepo; ErrorHandlerService errorHandlerService; NavigationService navigationService; List patientLabOrders = []; List filteredLabOrders = []; List tempLabOrdersList = []; List mainLabResults = []; List mainGraphPoints = []; List filteredGraphValues = []; List months = [ 'Jan', 'Feb', 'Mar', 'April', 'May', 'Jun', 'July', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ]; late List _labSuggestionsList = []; List get labSuggestions => _labSuggestionsList; Set uniqueTests = {}; double maxY = 0.0; double maxX = double.infinity; LabViewModel( {required this.labRepo, required this.errorHandlerService, required this.navigationService}); initLabProvider() { patientLabOrders.clear(); filteredLabOrders.clear(); isLabOrdersLoading = true; isLabResultsLoading = true; getPatientLabOrders(); notifyListeners(); } Future getPatientLabOrders({Function(dynamic)? onSuccess, Function(String)? onError}) async { final result = await labRepo.getPatientLabOrders(); result.fold( (failure) async => await errorHandlerService.handleError(failure: failure), (apiResponse) { if (apiResponse.messageStatus == 2) { // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); } else if (apiResponse.messageStatus == 1) { patientLabOrders = apiResponse.data!; filteredLabOrders = List.from(patientLabOrders); tempLabOrdersList = apiResponse.data!; isLabOrdersLoading = false; isLabResultsLoading = false; filterSuggestions(); getUniqueTestDescription(); notifyListeners(); if (onSuccess != null) { onSuccess(apiResponse); } } }, ); } filterSuggestions() { final List labels = patientLabOrders .expand((order) => order.testDetails!) .map((detail) => detail.description) .whereType() .toList(); _labSuggestionsList = labels.toSet().toList(); notifyListeners(); } filterLabReports(String query) { if (query.isEmpty) { filteredLabOrders = List.from(patientLabOrders); // reset } else { filteredLabOrders = patientLabOrders.where((order) { final descriptions = order.testDetails?.map((d) => d.description?.toLowerCase()).toList() ?? []; return descriptions.any((desc) => desc != null && desc.contains(query.toLowerCase())); }).toList(); patientLabOrders = filteredLabOrders; } notifyListeners(); } getUniqueTestDescription() { uniqueTests = { for (var item in patientLabOrders) if (item.testDetails != null) ...?item.testDetails?.map((test) => TestDetails( description: test.description.toString(), testCode: test.testCode.toString(), testID: test.testID, createdOn: item.createdOn, model: item)) }; } Future getPatientLabResult( PatientLabOrdersResponseModel laborder, String procedureName) async { LoaderBottomSheet.showLoader(); mainLabResults.clear(); filteredGraphValues.clear(); maxY = double.negativeInfinity; final result = await labRepo.getPatientLabResults( laborder, Utils.isVidaPlusProject(int.parse(laborder.projectID ?? "0")), procedureName); result.fold( (failure) async { LoaderBottomSheet.hideLoader(); await errorHandlerService.handleError(failure: failure); }, (apiResponse) { LoaderBottomSheet.hideLoader(); if (apiResponse.messageStatus == 2) { } else if (apiResponse.messageStatus == 1) { var sortedResponse = sortByFlagAndValue(apiResponse.data!); var recentThree = sort(sortedResponse); mainLabResults = recentThree; double counter = 1; recentThree.reversed.forEach((element) { try { var dateTime = DateUtil.convertStringToDate(element.verifiedOnDateTime!); var resultValue = double.parse(element.resultValue!); var transformedValue = transformValueInRange(double.parse(element.resultValue!), element.calculatedResultFlag??""); if (resultValue>maxY) { maxY = resultValue; maxX = maxY; } filteredGraphValues.add(DataPoint( value: transformedValue, actualValue:element.resultValue!, label: formatDateAsMMYY(dateTime), displayTime: resultDate(dateTime), time: DateUtil.convertStringToDate(element.verifiedOnDateTime), referenceValue: element.calculatedResultFlag ?? "", )); counter++; } catch (e) {} }); LabResult recentResult = recentThree.first; recentResult.verifiedOn = resultDate(DateUtil.convertStringToDate(recentResult.verifiedOnDateTime!)); // filteredGraphValues = [filteredGraphValues.first]; navigationService.push(MaterialPageRoute( builder: (_) => LabResultDetails(recentLabResult: recentResult))); notifyListeners(); } }, ); } String resultDate(DateTime date){ return '${date.day} ${months[date.month-1]},${date.year}'; } double transformValueInRange(double inputValue, String flag) { // Define range boundaries double rangeStart, rangeEnd; switch (flag) { case'LCL': case 'CL': rangeStart = 0.0; rangeEnd = 19.0; break; case 'L': rangeStart = 20.0; rangeEnd = 39.0; break; case 'N': rangeStart = 40.0; rangeEnd = 59.0; break; case 'H': rangeStart = 60.0; rangeEnd = 79.0; break; case 'HCH': case 'CH': rangeStart = 80.0; rangeEnd = 100.0; break; default: throw ArgumentError('Invalid flag: $flag'); } // Clamp input value to 0-100 and map it to the range bounds final clampedValue = inputValue.clamp(0.0, 100.0); final normalizedValue = clampedValue / 100.0; // Normalize input to 0-1 // Map the normalized value to the target range bounds final transformedValue = rangeStart + ((normalizedValue * (rangeEnd - rangeStart))); debugPrint("the actual value is $inputValue"); debugPrint("the flag is $flag"); debugPrint("the transformed value is $transformedValue"); return transformedValue; } void getSelectedDateRange(DateTime? start, DateTime? end) { maxY = double.negativeInfinity; if(start == null && end == null) { mainLabResults.forEach((element) { final time = DateUtil.convertStringToDate(element.verifiedOnDateTime!); try{ var resultValue = double.parse(element.resultValue!); var transformedValue = transformValueInRange(double.parse(element.resultValue!), element.calculatedResultFlag??""); if (resultValue > maxY) { maxY = resultValue; } filteredGraphValues.add(DataPoint( value: transformedValue, actualValue: element.resultValue!, label: formatDateAsMMYY(time), displayTime: resultDate(time), time: DateUtil.convertStringToDate(element.verifiedOnDateTime), referenceValue: element.calculatedResultFlag ?? "", )); }catch(e){ } }); }else { filteredGraphValues.clear(); mainLabResults.forEach((element) { try { var dateTime = DateUtil.convertStringToDate(element.verifiedOnDateTime!); var resultValue = double.parse(element.resultValue!); var transformedValue = transformValueInRange(double.parse(element.resultValue!), element.calculatedResultFlag??""); if (resultValue > maxY) { maxY = resultValue; } if (start != null && end == null) { if (dateTime.isAtSameMomentAs(start)) { filteredGraphValues.add(DataPoint( value: transformedValue, actualValue: element.resultValue!, label: formatDateAsMMYY(dateTime), displayTime: resultDate(dateTime), time: DateUtil.convertStringToDate(element.verifiedOnDateTime), referenceValue: element.calculatedResultFlag ?? "")); } } else if (start != null && end != null) { if ((dateTime.isAfter(start)) && (dateTime.isBefore(end))) { filteredGraphValues.add(DataPoint( value: transformedValue, actualValue: element.resultValue!, label: formatDateAsMMYY(dateTime), displayTime: resultDate(dateTime), time: DateUtil.convertStringToDate(element.verifiedOnDateTime), referenceValue: element.calculatedResultFlag ?? "")); } } } catch (e) {} }); } filteredGraphValues = sortFilteredList(filteredGraphValues).reversed.toList(); notifyListeners(); } String formatDateAsMMYY(DateTime date) { return '${months[date.month-1]}, ${date.year}'; } List sortByFlagAndValue(List original) { const priorityOrder = ['LCL', 'CL', 'L', 'N', 'H', 'CH', 'HCH']; int getFlagPriority(String? flag) { if (flag == null) return priorityOrder.length; final index = priorityOrder.indexOf(flag); return index == -1 ? priorityOrder.length : index; } double parseResultValue(String? value) { if (value == null) return double.nan; return double.tryParse(value) ?? double.nan; } final copy = List.from(original); copy.sort((a, b) { final aFlagPriority = getFlagPriority(a.calculatedResultFlag); final bFlagPriority = getFlagPriority(b.calculatedResultFlag); if (aFlagPriority != bFlagPriority) { return aFlagPriority.compareTo(bFlagPriority); } final aValue = parseResultValue(a.resultValue); final bValue = parseResultValue(b.resultValue); return aValue.compareTo(bValue); }); return copy; } List sort(List original) { DateTime? parseVerifiedDate(String? raw) { if (raw == null) return null; final regex = RegExp(r'\/Date\((\d+)\)\/'); final match = regex.firstMatch(raw); if (match != null) { final millis = int.tryParse(match.group(1)!); if (millis != null) { return DateTime.fromMillisecondsSinceEpoch(millis); } } return null; } final copy = List.from(original); copy.sort((a, b) { final aDate = DateUtil.convertStringToDate(a.verifiedOnDateTime); final bDate = DateUtil.convertStringToDate(b.verifiedOnDateTime); final now = DateTime.now(); if (aDate == now && bDate == now) return 0; if (aDate == now) return 1; if (bDate == now) return -1; return bDate.compareTo(aDate); // descending }); return copy.toList(); } List sortFilteredList(List original) { final copy = List.from(original); copy.sort((a, b) { final aDate =a.time; final bDate = a.time; final now = DateTime.now(); if (aDate == now && bDate == now) return 0; if (aDate == now) return 1; if (bDate == now) return -1; return bDate.compareTo(aDate); // descending }); return copy.toList(); } Color getColor(String flag) { switch (flag) { case 'LCL': return AppColors.criticalLowAndHigh; case 'CL': return AppColors.criticalLowAndHigh; case 'L': return AppColors.highAndLow; case 'N': return AppColors.bgGreenColor; case 'H': return AppColors.highAndLow; case 'CH': return AppColors.criticalLowAndHigh; case 'HCH': return AppColors.criticalLowAndHigh; default: return Colors.grey; } } String getFormattedDate(DateTime date){ return DateFormat('EEEE, dd MMMM. yyyy').format(date); } String getAssetUrlWRTResult(String refernceValue) { switch (refernceValue) { case 'CL': case 'LCL': return AppAssets.ic_critical_low_result; case 'L': return AppAssets.ic_low_result; case 'N': return AppAssets.ic_normal_result; case 'H': return AppAssets.ic_low_result; case 'CH': case 'HCH': return AppAssets.ic_critical_low_result; default: return AppAssets.ic_normal_result; } } bool getRotationWRTResult(String refernceValue) { switch (refernceValue) { case 'CL': case 'LCL': case 'L': case 'N': return false; case 'H': case 'CH': case 'HCH': return true; default: return true; } } }