import MoneyHash
import Foundation

@objc(MoneyHash)
class MoneyHash: NSObject {
    let moneyHashSDK = MoneyHashSDKBuilder.init().build()
    
    @objc(getIntentDetails:intentType:withResolver:withRejecter:)
    func getIntentDetails(
        _ intentId: String,
        intentType: String,
        resolver resolve: @escaping RCTPromiseResolveBlock,
        rejecter reject: @escaping RCTPromiseRejectBlock
    ) {
        Task {
            do {
                let intentDetails = try await moneyHashSDK.getIntentDetails(
                    intentId: intentId,
                    intentType: DataUtils.getIntentType(type: intentType)
                )
                let resultJsonString = try intentDetails.convertToJSONString()
                DispatchQueue.main.async {
                    resolve(resultJsonString)
                }
            } catch let error {
                DispatchQueue.main.async {
                    reject("1001", try? error.toJsonString(), error)
                }
            }
        }
    }
    
    
    @objc(getMethods:intentType:currency:amount:customer:flowId:operation:customFields:withResolver:withRejecter:)
    func getMethods(
        _ intentId: String,
        intentType: String,
        currency: String,
        amount: String,
        customer: String,
        flowId: String,
        operation: String,
        customFields: [String: Any],
        resolver resolve: @escaping RCTPromiseResolveBlock,
        rejecter reject: @escaping RCTPromiseRejectBlock
    ) {
        Task {
            do {
                let intentMethods: IntentMethods
                if !intentId.isEmpty {
                    // Fetch methods using intentId and intentType
                    intentMethods = try await moneyHashSDK.getMethods(
                        intentId: intentId,
                        intentType: DataUtils.getIntentType(type: intentType)
                    )
                } else {
                    // Fetch methods using currency, amount, customer, and flowId
                    // Handle amount, customer, and flowId as optional if they are empty
                    let amountValue: Double? = amount.isEmpty ? nil : Double(amount)
                    let customerValue: String? = customer.isEmpty ? nil : customer
                    let flowIdValue: String? = flowId.isEmpty ? nil : flowId
                    let operationValue: IntentOperation? = operation.isEmpty ? nil : DataUtils.getIntentOperation(operation: operation)
                    let customFields = DataUtils.getCustomFields(from: customFields)
                    
                    intentMethods = try await moneyHashSDK.getMethods(
                        currency: currency,
                        amount: amountValue,
                        customer: customerValue,
                        flowId: flowIdValue,
                        operation: operationValue,
                        customFields: customFields
                    )
                }
                
                let resultJsonString = try intentMethods.convertToJSONString()
                DispatchQueue.main.async {
                    resolve(resultJsonString)  // Return the result as a JSON string
                }
            } catch let error {
                DispatchQueue.main.async {
                    reject("1001", error.localizedDescription, error)  // Reject with error details
                }
            }
        }
    }
    
    @objc(resetSelectedMethod:intentType:withResolver:withRejecter:)
    func resetSelectedMethod(
        _ intentId: String,
        intentType: String,
        resolver resolve: @escaping RCTPromiseResolveBlock,
        rejecter reject: @escaping RCTPromiseRejectBlock
    ) {
        Task {
            do {
                let intentResult = try await moneyHashSDK.resetSelectedMethod(
                    intentId: intentId,
                    intentType: DataUtils.getIntentType(type: intentType)
                )
                let intentDetails = intentResult.intentData
                let intentMethods = intentResult.methods
                let intentMethodsResult = IntentMethodsResult(intentData: intentDetails, methods: intentMethods)
                let resultJsonString = try intentMethodsResult.convertToJSONString()
                DispatchQueue.main.async {
                    resolve(resultJsonString)
                }
            } catch let error {
                DispatchQueue.main.async {
                    reject("1001", try? error.toJsonString(), error)
                }
            }
        }
    }
    
