import 'package:dartz/dartz.dart';
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/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
import 'package:hmg_patient_app_new/features/todo_section/models/resp_models/ancillary_order_list_response_model.dart';
import 'package:hmg_patient_app_new/features/todo_section/models/resp_models/ancillary_order_procedures_detail_response_model.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';

abstract class TodoSectionRepo {
  Future<Either<Failure, GenericApiModel<List<AncillaryOrderItem>>>> getOnlineAncillaryOrderList();

  Future<Either<Failure, GenericApiModel<List<AncillaryOrderProcedureItem>>>> getOnlineAncillaryOrderDetailsProceduresList({
    required int appointmentNoVida,
    required int orderNo,
    required int projectID,
  });

  Future<Either<Failure, dynamic>> checkPaymentStatus({required String transID});

  Future<Either<Failure, dynamic>> createAdvancePayment({
    required int projectID,
    required num paymentAmount,
    required String paymentReference,
    required String paymentMethodName,
    required int patientTypeID,
    required String patientName,
    required int patientID,
    required String setupID,
    required bool isAncillaryOrder,
  });

  Future<Either<Failure, dynamic>> addAdvancedNumberRequest({
    required String advanceNumber,
    required String paymentReference,
    required int appointmentID,
    required int patientID,
    required int patientTypeID,
    required int patientOutSA,
  });

  Future<Either<Failure, dynamic>> autoGenerateAncillaryOrdersInvoice({
    required int orderNo,
    required int projectID,
    required int appointmentNo,
    required List<dynamic> selectedProcedures,
    required int languageID,
  });

  Future<Either<Failure, dynamic>> applePayInsertRequest({required dynamic applePayInsertRequest});

  Future<Either<Failure, dynamic>> getPatientDashboard();
}

class TodoSectionRepoImp implements TodoSectionRepo {
  final ApiClient apiClient;
  final LoggerService loggerService;

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

