//
//  DataUtils.swift
//  moneyhash_payment
//
//  Created by Ahmed Elzeiny on 6/4/23.
//

import Foundation
import MoneyHash

class DataUtils {
  static func getMethodMetaData(data: [String: Any]?) -> IntentMethodMetaData? {
    if data?.isEmpty ?? true { return nil }
    let cvv = data?["cvv"] as? String
    return IntentMethodMetaData(cvv: cvv)
  }
  
  static func getEmbedStyle(data: [String: Any]?) -> EmbedStyle? {
    guard let data = data else { return nil }
    do {
      let jsonData = try JSONSerialization.data(withJSONObject: data, options: .prettyPrinted)
      let decoder = JSONDecoder()
      let embedStyle = try decoder.decode(EmbedStyle.self, from: jsonData)
      return embedStyle
    } catch {
      return nil
    }
  }
  
  static func getCardFormConfiguration(_ configuration: [String: Any]?) -> CardFormConfiguration? {
        guard var cardFormConfiguration = configuration else { return nil }
        
        do {
            let jsonCardFormConfiguration = try JSONSerialization.data(withJSONObject: cardFormConfiguration, options: .prettyPrinted)
            let decoder = JSONDecoder()
            let swiftCardFormConfiguration = try decoder.decode(CardFormConfiguration.self, from: jsonCardFormConfiguration)
            return swiftCardFormConfiguration
        } catch {
            return nil
        }
    }
    
  static func getVaultData(data: [String: Any]?) -> VaultData? {
    guard let data = data else { return nil }
    do {
      let jsonData = try JSONSerialization.data(withJSONObject: data, options: .prettyPrinted)
      let decoder = JSONDecoder()
      let vaultData = try decoder.decode(VaultData.self, from: jsonData)
      return vaultData
    } catch {
      return nil
    }
  }
  
  static func getNativePayReceipt(receipt: [String: Any]?) -> NativePayReceipt? {
    guard var receipt = receipt else { return nil }
    
    // If `receipt["receipt"]` is a dictionary, convert it to JSON string first
    if let receiptDict = receipt["receipt"] as? [String: Any],
       let receiptData = try? JSONSerialization.data(withJSONObject: receiptDict, options: []),
       let receiptString = String(data: receiptData, encoding: .utf8) {
      // Replace the dictionary with the string
      receipt["receipt"] = receiptString
    }
    
    do {
      let jsonReceipt = try JSONSerialization.data(withJSONObject: receipt, options: .prettyPrinted)
      let decoder = JSONDecoder()
      let nativePayReceipt = try decoder.decode(NativePayReceipt.self, from: jsonReceipt)
      return nativePayReceipt
    } catch {
      return nil
    }
  }
  
  static func getNativeApplePayConfig(nativeApplePayConfig: [String: Any]?) -> ApplePayConfiguration? {
    guard var nativeApplePayConfig = nativeApplePayConfig else { return nil }
    
    do {
      let jsonNativeApplePayConfig = try JSONSerialization.data(withJSONObject: nativeApplePayConfig, options: .prettyPrinted)
      let decoder = JSONDecoder()
      let applePayConfiguration = try decoder.decode(ApplePayConfiguration.self, from: jsonNativeApplePayConfig)
      return applePayConfiguration
    } catch {
      return nil
    }
  }
  
  static func getMethodType(type: String?) -> IntentMethodType {
    switch type {
    case "expressMethod":
      return IntentMethodType.expressMethod
    case "customerBalance":
      return IntentMethodType.customerBalance
    case "savedCard":
      return IntentMethodType.savedCard
    case "payoutMethod":
      return IntentMethodType.payoutMethod
    case "savedBankAccount":
      return IntentMethodType.savedBankAccount
    default:
      return IntentMethodType.paymentMethod
    }
  }
  
  static func getIntentType(type: String?) -> IntentType {
    if type == "Payout" { return IntentType.payout }
    else { return IntentType.payment }
  }
  
  static func getIntentDetailsJson(intentDetails: IntentDetails) -> String {
    do {
      return try intentDetails.convertToJSONString()
    } catch {
      return ""
    }
  }
  
  static func getIntentOperation(operation: String?) -> IntentOperation? {
    switch operation?.lowercased() {
    case "authorize":
      return .authorize
    case "purchase":
      return .purchase
    default:
      return nil
    }
  }
  
  static func getIntentStateDetailsJson(intentStateDetails: IntentStateDetails) -> String {
    do {
      return try intentStateDetails.convertToJSONString()
    } catch {
      return ""
    }
  }
  
  
  static func getLogLevel(logLevel: String) -> LogLevel {
    switch logLevel.lowercased() {
    case "verbose":
      return .verbose
    case "debug":
      return .debug
    case "info":
      return .info
    case "warn":
      return .warning
    case "error":
      return .error
    case "assert":
      return .assert
    default:
      return .info
    }
  }
  
  static func getLocale(locale: String) -> MHLocale {
    switch locale.lowercased() {
    case "ar":
      return .arabic
    case "fr":
      return .french
    default:
      return .english
    }
  }
  
  
  static func getCardFieldType(fieldType: String) -> CardFieldType {
    switch fieldType.lowercased() {
    case "card_number":
      return .cardNumber
    case "card_holder_name":
      return .cardHolderName
    case "expiry_year":
      return .expireYear
    case "expiry_month":
      return .expireMonth
    case "cvv":
      return .cvv
    default:
      fatalError("No field found for the card type")
    }
  }
  
