import 'dart:collection';
import 'dart:convert';
import 'dart:core';

import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/common_models/data_points.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/loading_utils.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/ai_overview_labresult_model_payload.dart';
import 'package:hmg_patient_app_new/features/lab/models/resp_models/ai_overview_labresults_by_ai_model.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_order_response_by_ai_model.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_result_via_clinic/LabResultByClinic.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_results/lab_result_details.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_clinic/lab_ai_analysis_detailed_page.dart';
import 'package:hmg_patient_app_new/services/cache_service.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:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:intl/intl.dart' show DateFormat;

class LabViewModel extends ChangeNotifier {
  bool isLabOrdersLoading = false;
  bool isLabResultsLoading = false;
  bool isLabResultByHospitalLoading = false;
  bool isSpecialResultsLoading = false;
  bool isGraphVisible = true;
  bool shouldShowGraph = true;

  bool isLabNeedToLoad = true;

  LabRepo labRepo;
  ErrorHandlerService errorHandlerService;
  NavigationService navigationService;

  List<PatientLabOrdersResponseModel> patientLabOrders = [];
  List<PatientLabOrdersResponseModel> filteredLabOrders = [];
  List<PatientLabOrdersResponseModel> tempLabOrdersList = [];
  String labSpecialResult = "";
  List<String> labOrderTests = [];
  String patientLabResultReportPDFBase64 = "";
  String? flagForHighReferenceRange;
  double highRefrenceValue = double.negativeInfinity;
  double lowRefenceValue = double.infinity;

  double highTransformedReferenceValue = double.negativeInfinity;
  double lowTransformedReferenceValue = double.infinity;
  String? flagForLowReferenceRange;

  PatientLabOrdersResponseModel? currentlySelectedPatientOrder;

  // Flat list for UI compatibility
  List<LabResult> mainLabResultsByHospitalsList = [];
  
  // Grouped map by package name for efficient lookups
  Map<String, List<LabResult>> mainLabResultsByPackageName = {};

