import 'package:hmg_patient_app_new/core/api/api_client.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
import 'package:dartz/dartz.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/utils.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/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/features/lab/models/resp_models/patient_lab_special_result.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';

import 'models/resp_models/lab_result.dart' show LabResult;

abstract class LabRepo {
  Future<Either<Failure, GenericApiModel<List<PatientLabOrdersResponseModel>>>> getPatientLabOrders();

  Future<Either<Failure, GenericApiModel<List<LabResult>>>> getPatientLabResults(PatientLabOrdersResponseModel laborder, bool isVidaPlus, String procedureName);

  Future<Either<Failure, GenericApiModel<List<LabResult>>>> getPatientLabResultsByHospitals(PatientLabOrdersResponseModel laborder, bool isVidaPlus);

  Future<Either<Failure, GenericApiModel<List<PatientLabSpecialResult>>>> getSpecialLabResult(PatientLabOrdersResponseModel laborder, bool isVidaPlus);

  Future<Either<Failure, GenericApiModel<String>>> getLabResultReportPDF({required PatientLabOrdersResponseModel labOrder});

  Future<Either<Failure, GenericApiModel<dynamic>>> getLabResultsByAppointmentNo({required num appointmentNo, required num projectID, required num clinicID});

  Future<Either<Failure, GenericApiModel<LabOrdersResponseByAi>>> getPatientAiOverViewLabOrders({required int langId, required dynamic payload});

  Future<Either<Failure, GenericApiModel<LabOrderResponseByAi>>> getPatientAiOverViewLabOrder(var payloa);
  Map<String, List<LabResult>> groupLabResultsByPackageName(List<LabResult> labResults);
}

class LabRepoImp implements LabRepo {
  final ApiClient apiClient;
  final LoggerService loggerService;

  LabRepoImp({required this.loggerService, required this.apiClient});

  /// Groups a list of LabResult objects by their PackageName
  /// Returns a Map where the key is the PackageName and the value is a List of LabResult objects
  /// Both the keys and the lists are sorted alphabetically
  /// 
  /// Example usage:
  /// ```dart
  /// List<LabResult> labResults = [/* your lab results */];
  /// Map<String, List<LabResult>> groupedResults = labRepo.groupLabResultsByPackageName(labResults);
  /// 
  /// // Access results by package name
  /// groupedResults.forEach((packageName, results) {
  ///   print('Package: $packageName has ${results.length} results');
  /// });
  /// ```
  Map<String, List<LabResult>> groupLabResultsByPackageName(List<LabResult> labResults) {
    Map<String, List<LabResult>> groupedResults = {};
    
    // Group results by package name
    for (var labResult in labResults) {
      String packageName = labResult.PackageName ?? 'Unknown Package';
      
      if (groupedResults.containsKey(packageName)) {
        // Package name exists, add to existing list
        groupedResults[packageName]!.add(labResult);
      } else {
        // Package name doesn't exist, create new key with new list
        groupedResults[packageName] = [labResult];
      }
    }
    
    // Sort each list alphabetically by description
    groupedResults.forEach((key, list) {
      list.sort((a, b) {
        String descA = (a.description ?? '').toLowerCase();
        String descB = (b.description ?? '').toLowerCase();
        return descA.compareTo(descB);
      });
    });
    
    // Sort the map keys alphabetically and return a new sorted map
    var sortedKeys = groupedResults.keys.toList()..sort((a, b) => a.toLowerCase().compareTo(b.toLowerCase()));
    Map<String, List<LabResult>> sortedGroupedResults = {};
    for (var key in sortedKeys) {
      sortedGroupedResults[key] = groupedResults[key]!;
    }
    
    return sortedGroupedResults;
  }