  @override
  Future<Either<Failure, GenericApiModel<List<AncillaryOrderItem>>>> getOnlineAncillaryOrderList() async {
    Map<String, dynamic> mapDevice = {};

    try {
      GenericApiModel<List<AncillaryOrderItem>>? apiResponse;
      Failure? failure;
      await apiClient.post(
        ApiConsts.getOnlineAncillaryOrderList,
        body: mapDevice,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            List<AncillaryOrderItem> ancillaryOrders = [];

            // Parse the nested structure
            if (response['AncillaryOrderList'] != null && response['AncillaryOrderList'] is List) {
              final groupsList = response['AncillaryOrderList'] as List;

              // Iterate through each group
              for (var group in groupsList) {
                if (group is Map<String, dynamic> && group['AncillaryOrderList'] != null) {
                  final ordersList = group['AncillaryOrderList'] as List;
                  final projectName = group['ProjectName'] as String?;
                  final projectID = group['ProjectID'] as int?;
                  final setupID = group['SetupID'] as String?;


                  // Parse each order item in the group
                  for (var orderJson in ordersList) {
                    if (orderJson is Map<String, dynamic>) {
                      ancillaryOrders.add(AncillaryOrderItem.fromJson(orderJson, projectName: projectName, projectID: projectID, setupID: setupID));
                    }
                  }
                }
              }
            }

            ancillaryOrders.sort((a, b) => b.orderDate!.compareTo(a.orderDate!));

            apiResponse = GenericApiModel<List<AncillaryOrderItem>>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: errorMessage,
              data: ancillaryOrders,
            );
          } catch (e) {
            loggerService.logInfo("Error parsing ancillary orders: ${e.toString()}");
            failure = DataParsingFailure(e.toString());
          }
        },
      );
      if (failure != null) return Left(failure!);
      if (apiResponse == null) return Left(ServerFailure("Unknown error"));
      return Right(apiResponse!);
    } catch (e) {
      loggerService.logError("Unknown error in getOnlineAncillaryOrderList: ${e.toString()}");
      return Left(UnknownFailure(e.toString()));
    }
  }

  @override
  Future<Either<Failure, GenericApiModel<List<AncillaryOrderProcedureItem>>>> getOnlineAncillaryOrderDetailsProceduresList({
    required int appointmentNoVida,
    required int orderNo,
    required int projectID,
  }) async {
    Map<String, dynamic> mapDevice = {
      'AppointmentNo_Vida': appointmentNoVida,
      'OrderNo': orderNo,
      'ProjectID': projectID,
    };

    try {
      GenericApiModel<List<AncillaryOrderProcedureItem>>? apiResponse;
      Failure? failure;
      await apiClient.post(
        ApiConsts.getOnlineAncillaryOrderProcList,
        body: mapDevice,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          try {
            List<AncillaryOrderProcedureItem> ancillaryOrdersProcedures = [];

            // Parse the flat array structure (NOT nested like AncillaryOrderList)
            if (response['AncillaryOrderProcList'] != null && response['AncillaryOrderProcList'] is List) {
              final procList = response['AncillaryOrderProcList'] as List;

              // Parse each procedure item directly
              for (var procJson in procList) {
                if (procJson is Map<String, dynamic>) {
                  ancillaryOrdersProcedures.add(AncillaryOrderProcedureItem.fromJson(procJson));
                }
              }
            }

            apiResponse = GenericApiModel<List<AncillaryOrderProcedureItem>>(
              messageStatus: messageStatus,
              statusCode: statusCode,
              errorMessage: errorMessage,
              data: ancillaryOrdersProcedures,
            );
          } catch (e) {
            loggerService.logError("Error parsing ancillary Procedures: ${e.toString()}");
            failure = DataParsingFailure(e.toString());
          }
        },
      );
      if (failure != null) return Left(failure!);
      if (apiResponse == null) return Left(ServerFailure("Unknown error"));
      return Right(apiResponse!);
    } catch (e) {
      loggerService.logError("Unknown error in getOnlineAncillaryOrderDetailsProceduresList: ${e.toString()}");
      return Left(UnknownFailure(e.toString()));
    }
  }

  @override
  Future<Either<Failure, dynamic>> checkPaymentStatus({required String transID}) async {
    Map<String, dynamic> mapDevice = {'ClientRequestID': transID};

    try {
      dynamic apiResponse;
      Failure? failure;
      await apiClient.post(
        ApiConsts.getRequestStatusByRequestID,
        body: mapDevice,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          apiResponse = response;
        },
      );
      if (failure != null) return Left(failure!);
      return Right(apiResponse);
    } catch (e) {
      loggerService.logError("Unknown error in checkPaymentStatus: ${e.toString()}");
      return Left(UnknownFailure(e.toString()));
    }
  }

  @override
  Future<Either<Failure, dynamic>> createAdvancePayment({
    required int projectID,
    required num paymentAmount,
    required String paymentReference,
    required String paymentMethodName,
    required int patientTypeID,
    required String patientName,
    required int patientID,
    required String setupID,
    required bool isAncillaryOrder,
  }) async {
    //    //VersionID (number)
    //     // Channel (number)
    //     // IPAdress (string)
    //     // generalid (string)
    //     // LanguageID (number)
    //     // Latitude (number)
    //     // Longitude (number)
    //     // DeviceTypeID (number)
    //     // PatientType (number)
    //     // PatientTypeID (number)
    //     // PatientID (number)
    //     // PatientOutSA (number)
    //     // TokenID (string)
    //     // SessionID (string)

    Map<String, dynamic> mapDevice = {
      'CustName': patientName,
      'CustID': patientID,
      'SetupID': setupID,
      'ProjectID': projectID,
      'AccountID': patientID,
      'PaymentAmount': paymentAmount,
      'NationalityID': null,
      'DepositorName': patientName,
      'CreatedBy': 3,
      'PaymentMethodName': paymentMethodName,
      'PaymentReference': paymentReference,
      'PaymentMethod': paymentMethodName,
      'IsAncillaryOrder': isAncillaryOrder,
    };

    try {
      dynamic apiResponse;
      Failure? failure;
      await apiClient.post(
        ApiConsts.createAdvancePayments,
        body: mapDevice,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          apiResponse = response;
        },
      );
      if (failure != null) return Left(failure!);
      return Right(apiResponse);
    } catch (e) {
      loggerService.logError("Unknown error in createAdvancePayment: ${e.toString()}");
      return Left(UnknownFailure(e.toString()));
    }
  }

  @override
  Future<Either<Failure, dynamic>> addAdvancedNumberRequest({
    required String advanceNumber,
    required String paymentReference,
    required int appointmentID,
    required int patientID,
    required int patientTypeID,
    required int patientOutSA,
  }) async {
    Map<String, dynamic> mapDevice = {
      'AdvanceNumber': advanceNumber,
      'PaymentReference': paymentReference,
      'AppointmentID': appointmentID,
      'PatientID': patientID,
      'PatientTypeID': patientTypeID,
      'PatientOutSA': patientOutSA,
    };

    try {
      dynamic apiResponse;
      Failure? failure;
      await apiClient.post(
        ApiConsts.addAdvanceNumberRequest,
        body: mapDevice,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          apiResponse = response;
        },
      );
      if (failure != null) return Left(failure!);
      return Right(apiResponse);
    } catch (e) {
      loggerService.logError("Unknown error in addAdvancedNumberRequest: ${e.toString()}");
      return Left(UnknownFailure(e.toString()));
    }
  }

  @override
  Future<Either<Failure, dynamic>> autoGenerateAncillaryOrdersInvoice({
    required int orderNo,
    required int projectID,
    required int appointmentNo,
    required List<dynamic> selectedProcedures,
    required int languageID,
  }) async {
    // Extract procedure IDs from selectedProcedures
    List<String> procedureOrderIDs = [];
    selectedProcedures.forEach((element) {
      procedureOrderIDs.add(element["ProcedureID"].toString());
    });

    Map<String, dynamic> mapDevice = {
      'LanguageID': languageID,
      'RequestAncillaryOrderInvoice': [
        {
          'MemberID': 102,
          'ProjectID': projectID,
          'AppointmentNo': appointmentNo,
          'OrderNo': orderNo,
          'AncillaryOrderInvoiceProcList': selectedProcedures,
        }
      ],
      'ProcedureOrderIds': procedureOrderIDs,
    };

    try {
      dynamic apiResponse;
      Failure? failure;
      await apiClient.post(
        ApiConsts.autoGenerateAncillaryOrdersInvoice,
        body: mapDevice,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          apiResponse = response;
        },
      );
      if (failure != null) return Left(failure!);
      return Right(apiResponse);
    } catch (e) {
      loggerService.logError("Unknown error in autoGenerateAncillaryOrdersInvoice: ${e.toString()}");
      return Left(UnknownFailure(e.toString()));
    }
  }

  @override
  Future<Either<Failure, dynamic>> applePayInsertRequest({required dynamic applePayInsertRequest}) async {
    Map<String, dynamic> mapDevice = {
      'ApplePayInsertRequest': applePayInsertRequest,
    };

    try {
      dynamic apiResponse;
      Failure? failure;
      await apiClient.post(
        ApiConsts.applePayInsertRequest,
        body: mapDevice,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          apiResponse = response;
        },
      );
      if (failure != null) return Left(failure!);
      return Right(apiResponse);
    } catch (e) {
      loggerService.logError("Unknown error in applePayInsertRequest: ${e.toString()}");
      return Left(UnknownFailure(e.toString()));
    }
  }

  @override
  Future<Either<Failure, dynamic>> getPatientDashboard() async {
    Map<String, dynamic> mapDevice = {};

    try {
      dynamic apiResponse;
      Failure? failure;
      await apiClient.post(
        DASHBOARD,
        body: mapDevice,
        onFailure: (error, statusCode, {messageStatus, failureType}) {
          failure = failureType;
        },
        onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
          apiResponse = response;
        },
      );
      if (failure != null) return Left(failure!);
      return Right(apiResponse);
    } catch (e) {
      loggerService.logError("Unknown error in applePayInsertRequest: ${e.toString()}");
      return Left(UnknownFailure(e.toString()));
    }
  }
}
