import 'dart:convert';
import 'dart:developer';
import 'dart:io';

import 'package:amazon_payfort/amazon_payfort.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_amazonpaymentservices/flutter_amazonpaymentservices.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/cache_consts.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/get_tamara_installments_details_response_model.dart';
import 'package:hmg_patient_app_new/features/payfort/models/payfort_check_payment_status_response_model.dart';
import 'package:hmg_patient_app_new/features/payfort/models/payfort_project_details_resp_model.dart';
import 'package:hmg_patient_app_new/features/payfort/models/payfort_request_insert_model.dart';
import 'package:hmg_patient_app_new/features/payfort/models/payfort_response_insert_model.dart';
import 'package:hmg_patient_app_new/features/payfort/models/sdk_token_response_model.dart';
import 'package:hmg_patient_app_new/features/payfort/payfort_repo.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:network_info_plus/network_info_plus.dart';
import 'package:crypto/crypto.dart';

import 'models/apple_pay_request_insert_model.dart';

class PayfortViewModel extends ChangeNotifier {
  PayfortRepo payfortRepo;
  ErrorHandlerService errorHandlerService;

  bool isApplePayConfigurationLoading = false;

  PayfortProjectDetailsRespModel? payfortProjectDetailsRespModel;
  PayfortCheckPaymentStatusResponseModel? payfortCheckPaymentStatusResponseModel;
  late AmazonPayfort _payfort;
  final NetworkInfo _info = NetworkInfo();

  GetTamaraInstallmentsDetailsResponseModel? getTamaraInstallmentsDetailsResponseModel;
  bool isTamaraDetailsLoading = false;

  SdkTokenResponse? sdkTokenResponse;

  PayfortViewModel({required this.payfortRepo, required this.errorHandlerService});

  setIsApplePayConfigurationLoading(bool value) {
    isApplePayConfigurationLoading = value;
    notifyListeners();
  }

  initPayfortViewModel() async {
    _payfort = AmazonPayfort.instance;
    await AmazonPayfort.initialize(
      PayFortOptions(environment: ApiConsts.payFortEnvironment),
    );
    notifyListeners();
  }

  Future<void> getPayfortConfigurations(
      {int? serviceId, int? projectId, int integrationId = 2, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    final result = await payfortRepo.getPayfortConfigurations(serviceId: serviceId, projectId: projectId, integrationId: integrationId);

    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) {
          payfortProjectDetailsRespModel = apiResponse.data!;
          // isApplePayConfigurationLoading = false;
          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );
  }

  Future<void> applePayRequestInsert(
      {required ApplePayInsertRequest applePayInsertRequest, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    final result = await payfortRepo.applePayRequestInsert(applePayInsertRequest: applePayInsertRequest);

    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) {
          // payfortProjectDetailsRespModel = apiResponse.data!;
          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );
  }

  Future<void> payfortRequestInsert({required PayfortRequestInsertModel payfortRequestInsertModel, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    final result = await payfortRepo.payfortRequestInsert(payfortRequestInsertModel: payfortRequestInsertModel);

    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) {
          // payfortProjectDetailsRespModel = apiResponse.data!;
          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );
  }

  Future<void> payfortResponseInsert({required PayfortResponseInsertModel payfortResponseInsertModel, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    final result = await payfortRepo.payfortResponseInsert(payfortResponseInsertModel: payfortResponseInsertModel);

    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) {
          // payfortProjectDetailsRespModel = apiResponse.data!;
          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );
  }