  @override
  Future<Either<Failure, GenericApiModel<List<PatientLabOrdersResponseModel>>>> getPatientLabOrders() async {
    Map<String, dynamic> mapDevice = {};
    try {
      GenericApiModel<List<PatientLabOrdersResponseModel>>? apiResponse;
      Failure? failure;
      await apiClient.post(
        GET_Patient_LAB_ORDERS,
        body: mapDevice,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            final list = response['ListPLO'];
            if (list == null || list.isEmpty) {
              throw Exception("lab list is empty");
            }

            final labOrders = list.map((item) => PatientLabOrdersResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<PatientLabOrdersResponseModel>();

            apiResponse = GenericApiModel<List<PatientLabOrdersResponseModel>>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: null,
              data: labOrders,
            );
          } catch (e) {
            failure = DataParsingFailure(e.toString());
          }
        },
      );
      if (failure != null) return Left(failure!);
      if (apiResponse == null) return Left(ServerFailure("Unknown error"));
      return Right(apiResponse!);
    } catch (e) {
      return Left(UnknownFailure(e.toString()));
    }
  }

  @override
  Future<Either<Failure, GenericApiModel<List<LabResult>>>> getPatientLabResults(PatientLabOrdersResponseModel laborder, bool isVidaPlus, String procedureName) async {
    Map<String, dynamic> request = Map();
    request['InvoiceNo_VP'] = isVidaPlus ? laborder!.invoiceNo : "0";
    request['InvoiceNo'] = isVidaPlus ? "0" : laborder!.invoiceNo;
    request['OrderNo'] = laborder!.orderNo;
    request['isDentalAllowedBackend'] = false;
    request['SetupID'] = laborder!.setupID;
    request['ProjectID'] = laborder.projectID;
    request['ClinicID'] = laborder.clinicID;
    request['Procedure'] = procedureName;
    try {
      GenericApiModel<List<LabResult>>? apiResponse;
      Failure? failure;
      await apiClient.post(
        GET_Patient_LAB_ORDERS_RESULT,
        body: request,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            final list = response['ListPLR'];
            if (list == null || list.isEmpty) {
              throw Exception("lab list is empty");
            }

            final labOrders = list.map((item) => LabResult.fromJson(item as Map<String, dynamic>)).toList().cast<LabResult>();

            apiResponse = GenericApiModel<List<LabResult>>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: null,
              data: labOrders,
            );
          } catch (e) {
            failure = DataParsingFailure(e.toString());
          }
        },
      );
      if (failure != null) return Left(failure!);
      if (apiResponse == null) return Left(ServerFailure("Unknown error"));
      return Right(apiResponse!);
    } catch (e) {
      return Left(UnknownFailure(e.toString()));
    }
  }

  @override
  Future<Either<Failure, GenericApiModel<List<LabResult>>>> getPatientLabResultsByHospitals(PatientLabOrdersResponseModel laborder, bool isVidaPlus) async {
    Map<String, dynamic> request = Map();
    request['InvoiceNo_VP'] = isVidaPlus ? laborder!.invoiceNo : "0";
    request['InvoiceNo'] = isVidaPlus ? "0" : laborder!.invoiceNo;
    request['OrderNo'] = laborder!.orderNo;
    request['isDentalAllowedBackend'] = false;
    request['SetupID'] = laborder!.setupID;
    request['ProjectID'] = laborder.projectID;
    request['ClinicID'] = laborder.clinicID;
    request['InvoiceType'] = laborder.invoiceType ?? "";
    try {
      GenericApiModel<List<LabResult>>? apiResponse;
      Failure? failure;
      await apiClient.post(
        GET_Patient_LAB_RESULT,
        body: request,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            final list = response['ListPLR'];
            if (list == null || list.isEmpty) {
              throw Exception("lab list is empty");
            }

            final labOrders = list.map((item) => LabResult.fromJson(item as Map<String, dynamic>)).toList().cast<LabResult>();

            apiResponse = GenericApiModel<List<LabResult>>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: null,
              data: labOrders,
            );
          } catch (e) {
            failure = DataParsingFailure(e.toString());
          }
        },
      );
      if (failure != null) return Left(failure!);
      if (apiResponse == null) return Left(ServerFailure("Unknown error"));
      return Right(apiResponse!);
    } catch (e) {
      return Left(UnknownFailure(e.toString()));
    }
  }

  @override
  Future<Either<Failure, GenericApiModel<List<PatientLabSpecialResult>>>> getSpecialLabResult(PatientLabOrdersResponseModel laborder, bool isVidaPlus) async {
    Map<String, dynamic> request = Map();
    request['InvoiceNo_VP'] = isVidaPlus ? laborder!.invoiceNo : "0";
    request['InvoiceNo'] = isVidaPlus ? "0" : laborder!.invoiceNo;
    request['OrderNo'] = laborder!.orderNo;
    request['isDentalAllowedBackend'] = false;
    request['SetupID'] = laborder!.setupID;
    request['ProjectID'] = laborder.projectID;
    request['ClinicID'] = laborder.clinicID;
    request['InvoiceType'] = laborder.invoiceType ?? "";
    try {
      GenericApiModel<List<PatientLabSpecialResult>>? apiResponse;
      Failure? failure;
      await apiClient.post(
        GET_Patient_LAB_SPECIAL_RESULT,
        body: request,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            final list = response['ListPLSR'];
            if (list == null || list.isEmpty) {
              throw Exception("lab list is empty");
            }

            final labOrders = list.map((item) => PatientLabSpecialResult.fromJson(item as Map<String, dynamic>)).toList().cast<PatientLabSpecialResult>();

            apiResponse = GenericApiModel<List<PatientLabSpecialResult>>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: null,
              data: labOrders,
            );
          } catch (e) {
            failure = DataParsingFailure(e.toString());
          }
        },
      );
      if (failure != null) return Left(failure!);
      if (apiResponse == null) return Left(ServerFailure("Unknown error"));
      return Right(apiResponse!);
    } catch (e) {
      return Left(UnknownFailure(e.toString()));
    }
  }

  @override
  Future<Either<Failure, GenericApiModel<String>>> getLabResultReportPDF({required PatientLabOrdersResponseModel labOrder}) async {
    Map<String, dynamic> mapDevice = {
      "InvoiceNo": Utils.isVidaPlusProject(int.parse(labOrder.projectID!)) ? "0" : labOrder.invoiceNo,
      "InvoiceNo_VP": Utils.isVidaPlusProject(int.parse(labOrder.projectID!)) ? labOrder.invoiceNo : "0",
      // "LineItemNo": labOrder.invoiceLineItemNo,
      // "InvoiceLineItemNo": labOrder.invoiceLineItemNo,
      "ProjectID": labOrder.projectID!,
      "DoctorID": labOrder.doctorID!,
      "OrderNo": labOrder.orderNo!,
      "InvoiceType": labOrder.invoiceType!,
      "SetupID": labOrder.setupID ?? "",
      "IsDownload": true,
      'ClinicName': labOrder.clinicDescription,
      'DateofBirth': Utils.appState.getAuthenticatedUser()!.dateofBirth,
      'DoctorName': labOrder.doctorName,
      'OrderDate': '${DateUtil.convertStringToDate(labOrder.orderDate!).year}-${DateUtil.convertStringToDate(labOrder.orderDate!).month}-${DateUtil.convertStringToDate(labOrder.orderDate!).day}',
      'PatientIditificationNum': Utils.appState.getAuthenticatedUser()!.patientIdentificationNo,
      'PatientMobileNumber': Utils.appState.getAuthenticatedUser()!.mobileNumber,
      'PatientName': "${Utils.appState.getAuthenticatedUser()!.firstName!} ${Utils.appState.getAuthenticatedUser()!.lastName!}",
      'ProjectName': labOrder.projectName,
      "To": Utils.appState.getAuthenticatedUser()!.emailAddress
    };

    try {
      GenericApiModel<String>? apiResponse;
      Failure? failure;
      await apiClient.post(
        // Utils.isVidaPlusProject(int.parse(labOrder.projectID!)) ? SEND_LAB_RESULT_EMAIL : SEND_LAB_RESULT_EMAIL_NEW,
        SEND_LAB_RESULT_EMAIL_NEW,
        body: mapDevice,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            apiResponse = GenericApiModel<String>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: null,
              data: response["PdfContent"],
            );
          } catch (e) {
            failure = DataParsingFailure(e.toString());
          }
        },
      );
      if (failure != null) return Left(failure!);
      if (apiResponse == null) return Left(ServerFailure("Unknown error"));
      return Right(apiResponse!);
    } catch (e) {
      return Left(UnknownFailure(e.toString()));
    }
  }

  @override
  Future<Either<Failure, GenericApiModel>> getLabResultsByAppointmentNo({required num appointmentNo, required num projectID, required num clinicID}) async {
    Map<String, dynamic> request = {};
    request['AppointmentNo'] = appointmentNo;
    request['ProjectID'] = projectID;
    request['ClinicID'] = clinicID;

    try {
      GenericApiModel<dynamic>? apiResponse;
      Failure? failure;
      await apiClient.post(
        GET_PATIENT_LAB_ORDERS_BY_APPOINTMENT,
        body: request,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            apiResponse = GenericApiModel<dynamic>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: null,
              data: response['ListLabResultsByAppNo'],
            );
          } catch (e) {
            failure = DataParsingFailure(e.toString());
          }
        },
      );
      if (failure != null) return Left(failure!);
      if (apiResponse == null) return Left(ServerFailure("Unknown error"));
      return Right(apiResponse!);
    } catch (e) {
      return Left(UnknownFailure(e.toString()));
    }
  }

  @override
  Future<Either<Failure, GenericApiModel<LabOrderResponseByAi>>> getPatientAiOverViewLabOrder(var labOrder) async {
    try {
      GenericApiModel<LabOrderResponseByAi>? apiResponse;
      Failure? failure;
      await apiClient.post(
        ApiConsts.getAiOverViewLabOrderNew,
        body: labOrder,
        isAllowAny: true,
        isExternal: true,
        apiHeaders: {
          'Accept-Language': 'en-US,en;q=0.7',
          'Content-Type': 'application/json',
          'X-API-KEY': '9d9d2ac4c1fc302299dbb0fbb1b7fea1',
          'accept': 'application/json',
        },
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            var res;
            if (response != null) {
              res = response;
            }
            apiResponse = GenericApiModel<LabOrderResponseByAi>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: null,
              data: LabOrderResponseByAi.fromJson(res),
            );
          } catch (e) {
            failure = DataParsingFailure(e.toString());
          }
        },
      );
      if (failure != null) return Left(failure!);
      if (apiResponse == null) return Left(ServerFailure("Unknown error"));
      return Right(apiResponse!);
    } catch (e) {
      return Left(UnknownFailure(e.toString()));
    }
  }

  @override
  Future<Either<Failure, GenericApiModel<LabOrdersResponseByAi>>> getPatientAiOverViewLabOrders({required int langId, required dynamic payload}) async {
    try {
      GenericApiModel<LabOrdersResponseByAi>? apiResponse;
      Failure? failure;
      await apiClient.post(
        ApiConsts.getAiOverViewLabOrdersNew,
        isAllowAny: true,
        isExternal: true,
        apiHeaders: {
          'Accept-Language': 'en-US,en;q=0.7',
          'Content-Type': 'application/json',
          'X-API-KEY': '9d9d2ac4c1fc302299dbb0fbb1b7fea1',
          'accept': 'application/json',
        },
        body: {"lab_results": payload, "language_id": langId},
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            var res;
            if (response != null) {
              res = response;
            }
            apiResponse = GenericApiModel<LabOrdersResponseByAi>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: null,
              data: LabOrdersResponseByAi.fromJson(res),
            );
          } catch (e) {
            failure = DataParsingFailure(e.toString());
          }
        },
      );
      if (failure != null) return Left(failure!);
      if (apiResponse == null) return Left(ServerFailure("Unknown error"));
      return Right(apiResponse!);
    } catch (e) {
      return Left(UnknownFailure(e.toString()));
    }
  }
}