  List<LabResult> mainLabResults = [];
  List<DataPoint> mainGraphPoints = [];
  List<DataPoint> filteredGraphValues = [];
  List months = ['Jan', 'Feb', 'Mar', 'April', 'May', 'Jun', 'July', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

  late List<String> _labSuggestionsList = [];

  List<String> get labSuggestions => _labSuggestionsList;

  Set<TestDetails> uniqueTests = {};
  List<TestDetails> uniqueTestsList = [];
  List<String> indexedCharacterForUniqueTest = [];

  List<TestDetails> filteredUniqueTestsList = [];
  List<String> filteredIndexedCharacterForUniqueTest = [];

  double maxY = 0.0;
  double minY = double.infinity;
  double maxX = double.infinity;

  // --- New grouping fields ---
  bool isSortByClinic = true;
  List<List<PatientLabOrdersResponseModel>> patientLabOrdersByClinic = [];
  List<List<PatientLabOrdersResponseModel>> patientLabOrdersByHospital = [];
  List<List<PatientLabOrdersResponseModel>> patientLabOrdersViewList = [];

  LabOrderResponseByAi? labOrderResponseByAi;
  LabOrdersResponseByAi? labOrdersResponseByAi;

  bool isLabAIAnalysisNeedsToBeShown = true;
  bool isLabResultsHistoryShowMore = false;

  LabViewModel({required this.labRepo, required this.errorHandlerService, required this.navigationService});

  initLabProvider() {
    // if (isLabNeedToLoad) {
    patientLabOrders.clear();
    filteredLabOrders.clear();
    labOrderTests.clear();
    isLabOrdersLoading = true;
    isLabResultsLoading = true;
    isLabAIAnalysisNeedsToBeShown = true;
    isLabResultsHistoryShowMore = false;
    patientLabOrdersByClinic.clear();
    patientLabOrdersByHospital.clear();
    patientLabOrdersViewList.clear();
    getPatientLabOrders();
    // }
    notifyListeners();
  }

  setIsLabNeedToLoad(bool value) {
    isLabNeedToLoad = value;
    notifyListeners();
  }

  closeAILabResultAnalysis() {
    labOrderResponseByAi = null;
    notifyListeners();
  }

  setIsLabAIAnalysisNeedsToBeShown(bool isLabAIAnalysisNeedsToBeShown) {
    this.isLabAIAnalysisNeedsToBeShown = isLabAIAnalysisNeedsToBeShown;
    notifyListeners();
  }

  setIsLabResultsHistoryShowMore() {
    isLabResultsHistoryShowMore = !isLabResultsHistoryShowMore;
    notifyListeners();
  }

  void setIsSortByClinic(bool value) {
    isSortByClinic = value;
    patientLabOrdersViewList = isSortByClinic ? patientLabOrdersByClinic : patientLabOrdersByHospital;
    notifyListeners();
  }

  Future<void> getPatientLabOrders({Function(dynamic)? onSuccess, Function(String)? onError}) async {
    // if (!isLabNeedToLoad) return;

    isLabOrdersLoading = true;
    patientLabOrders.clear();
    uniqueTests.clear();
    labOrderTests.clear();
    uniqueTests = {};
    patientLabOrdersByClinic.clear();
    patientLabOrdersByHospital.clear();
    patientLabOrdersViewList.clear();
    notifyListeners();

    final result = await labRepo.getPatientLabOrders();

    result.fold(
      (failure) async {
        isLabOrdersLoading = false;
        isLabResultsLoading = false;
        notifyListeners();
      },
      // => await errorHandler_service.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;

          for (var order in patientLabOrders) {
            order.testDetails!.sort((a, b) => a.description!.compareTo(b.description!));
          }

          // --- Build groups by clinic and by hospital (projectName) ---
          // final clinicMap = <String, List<PatientLabOrdersResponseModel>>{};
          // final hospitalMap = <String, List<PatientLabOrdersResponseModel>>{};
          //
          // for (var order in patientLabOrders) {
          //   final clinicKey = (order.clinicDescription ?? 'Unknown').trim();
          //   clinicMap.putIfAbsent(clinicKey, () => []).add(order);
          //
          //   final hospitalKey = (order.projectName ?? order.projectID ?? 'Unknown').toString().trim();
          //   hospitalMap.putIfAbsent(hospitalKey, () => []).add(order);
          // }

          // patientLabOrdersByClinic = clinicMap.values.toList();
          // patientLabOrdersByHospital = hospitalMap.values.toList();
          // patientLabOrdersViewList = isSortByClinic ? patientLabOrdersByClinic : patientLabOrdersByHospital;
          // patientLabOrdersViewList = patientLabOrdersByClinic;

          filterSuggestions();
          getUniqueTestDescription();
          isLabNeedToLoad = false;
          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );
  }

  Future<void> getLabResultReportPDF({required PatientLabOrdersResponseModel labOrder, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    final result = await labRepo.getLabResultReportPDF(labOrder: labOrder);

    result.fold(
      (failure) async => await errorHandlerService.handleError(
        failure: failure,
        onOkPressed: () {
          onError!(failure.message);
        },
      ),
      (apiResponse) {
        if (apiResponse.messageStatus == 2) {
          onError!(apiResponse.errorMessage!);
          // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
        } else if (apiResponse.messageStatus == 1) {
          patientLabResultReportPDFBase64 = apiResponse.data!;
          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );
  }

  filterSuggestions() {
    final List<String> labels = patientLabOrders.expand((order) => order.testDetails ?? []).map((detail) => detail.description).whereType<String>().toList();
    _labSuggestionsList = labels.toSet().toList();

    notifyListeners();
  }

  filterLabReports(String query) {
    final clinicMap = <String, List<PatientLabOrdersResponseModel>>{};
    final hospitalMap = <String, List<PatientLabOrdersResponseModel>>{};
    if (query.isEmpty) {
      filteredLabOrders = List.from(patientLabOrders); // reset
      filteredUniqueTestsList = List.from(uniqueTestsList);
      for (var order in patientLabOrders) {
        final clinicKey = (order.clinicDescription ?? 'Unknown').trim();
        clinicMap.putIfAbsent(clinicKey, () => []).add(order);

        final hospitalKey = (order.projectName ?? order.projectID ?? 'Unknown').toString().trim();
        hospitalMap.putIfAbsent(hospitalKey, () => []).add(order);
      }
      patientLabOrdersByClinic = clinicMap.values.toList();
      patientLabOrdersByHospital = hospitalMap.values.toList();
      patientLabOrdersViewList = isSortByClinic ? patientLabOrdersByClinic : patientLabOrdersByHospital;
    } 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();
      filteredUniqueTestsList = uniqueTestsList.where((test) {
        final desc = test.description?.toLowerCase() ?? '';
        return desc.contains(query.toLowerCase());
      }).toList();
      for (var order in filteredLabOrders) {
        final clinicKey = (order.clinicDescription ?? 'Unknown').trim();
        clinicMap.putIfAbsent(clinicKey, () => []).add(order);

        final hospitalKey = (order.projectName ?? order.projectID ?? 'Unknown').toString().trim();
        hospitalMap.putIfAbsent(hospitalKey, () => []).add(order);
      }
      patientLabOrdersByClinic = clinicMap.values.toList();
      patientLabOrdersByHospital = hospitalMap.values.toList();
      patientLabOrdersViewList = isSortByClinic ? patientLabOrdersByClinic : patientLabOrdersByHospital;
    }
    // Rebuild filtered indexed characters
    filteredIndexedCharacterForUniqueTest = [];
    for (var test in filteredUniqueTestsList) {
      String label = test.description ?? "";
      if (label.isEmpty) continue;
      if (filteredIndexedCharacterForUniqueTest.contains(label[0].toLowerCase())) continue;
      filteredIndexedCharacterForUniqueTest.add(label[0].toLowerCase());
    }
    notifyListeners();
  }

  getUniqueTestDescription() {
    uniqueTests = {
      for (var item in patientLabOrders)
        if (item.testDetails != null)
          ...?item.testDetails?.map<TestDetails>((test) => TestDetails(
              testDescriptionEn: test.testDescriptionEn.toString(),
              testDescriptionAr: test.testDescriptionAr.toString(),
              description: test.description.toString(),
              testCode: test.testCode.toString(),
              testID: test.testID,
              createdOn: item.createdOn,
              model: item))
    };
    uniqueTestsList = uniqueTests.toList();

    uniqueTestsList = Utils.uniqueBy(uniqueTestsList, (p) => p.testCode);

    uniqueTestsList.sort((a, b) {
      return a.description!.toLowerCase().compareTo(b.description!.toLowerCase());
    });
    indexedCharacterForUniqueTest.clear();
    for (var test in uniqueTestsList) {
      String label = test.description ?? "";
      if (label.isEmpty) continue;
      if (indexedCharacterForUniqueTest.contains(label[0].toLowerCase())) continue;
      indexedCharacterForUniqueTest.add(label[0].toLowerCase());
    }

    for (var element in uniqueTests) {
      labOrderTests.add(element.description ?? "");
    }

    // Initialize filtered lists with full data
    filteredUniqueTestsList = List.from(uniqueTestsList);
    filteredIndexedCharacterForUniqueTest = List.from(indexedCharacterForUniqueTest);
  }

  Future<void> getLabResultsByAppointmentNo(
      {required num appointmentNo,
      required num projectID,
      required num clinicID,
      required int doctorID,
      required String clinicName,
      required String doctorName,
      required String projectName,
      required String appointmentDate,
      Function(dynamic)? onSuccess,
      Function(String)? onError}) async {
    bool isVidaPlus = Utils.isVidaPlusProject(projectID.toInt());
    final result = await labRepo.getLabResultsByAppointmentNo(appointmentNo: appointmentNo, projectID: projectID, clinicID: clinicID);

    result.fold(
      (failure) async {
        // await errorHandlerService.handleError(failure: failure);
        if (onError != null) {
          onError(failure.message);
        }
      },
      (apiResponse) {
        if (apiResponse.messageStatus == 2) {
          if (onError != null) {
            onError(apiResponse.errorMessage!);
          }
        } else if (apiResponse.messageStatus == 1) {
          if (apiResponse.data != null && apiResponse.data!.isNotEmpty) {
            PatientLabOrdersResponseModel labOrder = PatientLabOrdersResponseModel();

            labOrder.invoiceNoVP = isVidaPlus ? apiResponse.data[0]['InvoiceNo'].toString() : "0";
            labOrder.invoiceNo = isVidaPlus ? "0" : apiResponse.data[0]['InvoiceNo'].toString();
            labOrder.orderNo = apiResponse.data[0]['OrderNo'].toString();
            labOrder.invoiceType = apiResponse.data[0]['InvoiceType'].toString();
            labOrder.setupID = apiResponse.data[0]['SetupID'].toString();
            labOrder.projectID = projectID.toString();
            labOrder.clinicID = clinicID.toInt();
            labOrder.doctorID = doctorID;
            labOrder.clinicDescription = clinicName;
            labOrder.doctorName = doctorName;
            labOrder.projectName = projectName;
            labOrder.orderDate = appointmentDate;

            currentlySelectedPatientOrder = labOrder;

            getPatientLabResultByHospital(labOrder);
            getPatientSpecialResult(labOrder);

            if (onSuccess != null) {
              onSuccess(apiResponse);
            }
            navigationService.push(
              CustomPageRoute(
                page: LabResultByClinic(labOrder: labOrder),
              ),
            );
          } else {}
          notifyListeners();
        }
      },
    );
  }

  Future<void> getPatientLabResultByHospital(PatientLabOrdersResponseModel laborder) async {
    isLabResultByHospitalLoading = true;
    notifyListeners();
    mainLabResultsByHospitalsList.clear();
    mainLabResultsByPackageName.clear();

    final result = await labRepo.getPatientLabResultsByHospitals(laborder, Utils.isVidaPlusProject(int.parse(laborder.projectID ?? "0")));

    result.fold(
      (failure) async {
        isLabResultByHospitalLoading = false;
        notifyListeners();
        // await errorHandlerService.handleError(failure: failure);
      },
      (apiResponse) {
        isLabResultByHospitalLoading = false;
        if (apiResponse.messageStatus == 2) {
        } else if (apiResponse.messageStatus == 1) {
          mainLabResultsByHospitalsList = apiResponse.data ?? [];
          // Group the results by package name for efficient lookups
          mainLabResultsByPackageName = labRepo.groupLabResultsByPackageName(mainLabResultsByHospitalsList);
          notifyListeners();
        }
      },
    );
  }

  Future<void> getPatientLabResult(PatientLabOrdersResponseModel laborder, String procedureName, String testDescription, String unitOfMeasure) async {
    LoaderBottomSheet.showLoader();
    mainLabResults.clear();
    filteredGraphValues.clear();
    maxY = double.negativeInfinity;
    minY = double.infinity;
    maxX = double.infinity;
    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 sortedResult = sort(apiResponse.data!);
          var recentThree = sortedResult.take(3).toList();
          mainLabResults = sortedResult;

          double highRefrenceValue = double.negativeInfinity;
          String? flagForHighReferenceRange;
          double lowRefenceValue = double.infinity;
          String? flagForLowReferenceRange;

          sortedResult.toList().reversed.forEach((element) {
            try {
              shouldShowGraph = isNumeric(element.resultValue!);
              // if(isNumeric(element.resultValue!)){
              var dateTime = DateUtil.convertStringToDate(element.verifiedOnDateTime!);
              var resultValue = double.parse(element.resultValue!);
              // var transformedValue = transformValueInRange(double.parse(element.resultValue!), element.calculatedResultFlag ?? "");
              var transformedValue = resultValue;
              if (resultValue > maxY) {
                maxY = resultValue;
                maxX = maxY;
              }
              if (resultValue < minY) {
                minY = resultValue;
              }
              // if (highRefrenceValue < double.parse(element.referenceHigh ?? "0.0")) {
              highRefrenceValue = double.parse(element.referenceHigh ?? "0.0");
              flagForHighReferenceRange = element.calculatedResultFlag;
              // }
              // if (lowRefenceValue > double.parse(element.referenceLow ?? "0.0")) {
              lowRefenceValue = double.parse(element.referenceLow ?? "0.0");
              flagForLowReferenceRange = element.calculatedResultFlag;
              // }

              if (double.parse(element.referenceHigh ?? "0.0") == 0) {
                highRefrenceValue = double.negativeInfinity;
              }

              if (double.parse(element.referenceLow ?? "0.0") == 0) {
                lowRefenceValue = double.negativeInfinity;
              }

              filteredGraphValues.add(DataPoint(
                value: transformedValue,
                actualValue: element.resultValue!,
                label: formatDateAsMMYY(dateTime),
                unitOfMeasurement: element.uOM,
                displayTime: resultDate(dateTime),
                time: DateUtil.convertStringToDate(element.verifiedOnDateTime),
                referenceValue: element.calculatedResultFlag ?? "",
              ));
            } catch (e) {}
          });
          if (flagForLowReferenceRange == null && flagForHighReferenceRange == null) {
            highRefrenceValue = maxY;
            lowRefenceValue = minY;
          }
          //
          if (lowRefenceValue != double.negativeInfinity && minY > lowRefenceValue) {
            minY = lowRefenceValue - getInterval();
          } else if (highRefrenceValue != double.negativeInfinity && minY > highRefrenceValue) {
            minY = highRefrenceValue - getInterval();
          }

          this.flagForHighReferenceRange = flagForHighReferenceRange;
          this.flagForLowReferenceRange = flagForLowReferenceRange;
          highTransformedReferenceValue = double.parse(transformValueInRange(highRefrenceValue, flagForHighReferenceRange ?? "").toStringAsFixed(1));
          lowTransformedReferenceValue = double.parse(transformValueInRange(lowRefenceValue, flagForLowReferenceRange ?? "").toStringAsFixed(1));
          this.highRefrenceValue = double.parse(highRefrenceValue.toStringAsFixed(2));
          this.lowRefenceValue = double.parse(lowRefenceValue.toStringAsFixed(2));
          if (highRefrenceValue != double.negativeInfinity && maxY < highRefrenceValue) {
            maxY = highRefrenceValue + getInterval();

            maxX = maxY;
          } else if (lowRefenceValue != double.negativeInfinity && maxY < lowRefenceValue) {
            maxY = lowRefenceValue + getInterval() / 2;

            maxX = maxY;
          }

          // if (maxY < highRefrenceValue) {
          //   minY = highRefrenceValue - getInterval();
          // }
          // maxY += 25;
          // minY -= 25;
          LabResult recentResult = recentThree.first;
          recentResult.uOM = unitOfMeasure;
          checkIfGraphShouldBeDisplayed(recentResult);
          recentResult.verifiedOn = resultDate(DateUtil.convertStringToDate(recentResult.verifiedOnDateTime!));
          // filteredGraphValues = [filteredGraphValues.first];
          labOrderResponseByAi = null;
          navigationService.push(
            MaterialPageRoute(
              builder: (_) => LabResultDetails(recentLabResult: recentResult, testDescription: testDescription),
            ),
          );
          notifyListeners();
        }
      },
    );
  }