  static func getFeesItem(data: [String: Any]?) -> FeeItem? {
    guard let data = data else { return nil }
    do {
      let jsonData = try JSONSerialization.data(withJSONObject: data, options: .prettyPrinted)
      let decoder = JSONDecoder()
      let feeItem = try decoder.decode(FeeItem.self, from: jsonData)
      return feeItem
    } catch {
      return nil
    }
  }
  static func getApplePayData(data: [String: Any]?) -> ApplePayData? {
      guard let data = data else { return nil }
      do {
          let jsonData = try JSONSerialization.data(withJSONObject: data, options: .prettyPrinted)
          let decoder = JSONDecoder()
          let applePayData = try decoder.decode(ApplePayData.self, from: jsonData)
          return applePayData
      } catch {
          return nil
      }
  }
  static func getDiscountItem(data: [String: Any]?) -> DiscountItem? {
    guard let data = data else { return nil }
    do {
      let jsonData = try JSONSerialization.data(withJSONObject: data, options: .prettyPrinted)
      let decoder = JSONDecoder()
      let discountItem = try decoder.decode(DiscountItem.self, from: jsonData)
      return discountItem
    } catch {
      return nil
    }
  }
  
  static func getFeesItems(fees: [[String: Any]]) -> [FeeItem] {
    var feeItems: [FeeItem] = []
    
    fees.forEach { item in
      if let feeItem = getFeesItem(data: item) {
        feeItems.append(feeItem)
      }
    }
    
    return feeItems
  }
  
  static func getCustomFields(from json: [String: Any]) -> [String: CustomFieldValue]? {
    return json.reduce(into: [String: CustomFieldValue]()) { result, item in
      guard let value = item.value as? [String: Any],
            let customFieldValue = getCustomFieldValue(from: value) else {
        // If the value is not valid, skip this key-value pair
        return
      }
      result[item.key] = customFieldValue
    }
  }
  static func getCustomFieldValue(from json: [String: Any]) -> CustomFieldValue? {
    guard let type = json["type"] as? String else {
      print("Error: Missing or invalid 'type'")
      return nil
    }
    
    switch type {
    case "boolean":
      if let value = json["value"] as? Bool {
        return .boolean(value: value)
      } else {
        print("Error: Invalid value for type 'boolean'")
      }
    case "float":
      if let value = json["value"] as? Float {
        return .float(value: value)
      } else if let value = json["value"] as? Double {
        return .float(value: Float(value))
      } else {
        print("Error: Invalid value for type 'float'")
      }
    case "double":
      if let value = json["value"] as? Double {
        return .double(value: value)
      } else {
        print("Error: Invalid value for type 'double'")
      }
    case "int":
      if let value = json["value"] as? Int {
        return .int(value: value)
      } else {
        print("Error: Invalid value for type 'double'")
      }
    case "string":
      if let value = json["value"] as? String {
        return .string(value: value)
      } else {
        print("Error: Invalid value for type 'string'")
      }
    case "number":
      if let value = json["value"] as? Int {
        return .int(value: value)
      } else if let value = json["value"] as? Double {
        return .double(value: value)
      }else if let value = json["value"] as? Float {
        return .float(value: value)
      } else {
        print("Error: Invalid value for type 'string'")
      }
    default:
      print("Error: Unsupported type '\(type)'")
    }
    
    return nil
  }
  static func getInstallmentPlanData(data: [String: Any]?) -> InstallmentPlanData? {
        guard let data = data else { return nil }
        do {
            let jsonData = try JSONSerialization.data(withJSONObject: data, options: .prettyPrinted)
            let decoder = JSONDecoder()
            let installmentPlanData = try decoder.decode(InstallmentPlanData.self, from: jsonData)
            return installmentPlanData
        } catch {
            return nil
        }
    }

  static func getRecurringPaymentRequest(data: [String: Any]?) -> RecurringPaymentRequest? {
    guard let data = data else { return nil }
    do {
      let jsonData = try JSONSerialization.data(withJSONObject: data, options: .prettyPrinted)
      let decoder = JSONDecoder()
//      decoder.keyDecodingStrategy = .convertFromSnakeCase
//      decoder.dateDecodingStrategy = .iso8601
      let recurringPaymentRequest = try decoder.decode(RecurringPaymentRequest.self, from: jsonData)
      return recurringPaymentRequest
    } catch {
      print("Error decoding RecurringPaymentRequest: \(error)")
      return nil
    }
  }

  static func getAutomaticReloadPaymentRequest(data: [String: Any]?) -> AutomaticReloadPaymentRequest? {
    guard let data = data else { return nil }
    do {
      let jsonData = try JSONSerialization.data(withJSONObject: data, options: .prettyPrinted)
      let decoder = JSONDecoder()
//      decoder.keyDecodingStrategy = .convertFromSnakeCase
      let automaticReloadRequest = try decoder.decode(AutomaticReloadPaymentRequest.self, from: jsonData)
      return automaticReloadRequest
    } catch {
      print("Error decoding AutomaticReloadPaymentRequest: \(error)")
      return nil
    }
  }

}