  Future<void> checkPaymentStatus({required String transactionID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    final result = await payfortRepo.checkPaymentStatus(transactionID: transactionID);

    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) {
          payfortCheckPaymentStatusResponseModel = apiResponse.data!;
          notifyListeners();
          if (onSuccess != null) {
            onSuccess(apiResponse);
          }
        }
      },
    );
  }

  Future<void> checkTamaraPaymentStatus({required String transactionID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    final result = await payfortRepo.checkTamaraPaymentStatus(transactionID: transactionID);

    result.fold(
      (failure) async {
        onError!(failure.message);
      },
      (apiResponse) {
        log(apiResponse.data.toString());
        if (onSuccess != null) {
          onSuccess(apiResponse);
        }
        // }
      },
    );
  }

  Future<void> updateTamaraRequestStatus(
      {required String responseMessage,
      required String status,
      required String clientRequestID,
      required String tamaraOrderID,
      Function(dynamic)? onSuccess,
      Function(String)? onError}) async {
    final result = await payfortRepo.updateTamaraRequestStatus(
        responseMessage: responseMessage, status: status, clientRequestID: clientRequestID, tamaraOrderID: tamaraOrderID);

    result.fold(
      (failure) async {
        onError!(failure.message);
      },
      (apiResponse) {
        log(apiResponse.data.toString());
        if (onSuccess != null) {
          onSuccess(apiResponse);
        }
      },
    );
  }

  String buildManualPayfortSignature({
    required String accessCode,
    required String deviceId,
    required String merchantIdentifier,
    required String amount,
    required String currency,
    required String merchantReference,
    required String shaRequestPhrase, // Your secret Sandbox phrase
  }) {
    // 1. Map all properties in strict ALPHABETICAL order by native key names
    // Notice that "amount" and "currency" must come first alphabetically
    final parameters = {
      "access_code": accessCode,
      "device_id": deviceId,
      "language": "en",
      "merchant_identifier": merchantIdentifier,
      "service_command": "SDK_TOKEN",
    };

    // 2. Concatenate into a single continuous string wrapped in your SHA Phrase
    StringBuffer buffer = StringBuffer();
    buffer.write(shaRequestPhrase);

    parameters.forEach((key, value) {
      buffer.write('$key=$value');
    });
    buffer.write(shaRequestPhrase);

    // 3. Convert string to SHA-256 hex signature
    var bytes = utf8.encode(buffer.toString());
    return sha256.convert(bytes).toString();
  }

  Future<SdkTokenResponse?> generateSdkResponseForNormalPayment({
    String? accessCode,
    String? merchantIdentifier,
    String? shaType,
    String? shaRequestPhrase,
    String? merchantReference,
    String? amount,
  }) async {
    SdkTokenResponse? sdkTokenResponse;
    try {
      // String? deviceId = await _payfort.getDeviceId();
      String? deviceId = await FlutterAmazonpaymentservices.getUDID;
      // String? deviceId = "79B8F1C1-F8BD-40A1-9FDD-C07B8AC44D17";

      /// Step 2:  Generate the Signature
      SdkTokenRequest tokenRequest = SdkTokenRequest(
        accessCode: accessCode!,
        deviceId: deviceId ?? '',
        merchantIdentifier: merchantIdentifier!,
      );

      // String? signature = await _payfort.generateSignature(
      //   shaType: shaType!,
      //   concatenatedString: tokenRequest.toConcatenatedString(shaRequestPhrase!),
      // );

      String? signature = buildManualPayfortSignature(
        accessCode: accessCode,
        deviceId: deviceId!,
        merchantIdentifier: merchantIdentifier,
        amount: "10000",
        currency: "SAR",
        merchantReference: merchantReference!,
        shaRequestPhrase: shaRequestPhrase!,
      );

      tokenRequest = tokenRequest.copyWith(signature: signature);

      this.sdkTokenResponse = SdkTokenResponse();

      /// Step 3: Generate the SDK Token
      final result = await payfortRepo.generateSdkSignatureFromAPI(tokenRequest: tokenRequest);
      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) {
            // payfortProjectDetailsRespModel = apiResponse.data!;
            sdkTokenResponse = apiResponse.data;
            this.sdkTokenResponse = sdkTokenResponse;
            notifyListeners();
          }
        },
      );
    } catch (e) {
      log("Error here: ${e.toString()}");
    }
    return sdkTokenResponse;
  }

  Future<SdkTokenResponse?> _generateSdkResponse({
    String? applePayAccessCode,
    String? merchantIdentifier,
    String? applePayShaType,
    String? applePayShaRequestPhrase,
  }) async {
    SdkTokenResponse? sdkTokenResponse;
    try {
      String? deviceId = await _payfort.getDeviceId();

      /// Step 2:  Generate the Signature
      SdkTokenRequest tokenRequest = SdkTokenRequest(
        accessCode: applePayAccessCode!,
        deviceId: deviceId ?? '',
        merchantIdentifier: merchantIdentifier!,
      );

      String? signature = await _payfort.generateSignature(
        shaType: applePayShaType!,
        concatenatedString: tokenRequest.toConcatenatedString(applePayShaRequestPhrase!),
      );

      tokenRequest = tokenRequest.copyWith(signature: signature);

      /// Step 3: Generate the SDK Token
      final result = await payfortRepo.generateSdkSignatureFromAPI(tokenRequest: tokenRequest);
      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) {
            // payfortProjectDetailsRespModel = apiResponse.data!;
            sdkTokenResponse = apiResponse.data;
            isApplePayConfigurationLoading = false;
            notifyListeners();
          }
        },
      );
    } catch (e) {
      log("Error here: ${e.toString()}");
    }
    return sdkTokenResponse;
  }

  Future<void> initiatePaymentWithCard( // var request, {
    String paymentOption,
    String liveServiceID,
    String isScheduleLiveCare,
    String appointmentDate,
    String appointmentNo,
    String doctorID, {
    SucceededCallback? onSucceeded,
    FailedCallback? onFailed,
    String countryIsoCode = "SA",
    String transactionID = "",
    String amount = "0",
    String fileNumber = "0",
    String orderDescription = "",
  }) async {
    dynamic paymentResult;

    try {
      await generateSdkResponseForNormalPayment(
          accessCode: payfortProjectDetailsRespModel!.accessCode,
          merchantIdentifier: payfortProjectDetailsRespModel!.merchantIdentifier,
          shaRequestPhrase: payfortProjectDetailsRespModel!.shaRequest,
          shaType: "SHA-256",
          merchantReference: transactionID,
          amount: amount);

      // Force a 100ms microtask buffer to isolate the UI rendering channel
      await Future.delayed(const Duration(milliseconds: 350));

      var request = {
        "command": "PURCHASE",
        "merchant_reference": transactionID, // Completely alphanumeric testing reference
        "amount": Platform.isAndroid ? (num.parse(amount) * 100).toInt() : (num.parse(amount) * 100).toInt().toString(),
        // "amount": "100000",
        "currency": getIt.get<AppState>().getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED",
        "language": getIt.get<AppState>().isArabic() ? "ar" : "en",
        "customer_email": "CustID_$fileNumber@HMG.com",
        "sdk_token": sdkTokenResponse!.sdkToken,
        "customer_name": "${getIt.get<AppState>().getAuthenticatedUser()!.firstName} ${getIt.get<AppState>().getAuthenticatedUser()!.lastName}",
      };

      PayfortRequestInsertModel payfortRequestInsertModel = PayfortRequestInsertModel();

      payfortRequestInsertModel.accessCode = payfortProjectDetailsRespModel!.accessCode;
      payfortRequestInsertModel.commandType = "PURCHASE";
      payfortRequestInsertModel.amount = num.parse(amount);
      payfortRequestInsertModel.customerEmail = getIt.get<AppState>().getAuthenticatedUser()!.emailAddress;
      payfortRequestInsertModel.orderDescription = orderDescription;
      payfortRequestInsertModel.serviceID = payfortProjectDetailsRespModel!.servID.toString();
      payfortRequestInsertModel.paymentOption = paymentOption;
      payfortRequestInsertModel.clientRequestID = transactionID;
      payfortRequestInsertModel.projectID = payfortProjectDetailsRespModel!.projectID.toString();
      payfortRequestInsertModel.fileNumber = getIt.get<AppState>().getAuthenticatedUser()!.patientId.toString();
      payfortRequestInsertModel.customerName = "${getIt.get<AppState>().getAuthenticatedUser()!.firstName} ${getIt.get<AppState>().getAuthenticatedUser()!.lastName}";
      payfortRequestInsertModel.currency = getIt.get<AppState>().getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED";
      payfortRequestInsertModel.liveServiceID = liveServiceID;
      payfortRequestInsertModel.latitude = getIt.get<AppState>().userLat.toString();
      payfortRequestInsertModel.longitude = getIt.get<AppState>().userLong.toString();
      payfortRequestInsertModel.merchantReference = transactionID;
      payfortRequestInsertModel.merchantIdentifier = payfortProjectDetailsRespModel!.merchantIdentifier;
      payfortRequestInsertModel.deviceToken = await Utils.getStringFromPrefs(CacheConst.pushToken);
      payfortRequestInsertModel.sHARequestPhase = payfortProjectDetailsRespModel!.shaRequest;
      payfortRequestInsertModel.sHAResponsePhase = payfortProjectDetailsRespModel!.shaResponse;
      payfortRequestInsertModel.signature = sdkTokenResponse!.signature;
      payfortRequestInsertModel.language = getIt.get<AppState>().isArabic() ? "ar" : "en";
      payfortRequestInsertModel.integrationId = "1";
      payfortRequestInsertModel.isSchedule = isScheduleLiveCare;
      payfortRequestInsertModel.appointmentDate = appointmentDate;
      payfortRequestInsertModel.appointmentNo = appointmentNo;
      payfortRequestInsertModel.doctorID = doctorID;

      await payfortRequestInsert(payfortRequestInsertModel: payfortRequestInsertModel);

      LoaderBottomSheet.hideLoader();

      paymentResult = await FlutterAmazonpaymentservices.normalPay(request, ApiConsts.payFortEnvironmentType, isShowResponsePage: false);
      final paymentMap = Map<String, dynamic>.from(paymentResult);
      String responseCode = paymentMap['response_code'] ?? "";
      print("Payment Success Data: ${paymentMap.toString()}");

      PayfortResponseInsertModel payfortResponseInsertModel = PayfortResponseInsertModel();

      payfortResponseInsertModel.fortId = paymentMap['fort_id'] ?? "";
      payfortResponseInsertModel.commandType = paymentMap['command'] ?? "";
      payfortResponseInsertModel.amount = num.tryParse(paymentMap['amount'] ?? "0") ?? 0;
      payfortResponseInsertModel.sadadOLP = "";
      payfortResponseInsertModel.paymentOption = paymentMap['payment_option'] ?? "";
      payfortResponseInsertModel.customerIP = paymentMap['customer_ip'] ?? "";
      payfortResponseInsertModel.eCI = paymentMap['eci'] ?? "";
      payfortResponseInsertModel.responseMessage = paymentMap['response_message'] ?? "";
      payfortResponseInsertModel.cardNumber = paymentMap['card_number'] ?? "";
      payfortResponseInsertModel.cardBin = paymentMap['card_bin'] ?? "";
      payfortResponseInsertModel.installmentsNumber = int.tryParse(paymentMap['installments_number'] ?? "0") ?? 0;
      payfortResponseInsertModel.status = paymentMap['status'] ?? "";
      payfortResponseInsertModel.merchantRef = paymentMap['merchant_reference'] ?? "";
      payfortResponseInsertModel.isRefund = false;
      payfortResponseInsertModel.remmeberMe = true;
      payfortResponseInsertModel.reconciliationReference = "";

      await payfortResponseInsert(payfortResponseInsertModel: payfortResponseInsertModel);

      if (responseCode.startsWith("14") || responseCode == "20064") {
        if (onSucceeded != null) {
          onSucceeded(PayFortResult.fromMap(paymentMap));
        }
      } else if (responseCode == "13000") {
        // Push user to 3D Secure Web View
      } else {
        // Treat as a payment rejection/technical failure
        if (onFailed != null) {
          onFailed(PayFortFailureResult.fromMap(paymentMap));
        }
      }
    } on PlatformException catch (e) {
      // If the error is the MethodChannel duplicate reply bug, handle it safely
      if (e.message != null && e.message!.contains("Reply already submitted")) {
        print("Swallowed native channel race-condition crash safely.");
        return;
      }

      // Process real payment rejection errors here
      print("Actual Payment Failure: ${e.details.toString()}");
      if (onFailed != null) {
        final paymentMap = (e.details as Map).cast<String, dynamic>();
        paymentMap["message"] = e.details["response_message"] ?? "Payment failed with unknown error.";
        onFailed(PayFortFailureResult.fromMap(paymentMap));
      }
    } catch (error) {
      print("Generic Error: $error");
    }
  }

  Future<void> paymentWithApplePay({
    SucceededCallback? onSucceeded,
    FailedCallback? onFailed,
    String? customerName,
    String? customerEmail,
    String? orderDescription,
    num? orderAmount,
    String? merchantIdentifier,
    String? applePayAccessCode,
    String? applePayShaRequestPhrase,
    String? merchantReference,
    String currency = "SAR",
    String applePayShaType = "SHA-256",
    String countryIsoCode = "SA",
  }) async {
    try {
      SdkTokenResponse? sdkTokenResponse = await _generateSdkResponse(
        applePayAccessCode: applePayAccessCode,
        merchantIdentifier: merchantIdentifier,
        applePayShaType: applePayShaType,
        applePayShaRequestPhrase: applePayShaRequestPhrase,
      );

      if (sdkTokenResponse != null && sdkTokenResponse.sdkToken == null) {
        onFailed!((sdkTokenResponse.responseMessage ?? '') as PayFortFailureResult);
        return;
      }

      /// Step 4: Processing Payment [Don't multiply with 100]
      /// Amount value send always round ex. [100] not [100.00, 100.21]
      FortRequest request = FortRequest(
          command: FortCommand.purchase,
          amount: orderAmount!,
          customerName: customerName!,
          customerEmail: customerEmail!,
          // orderDescription: orderDescription!,
          orderDescription: "Dr. Sulaiman Al Habib Hospital",
          sdkToken: sdkTokenResponse?.sdkToken ?? '',
          merchantReference: merchantReference!,
          currency: currency,
          customerIp: (await _info.getWifiIP() ?? ''),
          language: 'en');

      isApplePayConfigurationLoading = false;
      notifyListeners();
      LoaderBottomSheet.hideLoader();

      _payfort.callPayFortForApplePay(
        request: request,
        countryIsoCode: countryIsoCode,
        applePayMerchantId: ApiConsts.applePayMerchantId,
        callback: ApplePayResultCallback(
          onSucceeded: onSucceeded!,
          onFailed: onFailed!,
        ),
      );
    } catch (e) {
      onFailed!(e.toString() as PayFortFailureResult);
    }
  }

  Future<void> markAppointmentAsTamaraPaid(
      {required int projectID, required int appointmentNo, Function(dynamic)? onSuccess, Function(String)? onError}) async {
    final result = await payfortRepo.markAppointmentAsTamaraPaid(projectID: projectID, appointmentNo: appointmentNo);

    result.fold(
      (failure) async {
        onError!(failure.message);
      },
      (apiResponse) {
        if (onSuccess != null) {
          onSuccess(apiResponse);
        }
      },
    );
  }

  Future<void> getTamaraInstallmentsDetails({Function(dynamic)? onSuccess, Function(String)? onError}) async {
    final result = await payfortRepo.getTamaraInstallmentsDetails();

    result.fold(
          (failure) async => await errorHandlerService.handleError(failure: failure),
          (apiResponse) {
        getTamaraInstallmentsDetailsResponseModel = apiResponse.data!;
        isTamaraDetailsLoading = false;
        notifyListeners();
        if (onSuccess != null) {
          onSuccess(apiResponse);
        }
      },
    );
  }

}