    @objc(proceedWithMethod:intentType:selectedMethodId:methodType:methodMetaData:useWalletBalance:installmentPlanData:withResolver:withRejecter:)
    func proceedWithMethod(
        _ intentId: String,
        intentType: String,
        selectedMethodId: String,
        methodType: String,
        methodMetaData: [String: Any],
        useWalletBalance: Bool,
        installmentPlanData: [String: Any]?,
        resolver resolve: @escaping RCTPromiseResolveBlock,
        rejecter reject: @escaping RCTPromiseRejectBlock
    ) {
        Task {
            do {
                let planData = DataUtils.getInstallmentPlanData(data: installmentPlanData) 
                let intentResult = try await moneyHashSDK.proceedWithMethod(
                    intentId: intentId,
                    intentType: DataUtils.getIntentType(type: intentType),
                    selectedMethodId: selectedMethodId,
                    methodType: DataUtils.getMethodType(type: methodType),
                    metaData: DataUtils.getMethodMetaData(data: methodMetaData),
                    useWalletBalance: useWalletBalance,
                    installmentPlanData: planData
                )
                let intentDetails = intentResult.intentData
                let intentMethods = intentResult.methods
                let intentMethodsResult = IntentMethodsResult(intentData: intentDetails, methods: intentMethods)
                let resultJsonString = try intentMethodsResult.convertToJSONString()
                DispatchQueue.main.async {
                    resolve(resultJsonString)
                }
            } catch let error {
                DispatchQueue.main.async {
                    reject("1001", try? error.toJsonString(), error)
                }
            }
        }
    }
    
    
    @objc(deleteSavedCard:intentSecret:withResolver:withRejecter:)
    func deleteSavedCard(
        _ cardTokenId: String,
        intentSecret: String,
        resolver resolve: @escaping RCTPromiseResolveBlock,
        rejecter reject: @escaping RCTPromiseRejectBlock
    ) {
        Task {
            do {
                _ = try await moneyHashSDK.deleteSavedCard(
                    cardTokenId: cardTokenId,
                    intentSecret: intentSecret
                )
                DispatchQueue.main.async {
                    resolve("Success")
                }
            } catch let error {
                DispatchQueue.main.async {
                    reject("1001", try? error.toJsonString(), error)
                }
            }
        }
    }
    
    
    @objc(renderForm:intentType:embedStyle:withResolver:withRejecter:)
    func renderForm(_ intentId: String,
                    intentType: String,
                    embedStyle: [String: Any],
                    withResolver resolve: @escaping RCTPromiseResolveBlock,
                    withRejecter reject: @escaping RCTPromiseRejectBlock) {
        
        var rootViewController: UIViewController?
        
        let embedStyle = DataUtils.getEmbedStyle(data: embedStyle)
        
        DispatchQueue.main.async {
            
            if #available(iOS 13.0, *) {
                rootViewController = (UIApplication.shared.connectedScenes.first as? UIWindowScene)?.windows.first(where: { $0.isKeyWindow })?.rootViewController
            } else {
                rootViewController = UIApplication.shared.delegate?.window??.rootViewController
            }
            
            
            self.moneyHashSDK.renderForm(
                on: rootViewController!,
                intentId: intentId,
                embedStyle: embedStyle,
                intentType: DataUtils.getIntentType(type: intentType)
            ) { intentDetails in
                do {
                    resolve(try intentDetails.get().convertToJSONString())
                } catch let error {
                  DispatchQueue.main.async {
                      reject("1001", try? error.toJsonString(), error)
                  }
              }
            }
            
        }
    }

    @objc(renderSubscriptionEmbed:embedStyle:withResolver:withRejecter:)
    func renderSubscriptionEmbed(_ intentId: String,
                    embedStyle: [String: Any],
                    withResolver resolve: @escaping RCTPromiseResolveBlock,
                    withRejecter reject: @escaping RCTPromiseRejectBlock) {
        
        var rootViewController: UIViewController?
        
        let embedStyle = DataUtils.getEmbedStyle(data: embedStyle)
        
        DispatchQueue.main.async {
            
            if #available(iOS 13.0, *) {
                rootViewController = (UIApplication.shared.connectedScenes.first as? UIWindowScene)?.windows.first(where: { $0.isKeyWindow })?.rootViewController
            } else {
                rootViewController = UIApplication.shared.delegate?.window??.rootViewController
            }
            
            
            self.moneyHashSDK.renderSubscriptionEmbed(
                on: rootViewController!,
                intentId: intentId,
                embedStyle: embedStyle
            ) { intentDetails in
                do {
                    resolve(try intentDetails.get().convertToJSONString())
                } catch let error {
                  DispatchQueue.main.async {
                      reject("1001", try? error.toJsonString(), error)
                  }
              }
            }
            
        }
    }

  @objc(renderURL:intentId:intentType:embedStyle:withResolver:withRejecter:)
  func renderURL(_ url: String,
                 intentId: String,
                 intentType: String,
                 embedStyle: [String: Any],
                 withResolver resolve: @escaping RCTPromiseResolveBlock,
                 withRejecter reject: @escaping RCTPromiseRejectBlock) {
    
    var rootViewController: UIViewController?
    
    let embedStyle = DataUtils.getEmbedStyle(data: embedStyle)
    
    DispatchQueue.main.async {
      
      if #available(iOS 13.0, *) {
        rootViewController = (UIApplication.shared.connectedScenes.first as? UIWindowScene)?.windows.first(where: { $0.isKeyWindow })?.rootViewController
      } else {
        rootViewController = UIApplication.shared.delegate?.window??.rootViewController
      }
      guard let url = URL(string: url) else {
        let error = NSError(domain: "com.example.MoneyhashPaymentModule", code: -1, userInfo: [NSLocalizedDescriptionKey: "cannot create url object from path: \(url)"])
        DispatchQueue.main.async {
          reject("1000", try? error.toJsonString(), error)
        }
        return
      }
      
      self.moneyHashSDK.renderURL(
        on: rootViewController!,
        urlToRender: url,
        intentId: intentId,
        embedStyle: embedStyle,
        intentType: DataUtils.getIntentType(type: intentType)
      ) { intentDetails in
        do {
          resolve(try intentDetails.get().convertToJSONString())
        } catch let error {
          DispatchQueue.main.async {
            reject("1001", try? error.toJsonString(), error)
          }
        }
      }
    }
  }
  
    @objc(submitForm:selectedMethodId:billingData:shippingData:cardData:saveCard:installmentPlanData:withResolver:withRejecter:)
    func submitForm(
        _ intentId: String,
        selectedMethodId: String,
        billingData: [String: String]?,
        shippingData: [String: String]?,
        cardData: [String: Any]?,
        saveCard: Bool,
        installmentPlanData: [String: Any]?,
        withResolver resolve: @escaping RCTPromiseResolveBlock,
        withRejecter reject: @escaping RCTPromiseRejectBlock
    ) {
        Task {
            do {
                let cardVaultData = DataUtils.getVaultData(data: cardData)
                let planData = DataUtils.getInstallmentPlanData(data: installmentPlanData) 
                let intentDetails = try await moneyHashSDK.submitForm(
                    intentID: intentId,
                    selectedMethod: selectedMethodId,
                    billingData: billingData,
                    shippingData: shippingData,
                    vaultData: cardVaultData,
                    saveCard: saveCard,
                    installmentPlanData: planData
                )
                let resultJsonString = try intentDetails.convertToJSONString()
                DispatchQueue.main.async {
                    resolve(resultJsonString)
                }
            } catch let error {
                DispatchQueue.main.async {
                    reject("1001", try? error.toJsonString(), error)
                }
            }
        }
    }
    
    @objc(submitCardCVV:cvv:installmentPlanData:withResolver:withRejecter:)
    func submitCardCVV(
        _ intentId: String,
        cvv: String,
        installmentPlanData: [String: Any]?,
        withResolver resolve: @escaping RCTPromiseResolveBlock,
        withRejecter reject: @escaping RCTPromiseRejectBlock
    ) {
        Task {
            do {
                let planData = DataUtils.getInstallmentPlanData(data: installmentPlanData) 
                let intentDetails = try await moneyHashSDK.submitCardCVV(
                    intentID: intentId,
                    cvv: cvv,
                    installmentPlanData: planData
                )
                let resultJsonString = try intentDetails.convertToJSONString()
                DispatchQueue.main.async {
                    resolve(resultJsonString)
                }
            } catch let error {
                DispatchQueue.main.async {
                    reject("1001", try? error.toJsonString(), error)
                }
            }
        }
    }
    
    @objc(submitPaymentReceipt:receipt:withResolver:withRejecter:)
    func submitPaymentReceipt(
        _ intentId: String,
        receipt: [String: Any],
        withResolver resolve: @escaping RCTPromiseResolveBlock,
        withRejecter reject: @escaping RCTPromiseRejectBlock
    ) {
        Task {
            do {
                // Ensure receipt is valid
                guard let nativePayReceipt = DataUtils.getNativePayReceipt(receipt: receipt) else {
                    let error = NSError(domain: "com.example.MoneyhashPaymentModule", code: 1000, userInfo: [NSLocalizedDescriptionKey: "Invalid receipt data"])
                    
                    DispatchQueue.main.async {
                        reject("1001", try? error.toJsonString(), error)
                    }
                    return
                }
                
                let intentDetails = try await moneyHashSDK.submitPaymentReceipt(
                    intentId: intentId,
                    nativePayReceipt: nativePayReceipt                  )
                let resultJsonString = try intentDetails.convertToJSONString()
                DispatchQueue.main.async {
                    resolve(resultJsonString)
                }
            } catch let error {
                DispatchQueue.main.async {
                    reject("1001", try? error.toJsonString(), error)
                }
            }
        }
    }
    
    
    @objc(setLogLevel:withResolver:withRejecter:)
    func setLogLevel(
        _ logLevel: String,
        withResolver resolve: @escaping RCTPromiseResolveBlock,
        withRejecter reject: @escaping RCTPromiseRejectBlock
    ) {
        let logLevel = DataUtils.getLogLevel(logLevel: logLevel)
        moneyHashSDK.setLogLevel(logLevel: logLevel)
        DispatchQueue.main.async {
            resolve("Success")
        }
    }
    
    @objc(setNativeApplePayConfig:withResolver:withRejecter:)
    func setNativeApplePayConfig(
        _ nativeApplePayConfig: [String: Any],
        withResolver resolve: @escaping RCTPromiseResolveBlock,
        withRejecter reject: @escaping RCTPromiseRejectBlock
    ) {
        // Ensure config is valid
        guard let nativeApplePayConfig = DataUtils.getNativeApplePayConfig(nativeApplePayConfig: nativeApplePayConfig) else {
            let error = NSError(domain: "com.example.MoneyhashPaymentModule", code: 1000, userInfo: [NSLocalizedDescriptionKey: "Invalid Apple Pay configuration"])
            
            DispatchQueue.main.async {
                reject("1001", try? error.toJsonString(), error)
            }
            return
        }
        
        moneyHashSDK.setApplePayConfiguration(nativeApplePayConfig)
        DispatchQueue.main.async {
            resolve("Success")
        }
    }
    
  @objc(generateApplePayReceiptWithApplePayData:applePayData:withResolver:withRejecter:)
  func generateApplePayReceiptWithApplePayData(
    _ depositAmount: Double,
    applePayData: [String: Any],
    withResolver resolve: @escaping RCTPromiseResolveBlock,
    withRejecter reject: @escaping RCTPromiseRejectBlock
  ) {
    Task { @MainActor in
      do {
        guard let applePayData = DataUtils.getApplePayData(data: applePayData) else {
          let error = NSError(domain: "com.example.MoneyhashPaymentModule", code: 1000, userInfo: [NSLocalizedDescriptionKey: "Invalid apple pay data"])
          DispatchQueue.main.async {
              reject("1001", try? error.toJsonString(), error)
          }
          return
        }
        let nativePayReceipt = try await moneyHashSDK.generateApplePayReceipt(depositAmount: Float(depositAmount), applePayData: applePayData)
        let resultJsonString = try nativePayReceipt.convertToJSONString()
        DispatchQueue.main.async {
          resolve(resultJsonString)
        }
      } catch let error {
        DispatchQueue.main.async {
          reject("1001", try? error.toJsonString(), error)
        }
      }
    }
  }
  @objc(generateRecurringApplePayReceipt:merchantIdentifier:currencyCode:countryCode:supportedNetworks:merchantCapabilities:recurringPayment:automaticReload:withResolver:withRejecter:)
  func generateRecurringApplePayReceipt(
    _ depositAmount: Double,
    merchantIdentifier: String,
    currencyCode: String,
    countryCode: String,
    supportedNetworks: [String],
    merchantCapabilities: [String],
    recurringPayment: [String: Any]?,
    automaticReload: [String: Any]?,
    withResolver resolve: @escaping RCTPromiseResolveBlock,
    withRejecter reject: @escaping RCTPromiseRejectBlock
  ) {
    Task { @MainActor in
      do {
        // Convert JSON dictionaries to Swift objects
        var recurringPaymentRequest: RecurringPaymentRequest? = nil
        var automaticReloadRequest: AutomaticReloadPaymentRequest? = nil
        
        if let recurringPaymentData = recurringPayment {
          recurringPaymentRequest = DataUtils.getRecurringPaymentRequest(data: recurringPaymentData)
        }
        
        if let automaticReloadData = automaticReload {
          automaticReloadRequest = DataUtils.getAutomaticReloadPaymentRequest(data: automaticReloadData)
        }
        
        if #available(iOS 16.0, *) {
          let receipt = try await moneyHashSDK.generateApplePayReceipt(
            depositAmount: Float(depositAmount),
            merchantIdentifier: merchantIdentifier,
            currencyCode: currencyCode,
            countryCode: countryCode,
            supportedNetworks: supportedNetworks,
            merchantCapabilities: merchantCapabilities,
            recurringPayment: recurringPaymentRequest,
            automaticReload: automaticReloadRequest
          )
          
          let json = try receipt.convertToJSONString()
          DispatchQueue.main.async { resolve(json) }
        } else {
          print("recurringPayment and automaticReload are only supported on iOS 16.0 and above. Proceeding without these features.")
          let receipt = try await moneyHashSDK.generateApplePayReceipt(
            depositAmount: Float(depositAmount),
            merchantIdentifier: merchantIdentifier,
            currencyCode: currencyCode,
            countryCode: countryCode,
            supportedNetworks: supportedNetworks,
            merchantCapabilities: merchantCapabilities,
          )
          
          let json = try receipt.convertToJSONString()
          DispatchQueue.main.async { resolve(json) }
        }

      } catch {
        DispatchQueue.main.async {
          reject("1001", try? error.toJsonString(), error)
        }
      }
    }
  }
    
    @objc(updateCardField:value:withResolver:withRejecter:)
    func updateCardField(
        _ fieldType: String,
        value: String,
        withResolver resolve: @escaping RCTPromiseResolveBlock,
        withRejecter reject: @escaping RCTPromiseRejectBlock
    ) {
        Task {
            // Update only: store the value and refresh the brand. The field state
            // is fetched separately via `validateCardField`.
            let cardFieldType = DataUtils.getCardFieldType(fieldType: fieldType)
            moneyHashSDK.updateField(
                fieldType: cardFieldType,
                currentValue: value
            )
            DispatchQueue.main.async {
                resolve("success")
            }
        }
    }

    @objc(validateCardField:withResolver:withRejecter:)
    func validateCardField(
        _ fieldType: String,
        withResolver resolve: @escaping RCTPromiseResolveBlock,
        withRejecter reject: @escaping RCTPromiseRejectBlock
    ) {
        Task {
            do {
                let cardFieldType = DataUtils.getCardFieldType(fieldType: fieldType)
                let fieldCardState = moneyHashSDK.validateField(
                    fieldType: cardFieldType
                )
                let resultJsonString = try fieldCardState.convertToJSONString()
                DispatchQueue.main.async {
                    resolve(resultJsonString)
                }
            } catch let error {
                DispatchQueue.main.async {
                    reject("1001", try? error.toJsonString(), error)
                }
            }
        }
    }
    
    @objc(isValidCardForm:withRejecter:)
    func isValidCardForm(
        withResolver resolve: @escaping RCTPromiseResolveBlock,
        withRejecter reject: @escaping RCTPromiseRejectBlock
    ) {
        Task {
            do {
                
                let cardFormState = moneyHashSDK.isValidForm()
                let resultJsonString = try cardFormState.convertToJSONString()
                DispatchQueue.main.async {
                    resolve(resultJsonString)
                }
            } catch let error {
                DispatchQueue.main.async {
                    reject("1001", try? error.toJsonString(), error)
                }
            }
        }
    }
    
    @objc(isDeviceCompatibleWithApplePay:withRejecter:)
    func isDeviceCompatibleWithApplePay(
        withResolver resolve: @escaping RCTPromiseResolveBlock,
        withRejecter reject: @escaping RCTPromiseRejectBlock
    ) {
      Task {
        await MainActor.run {
          let isCompatible = moneyHashSDK.isDeviceCompatible()
          resolve(isCompatible)
        }
      }
    }
    
    
    @objc(collectCardForm:withResolver:withRejecter:)
    func collectCardForm(
        _ token: String,
        withResolver resolve: @escaping RCTPromiseResolveBlock,
        withRejecter reject: @escaping RCTPromiseRejectBlock
    ) {
        Task {
            do {
                let tokenValue: String? = token.isEmpty ? nil : token
                let vaultData = try await moneyHashSDK.collect(
                    token: tokenValue
                )
                
                let resultJsonString = try vaultData?.convertToJSONString()
                DispatchQueue.main.async {
                    resolve(resultJsonString)
                }
            } catch let error {
                DispatchQueue.main.async {
                    reject("1001", try? error.toJsonString(), error)
                }
            }
        }
    }
    
    @objc(setCardFormConfiguration:withResolver:withRejecter:)
    func setCardFormConfiguration(
        _ configuration: [String: Any],
        withResolver resolve: @escaping RCTPromiseResolveBlock,
        withRejecter reject: @escaping RCTPromiseRejectBlock
    ) {
        // Ensure cardData is valid
        guard let configuration = DataUtils.getCardFormConfiguration(configuration) else {
            let error = NSError(domain: "com.example.MoneyhashPaymentModule", code: 1000, userInfo: [NSLocalizedDescriptionKey: "Invalid Card Form Configuration"])
            
            DispatchQueue.main.async {
                reject("1001", try? error.toJsonString(), error)
            }
            return
        }
        moneyHashSDK.setCardFormConfiguration(configuration)
        DispatchQueue.main.async {
            resolve("success")
        }
    }
    
    @objc(pay:cardData:saveCard:billingData:shippingData:installmentPlanData:withResolver:withRejecter:)
    func pay(
        _ intentId: String,
        cardData: [String: Any],
        saveCard: Bool,
        billingData: [String: String]?,
        shippingData: [String: String]?,
        installmentPlanData: [String: Any]?,
        resolver resolve: @escaping RCTPromiseResolveBlock,
        rejecter reject: @escaping RCTPromiseRejectBlock
    ) {
        // Ensure cardData is valid
        guard let vaultData = DataUtils.getVaultData(data: cardData) else {
            let error = NSError(domain: "com.example.MoneyhashPaymentModule", code: 1000, userInfo: [NSLocalizedDescriptionKey: "Invalid card data"])
            
            DispatchQueue.main.async {
                reject("1001", try? error.toJsonString(), error)
            }
            return
        }
        let planData = DataUtils.getInstallmentPlanData(data: installmentPlanData)
        // Handle the asynchronous payment operation
        Task {
            do {
                // Execute the payment asynchronously using the SDK
                let paymentResult = try await moneyHashSDK.pay(
                    intentId: intentId,
                    cardData: vaultData,
                    saveCard: saveCard,
                    billingData: billingData,
                    shippingData: shippingData,
                    installmentPlanData: planData
                )
                
                // Convert the payment result to a JSON string
                let resultJsonString = DataUtils.getIntentDetailsJson(intentDetails: paymentResult)
                
                // Return the result on the main thread
                DispatchQueue.main.async {
                    resolve(resultJsonString)
                }
            } catch let error {
                // Handle error on the main thread
                DispatchQueue.main.async {
                  reject("1001", try? error.toJsonString(), error)
                }
            }
        }
    }
    
    @objc(createCardToken:cardData:withResolver:withRejecter:)
    func createCardToken(
        _ cardIntentId: String,
        cardData: [String: Any],
        resolver resolve: @escaping RCTPromiseResolveBlock,
        rejecter reject: @escaping RCTPromiseRejectBlock
    ) {
        // Validate cardData
        guard let vaultData = DataUtils.getVaultData(data: cardData) else {
            let error = NSError(domain: "com.example.MoneyhashPaymentModule", code: 1000, userInfo: [NSLocalizedDescriptionKey: "Invalid card data"])
            
            DispatchQueue.main.async {
                reject("1001", try? error.toJsonString(), error)
            }
            return
        }
        
        // Handle the asynchronous operation
        Task {
            do {
                // Fetch the card token asynchronously
                let tokenResult = try await moneyHashSDK.createCardToken(
                    cardIntentId: cardIntentId,
                    cardData: vaultData
                )
                
                // Handle null token result
                guard let tokenResult = tokenResult else {
                    let error = NSError(domain: "com.example.MoneyhashPaymentModule", code: 1000, userInfo: [NSLocalizedDescriptionKey: "Can't create card token"])
                    
                    DispatchQueue.main.async {
                        reject("1001", try? error.toJsonString(), error)
                    }
                    
                    return
                }
                
                // Convert the token result to a JSON string
                let resultJsonString = DataUtils.getIntentStateDetailsJson(intentStateDetails: tokenResult)
                
                // Return the result on the main thread
                DispatchQueue.main.async {
                    resolve(resultJsonString)
                }
            } catch let error {
                // Handle any errors that occur during the async operation
                DispatchQueue.main.async {
                  reject("1001", try? error.toJsonString(), error)
                }
            }
        }
    }
    
    @objc(binLookup:withRejecter:)
    func binLookup(
        withResolver resolve: @escaping RCTPromiseResolveBlock,
        withRejecter reject: @escaping RCTPromiseRejectBlock
    ) {
        Task {
            do {
                // Call binLookup without cardData
                let lookupData = try await moneyHashSDK.binLookup()
                
                // Convert result to JSON string
              let resultJsonString = try lookupData.convertToJSONString()
                DispatchQueue.main.async {
                    resolve(resultJsonString)
                }
            } catch let error {
                DispatchQueue.main.async {
                    reject("1001", try? error.toJsonString(), error)
                }
            }
        }
    }
    
    @objc(binLookupWithCardData:withResolver:withRejecter:)
    func binLookupWithCardData(
        _ cardData: [String: Any],
        withResolver resolve: @escaping RCTPromiseResolveBlock,
        withRejecter reject: @escaping RCTPromiseRejectBlock
    ) {
        Task {
            do {
                // Validate cardData
                guard let vaultData = DataUtils.getVaultData(data: cardData) else {
                    let error = NSError(domain: "com.example.MoneyhashPaymentModule", code: 1000, userInfo: [NSLocalizedDescriptionKey: "Invalid card data"])
                    
                    DispatchQueue.main.async {
                        reject("1001", try? error.toJsonString(), error)
                    }
                    return
                }
                
                // Call binLookup with cardData
                let lookupData = try await moneyHashSDK.binLookup(cardData: vaultData)
                
                // Convert result to JSON string
                let resultJsonString = try lookupData.convertToJSONString()
                DispatchQueue.main.async {
                    resolve(resultJsonString)
                }
            } catch let error {
                DispatchQueue.main.async {
                    reject("1001", try? error.toJsonString(), error)
                }
            }
        }
    }
    
    @objc(setLocale:withResolver:withRejecter:)
    func setLocale(
        _ localeString: String,
        withResolver resolve: @escaping RCTPromiseResolveBlock,
        withRejecter reject: @escaping RCTPromiseRejectBlock
    ) {
        
        let locale = DataUtils.getLocale(locale: localeString)
        moneyHashSDK.setLocale(locale)
        DispatchQueue.main.async {
            resolve("Success")
        }
    }
    
    @objc(setPublicKey:withResolver:withRejecter:)
    func setPublicKey(
        _ publicKey: String,
        withResolver resolve: @escaping RCTPromiseResolveBlock,
        withRejecter reject: @escaping RCTPromiseRejectBlock
    ) {
        
        moneyHashSDK.setPublicKey(publicKey)
        DispatchQueue.main.async {
            resolve("Success")
        }
    }
    
    @objc(setVersionName:withResolver:withRejecter:)
    func setVersionName(
        _ versionName: String,
        withResolver resolve: @escaping RCTPromiseResolveBlock,
        withRejecter reject: @escaping RCTPromiseRejectBlock
    ) {
        
        moneyHashSDK.setCrossplatformVersion(versionName)
        DispatchQueue.main.async {
            resolve("Success")
        }
    }
    
    @objc(updateFees:fees:withResolver:withRejecter:)
    func updateFees(
        _ intentId: String,
        fees: [[String: Any]],
        withResolver resolve: @escaping RCTPromiseResolveBlock,
        withRejecter reject: @escaping RCTPromiseRejectBlock
    ) {
        Task {
            do {
                let feesData = DataUtils.getFeesItems(fees: fees)
                let updatedFeesData = try await moneyHashSDK.updateFees(
                    intentId: intentId,
                    fees: feesData
                )
                
                let resultJsonString = try updatedFeesData.convertToJSONString()
                DispatchQueue.main.async {
                    resolve(resultJsonString)
                }
            } catch let error {
                DispatchQueue.main.async {
                    reject("1001", try? error.toJsonString(), error)
                }
            }
        }
    }
    
    @objc(updateDiscount:discount:withResolver:withRejecter:)
    func updateDiscount(
        _ intentId: String,
        discount: [String: Any],
        withResolver resolve: @escaping RCTPromiseResolveBlock,
        withRejecter reject: @escaping RCTPromiseRejectBlock
    ) {
        Task {
            do {
                guard let discountItem = DataUtils.getDiscountItem(data: discount) else {
                    let error = NSError(domain: "com.example.MoneyhashPaymentModule", code: 1000, userInfo: [NSLocalizedDescriptionKey: "Discount item is required and cannot be null or empty"])
                    
                    DispatchQueue.main.async {
                        reject("1001", try? error.toJsonString(), error)
                    }
                    return
                }
                
                let updatedDiscountData = try await moneyHashSDK.updateDiscount(
                    intentId: intentId,
                    discount: discountItem
                )
                
                let resultJsonString = try updatedDiscountData.convertToJSONString()
                DispatchQueue.main.async {
                    resolve(resultJsonString)
                }
            } catch let error {
                DispatchQueue.main.async {
                    reject("1001", try? error.toJsonString(), error)
                }
            }
        }
    }

    @objc(selectInstallmentPlan:installmentPlanData:withResolver:withRejecter:)
    func selectInstallmentPlan(
        _ intentId: String,
        installmentPlanData: [String: Any],
        withResolver resolve: @escaping RCTPromiseResolveBlock,
        withRejecter reject: @escaping RCTPromiseRejectBlock
    ) {
        Task {
            do {
                guard let planData = DataUtils.getInstallmentPlanData(data: installmentPlanData) else {
                    let error = NSError(
                        domain: "com.example.MoneyhashPaymentModule",
                        code: 1000,
                        userInfo: [NSLocalizedDescriptionKey: "Invalid InstallmentPlanData"]
                    )
                    DispatchQueue.main.async {
                        reject("1001", try? error.toJsonString(), error)
                    }
                    return
                }

                // 2. Call the SDK
                let intentDetails = try await moneyHashSDK.selectInstallmentPlan(
                    intentId: intentId,
                    installmentPlanData: planData
                )

                // 3. Convert result to JSON string
                let resultJsonString = try intentDetails.convertToJSONString()
                DispatchQueue.main.async {
                    resolve(resultJsonString)
                }
            } catch let error {
                DispatchQueue.main.async {
                    reject("1001", try? error.toJsonString(), error)
                }
            }
        }
    }

    @objc(getInstallmentPlans:currency:first6Digits:withResolver:withRejecter:)
    func getInstallmentPlans(
        _ amount: Double,
        currency: String,
        first6Digits: String,
        withResolver resolve: @escaping RCTPromiseResolveBlock,
        withRejecter reject: @escaping RCTPromiseRejectBlock
    ) {
        Task {
            do {
                // 1. Treat empty string as nil for `first6Digits`
                let first6DigitsValue: String? = first6Digits.isEmpty ? nil : first6Digits

                // 2. Call the SDK
                let installmentPlans = try await moneyHashSDK.getInstallmentPlans(
                    amount: amount,
                    currency: currency,
                    first6Digits: first6DigitsValue
                )

                // 3. Convert result ([InstallmentPlan]) to JSON
                //    (If your `convertToJSONString()` doesn't handle arrays automatically,
                //     you may need a helper method. For example, use `map { try $0.toDictionary() }`
                //     then serialize. Adjust to your actual approach.)
                let jsonString = try installmentPlans.convertToJSONString()
                
                DispatchQueue.main.async {
                    resolve(jsonString)
                }
            } catch let error {
                DispatchQueue.main.async {
                    reject("1001", try? error.toJsonString(), error)
                }
            }
        }
    }
  
  @objc(getApplePayBinLookup:methodID:flowId:withResolver:withRejecter:)
  func getApplePayBinLookup(
    _ receipt:  [String: Any],
    methodID: String,
    flowId: String,
    withResolver resolve: @escaping RCTPromiseResolveBlock,
    withRejecter reject: @escaping RCTPromiseRejectBlock
  ) {
    Task {
      do {
        // Ensure receipt is valid
        guard let nativePayReceipt = DataUtils.getNativePayReceipt(receipt: receipt) else {
          let error = NSError(domain: "com.example.MoneyhashPaymentModule", code: 1000, userInfo: [NSLocalizedDescriptionKey: "Invalid receipt data"])
          
          DispatchQueue.main.async {
            reject("1001", try? error.toJsonString(), error)
          }
          return
        }
        let flowIdValue: String? = flowId.isEmpty ? nil : flowId
        
        let lookupData = try await moneyHashSDK.getApplePayBinLookup(
          receipt: nativePayReceipt,
          methodID: methodID,
          flowId: flowIdValue
        )
        let resultJsonString = try lookupData.convertToJSONString()
        DispatchQueue.main.async {
          resolve(resultJsonString)
        }
      } catch let error {
        DispatchQueue.main.async {
          reject("1001", try? error.toJsonString(), error)
        }
      }
    }
  }

  @objc(tokenizeReceipt:methodId:cardTokenIntentId:withResolver:withRejecter:)
  func tokenizeReceipt(
    _ receipt: String,
    methodId: String,
    cardTokenIntentId: String,
    withResolver resolve: @escaping RCTPromiseResolveBlock,
    withRejecter reject: @escaping RCTPromiseRejectBlock
  ) {
    Task {
      do {
        let cardTokenId = try await moneyHashSDK.tokenizeReceipt(
          receipt: receipt,
          methodId: methodId,
          cardTokenIntentId: cardTokenIntentId
        )
        
        DispatchQueue.main.async {
          resolve(cardTokenId)
        }
      } catch let error {
        DispatchQueue.main.async {
          reject("1001", try? error.toJsonString(), error)
        }
      }
    }
  }
  @objc(getSubscriptionPlans:customerId:withResolver:withRejecter:)
  func getSubscriptionPlans(
    _ planGroupId: String,
    customerId: String,
    withResolver resolve: @escaping RCTPromiseResolveBlock,
    withRejecter reject: @escaping RCTPromiseRejectBlock
  ) {
    Task {
      do {
        // 1. Call SDK
        let plans = try await moneyHashSDK.getSubscriptionPlans(
          planGroupId: planGroupId,
          customerId: customerId
        )
        
        // 2. Convert `[SubscriptionPlan]` → JSON string
        let jsonString = try plans.convertToJSONString()
        DispatchQueue.main.async { resolve(jsonString) }
      } catch let error {
        DispatchQueue.main.async {
          reject("1001", try? error.toJsonString(), error)
        }
      }
    }
  }
  
  @objc(selectSubscriptionPlan:customerId:planId:withResolver:withRejecter:)
  func selectSubscriptionPlan(
    _ planGroupId: String,
    customerId: String,
    planId: String,
    withResolver resolve: @escaping RCTPromiseResolveBlock,
    withRejecter reject: @escaping RCTPromiseRejectBlock
  ) {
    Task {
      do {
        // 1. Call SDK
        let intentDetails = try await moneyHashSDK.selectSubscriptionPlan(
          planGroupId: planGroupId,
          customerId: customerId,
          planId: planId
        )
        
        // 2. Convert `IntentDetails` → JSON string
        let jsonString = try intentDetails.convertToJSONString()
        DispatchQueue.main.async { resolve(jsonString) }
      } catch let error {
        DispatchQueue.main.async {
          reject("1001", try? error.toJsonString(), error)
        }
      }
    }
  }
  
  @objc(getSubscriptionPlanGroups:offset:currency:withResolver:withRejecter:)
  func getSubscriptionPlanGroups(
    _ limit: Int,
    offset: Int,
    currency: String,
    withResolver resolve: @escaping RCTPromiseResolveBlock,
    withRejecter reject: @escaping RCTPromiseRejectBlock
  ) {
    Task {
      do {
        // 1. Call SDK with optional parameters
        let response = try await moneyHashSDK.getSubscriptionPlanGroups(
          limit: limit == 0 ? nil : limit,
          offset: offset == 0 ? nil : offset,
          currency: currency.isEmpty ? nil : currency
        )
        
        // 2. Convert `SubscriptionPlanGroupsResponse` → JSON string
        let jsonString = try response.convertToJSONString()
        DispatchQueue.main.async { resolve(jsonString) }
      } catch let error {
        DispatchQueue.main.async {
          reject("1001", try? error.toJsonString(), error)
        }
      }
    }
  }

  @objc(renderCreateBankAccountTokenEmbed:withResolver:withRejecter:)
  func renderCreateBankAccountTokenEmbed(
    _ intentId: String,
    withResolver resolve: @escaping RCTPromiseResolveBlock,
    withRejecter reject: @escaping RCTPromiseRejectBlock
  ) {
    var rootViewController: UIViewController?
    
    DispatchQueue.main.async {
      
      if #available(iOS 13.0, *) {
        rootViewController = (UIApplication.shared.connectedScenes.first as? UIWindowScene)?.windows.first(where: { $0.isKeyWindow })?.rootViewController
      } else {
        rootViewController = UIApplication.shared.delegate?.window??.rootViewController
      }
      
      self.moneyHashSDK.renderCreateBankAccountTokenEmbed(
        on: rootViewController!,
        intentId: intentId
      ) { result in
        DispatchQueue.main.async {
          switch result {
          case .success(let status):
            do {
              let jsonString = try status.convertToJSONString()
              resolve(jsonString)
            } catch let error {
              reject("1001", try? error.toJsonString(), error)
            }
          case .failure(let error):
            reject("1001", try? error.toJsonString(), error)
          }
        }
      }
    }
  }
}