  bool isNumeric(String s) {
    return num.tryParse(s) != null;
  }

  num getInterval() {
    // return .1;
    var maxX = maxY;
    if (maxX < 1) return .2;
    if (maxX >= 1.0 && maxX < 5.0) return .3;
    if (maxX >= 5.0 && maxX < 10.0) return 1.5;
    if (maxX >= 10.0 && maxX < 50.0) return 2.5;
    if (maxX >= 50.0 && maxX < 100.0) return 5;
    if (maxX >= 100.0 && maxX < 200.0) return 10;
    return 15;
  }

  void checkIfGraphShouldBeDisplayed(LabResult recentResult) {
    shouldShowGraph = recentResult.checkIfGraphShouldBeDisplayed();
    isGraphVisible = shouldShowGraph;
    notifyListeners();
  }

  Future<void> getPatientSpecialResult(PatientLabOrdersResponseModel laborder) async {
    isSpecialResultsLoading = true;
    labSpecialResult = "";
    notifyListeners();
    final result = await labRepo.getSpecialLabResult(
      laborder,
      Utils.isVidaPlusProject(int.parse(laborder.projectID ?? "0")),
    );

    result.fold(
      (failure) async {
        isSpecialResultsLoading = false;
        notifyListeners();
        // await errorHandlerService.handleError(failure: failure);
      },
      (apiResponse) {
        isSpecialResultsLoading = false;
        if (apiResponse.messageStatus == 2) {
        } else if (apiResponse.messageStatus == 1) {
          StringBuffer htmlbuffer = StringBuffer("");

          apiResponse.data?.forEach((element) {
            if (element.resultDataHTML != null && element.resultDataHTML?.isNotEmpty == true) htmlbuffer.write("${element.resultDataHTML} <br/> <br/>");
          });

          labSpecialResult = htmlbuffer.toString();

          notifyListeners();
        }
        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:
        rangeStart = 40.0;
        rangeEnd = 59.0;
        break;
      // 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),
            unitOfMeasurement: element.uOM,
            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 ?? "");
          var transformedValue = double.parse(element.resultValue!);
          if (resultValue > maxY) {
            maxY = resultValue;
          }
          if (start != null && end == null) {
            if (dateTime.isAtSameMomentAs(start)) {
              filteredGraphValues.add(DataPoint(
                  value: transformedValue,
                  actualValue: element.resultValue!,
                  unitOfMeasurement: element.uOM,
                  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),
                  unitOfMeasurement: element.uOM,
                  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<LabResult> sortByFlagAndValue(List<LabResult> 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<LabResult>.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<LabResult> sort(List<LabResult> original) {
    final copy = List<LabResult>.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<DataPoint> sortFilteredList(List<DataPoint> original) {
    final copy = List<DataPoint>.from(original);
    copy.sort((a, b) {
      final aDate = a.time;
      final bDate = b.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;
    }
  }

  String getSeverityText(String refernceValue) {
    switch (refernceValue) {
      case 'N':
        return "normal";
      case 'L':
      case 'H':
        return "monitor";
      case 'CL':
      case 'LCL':
      case 'CH':
      case 'HCH':
        return "attention";
      default:
        return "normal";
    }
  }

  alterGraphVisibility() {
    isGraphVisible = !isGraphVisible;
    notifyListeners();
  }

  // /// Finds a matching LabResult from the grouped map by package name
  // /// Searches through all packages to find a result matching the test code or description
  // LabResult? _findMatchingLabResult(TestDetails testDetail) {
  //   // Search through all packages in the grouped map
  //   for (var packageResults in mainLabResultsByPackageName.values) {
  //     try {
  //       var matchingResult = packageResults.firstWhere(
  //         (labResult) => labResult.testCode == testDetail.testCode || labResult.description == testDetail.description,
  //       );
  //       return matchingResult;
  //     } catch (e) {
  //       // Continue searching in other packages
  //       continue;
  //     }
  //   }
  //   // Return null if no match found
  //   return LabResult();
  // }

  Future<void> getAiOverviewLabOrders({required int langId, required PatientLabOrdersResponseModel labOrder, required String loadingText}) async {
    isLabAIAnalysisNeedsToBeShown = true;
    notifyListeners();

    LoaderBottomSheet.showLoader(
        loadingText: loadingText,
        showCloseButton: true,
        onCloseTap: () {
          setIsLabAIAnalysisNeedsToBeShown(false);
        });
    List<Map<String, dynamic>> results = [];
    Map<String, dynamic> orderData = {"order_date": labOrder.orderDate ?? "", "clinic": labOrder.clinicDescription ?? "", "doctor": labOrder.doctorName ?? "", "results": []};
    List<Map<String, dynamic>> testResults = [];
    if (labOrder.testDetails != null && labOrder.testDetails!.isNotEmpty) {
      for (var testDetail in labOrder.testDetails!) {
        LabResult? matchingResult =
            mainLabResultsByHospitalsList.firstWhere((labResult) => labResult.testCode == testDetail.testCode || labResult.description == testDetail.description, orElse: () => LabResult());
        testResults.add({
          "test_name": testDetail.description ?? "",
          "result_value": matchingResult.resultValue ?? "",
          "reference_range": matchingResult.referanceRange ?? "",
          "unit": matchingResult.uOM ?? "",
          "flag": matchingResult.calculatedResultFlag ?? "N"
        });
      }
    }
    orderData["results"] = testResults;
    results.add(orderData);

    final result = await labRepo.getPatientAiOverViewLabOrders(payload: results, langId: langId);
    result.fold(
      (failure) async {
        LoaderBottomSheet.hideLoader();
        if (isLabAIAnalysisNeedsToBeShown) {
          await errorHandlerService.handleError(failure: failure);
        }
      },
      (apiResponse) {
        LoaderBottomSheet.hideLoader();
        if (apiResponse.messageStatus == 2) {
        } else if (apiResponse.messageStatus == 1) {
          if (isLabAIAnalysisNeedsToBeShown) {
            labOrdersResponseByAi = apiResponse.data;
            navigationService.push(
              CustomPageRoute(
                direction: AxisDirection.down,
                page: LabAiAnalysisDetailedPage(),
              ),
            );
          }
        }
      },
    );

    // LoadingUtils.hideFullScreenLoader();
  }

  Future<void> getAiOverviewSingleLabResult({required int langId, required LabResult recentLabResult, required String loadingText}) async {
    LoaderBottomSheet.showLoader(
        loadingText: loadingText,
        showCloseButton: true,
        onCloseTap: () {
          setIsLabAIAnalysisNeedsToBeShown(false);
        });
    List<Map<String, dynamic>> results = [];
    results.add({
      "Description": recentLabResult.description ?? '',
      "ResultValue": recentLabResult.resultValue ?? '',
      "CalculatedResultFlag": recentLabResult.calculatedResultFlag ?? '',
      "UOM": recentLabResult.uOM ?? '',
      "ReferanceRange": recentLabResult.referanceRange ?? '',
    });

    // var payload = {"patient_id": currentlySelectedPatientOrder!.patientID, "language_id": langId, "lab_results": results};
    var payload = {"patient_id": getIt.get<AppState>().getAuthenticatedUser()!.patientId, "language_id": langId, "lab_results": results};
    final result = await labRepo.getPatientAiOverViewLabOrder(payload);
    result.fold(
      (failure) async {
        LoaderBottomSheet.hideLoader();
        if (isLabAIAnalysisNeedsToBeShown) {
          await errorHandlerService.handleError(failure: failure);
        }
      },
      (apiResponse) {
        LoaderBottomSheet.hideLoader();
        if (apiResponse.messageStatus == 2) {
        } else if (apiResponse.messageStatus == 1) {
          if (isLabAIAnalysisNeedsToBeShown) {
            labOrderResponseByAi = apiResponse.data;
            notifyListeners();
          }
        }
      },
    );

    LoaderBottomSheet.hideLoader();
  }
}
