<commit_before>//
//  PXAmountHelper.swift
//  MercadoPagoSDK
//
//  Created by <NAME> on 29/5/18.
//  Copyright © 2018 MercadoPago. All rights reserved.
//

import Foundation

internal struct PXAmountHelper {

    internal let preference: PXCheckoutPreference
    private let paymentData: PXPaymentData
    internal let chargeRules: [PXPaymentTypeChargeRule]?
    internal let paymentConfigurationService: PXPaymentConfigurationServices
    internal var splitAccountMoney: PXPaymentData?

    init (preference: PXCheckoutPreference, paymentData: PXPaymentData, chargeRules: [PXPaymentTypeChargeRule]?, paymentConfigurationService: PXPaymentConfigurationServices, splitAccountMoney: PXPaymentData?) {
        self.preference = preference
        self.paymentData = paymentData
        self.chargeRules = chargeRules
        self.paymentConfigurationService = paymentConfigurationService
        self.splitAccountMoney = splitAccountMoney
    }

    internal var consumedDiscount: Bool {
        return paymentData.consumedDiscount ?? false
    }

    var discount: PXDiscount? {
        return paymentData.discount
    }

    var campaign: PXCampaign? {
        return paymentData.campaign
    }

    var preferenceAmount: Double {
        return self.preference.getTotalAmount()
    }

    var preferenceAmountWithCharges: Double {
        return preferenceAmount + chargeRuleAmount
    }
    
    var amount: Double? {
        return paymentData.amount
    }
    
    var taxFreeAmount: Double? {
        return paymentData.taxFreeAmount
    }
    
    var noDiscountAmount: Double? {
        return paymentData.noDiscountAmount
    }

    var amountToPay: Double {
        if let payerCost = paymentData.payerCost {
            return payerCost.totalAmount
        }
        if let couponAmount = paymentData.discount?.couponAmount {
            return preferenceAmount - couponAmount + chargeRuleAmount
        } else {
            return preferenceAmount + chargeRuleAmount
        }
    }

    var isSplitPayment: Bool {
        return splitAccountMoney != nil
    }

    func getAmountToPayWithoutPayerCost(paymentOptionID: String?, paymentMethodId: String?, paymentTypeId: String?) -> Double {
        guard let paymentMethodId = paymentMethodId,
              let amountFromPaymentMethod = paymentConfigurationService.getAmountToPayWithoutPayerCostForPaymentMethod(paymentOptionID: paymentOptionID, paymentMethodId: paymentMethodId, paymentTypeId: paymentTypeId) else {
            return amountToPayWithoutPayerCost
        }
        return amountFromPaymentMethod
    }

    private var amountToPayWithoutPayerCost: Double {
        if let couponAmount = paymentData.discount?.couponAmount {
            return preferenceAmount - couponAmount + chargeRuleAmount
        } else {
            return preferenceAmount + chargeRuleAmount
        }
    }

    var amountOff: Double {
        guard let discount = self.paymentData.discount else {
            return 0
        }
        return discount.couponAmount
    }

    var maxCouponAmount: Double? {
        if let maxCouponAmount = paymentData.campaign?.maxCouponAmount, maxCouponAmount > 0.0 {
            return maxCouponAmount
        }
        return nil
    }

    internal var chargeRuleAmount: Double {
        guard let rules = chargeRules else {
            return 0
        }
        for rule in rules {
            if rule.paymentTypeId == paymentData.paymentMethod?.paymentTypeId {
                return rule.amountCharge
            }
        }
        return 0
    }

    internal var chargeRuleLabel: String? {
        guard let rules = chargeRules else {
            return nil
        }
        
        for rule in rules {
            if rule.paymentTypeId == paymentData.paymentMethod?.paymentTypeId {
                return rule.label
            }
        }
        return nil
    }
    
    internal func getPaymentData() -> PXPaymentData {

        // Set total card amount with charges without discount
        if paymentData.transactionAmount == nil || paymentData.transactionAmount == 0 {
            
            if let paymentMethod = paymentData.paymentMethod,
               let paymentOptionId = paymentData.paymentOptionId,
               let amount = paymentConfigurationService.getAmount(paymentOptionId: paymentOptionId, paymentMethodId: paymentMethod.id, paymentTypeId: paymentMethod.paymentTypeId),
               let taxFreeAmount = paymentConfigurationService.getTaxFreeAmount(paymentOptionId: paymentOptionId, paymentMethodId: paymentMethod.id, paymentTypeId: paymentMethod.paymentTypeId),
               let noDiscountAmount = paymentConfigurationService.getNoDiscountAmount(paymentOptionId: paymentOptionId, paymentMethodId: paymentMethod.id, paymentTypeId: paymentMethod.paymentTypeId) {
                self.paymentData.transactionAmount = NSDecimalNumber(string: String(taxFreeAmount))
                self.paymentData.amount = amount
                self.paymentData.taxFreeAmount = taxFreeAmount
                self.paymentData.noDiscountAmount = noDiscountAmount
            } else {
                self.paymentData.transactionAmount = NSDecimalNumber(string: String(preferenceAmountWithCharges))
            }
        }
        return paymentData
    }
}

internal extension PXAmountHelper {
    static func getRoundedAmountAsNsDecimalNumber(amount: Double?, forInit: Bool = false) -> NSDecimalNumber {
         guard let targetAmount = amount else { return 0 }
         let decimalPlaces: Double = forInit ? 2 : Double(SiteManager.shared.getCurrency().getDecimalPlacesOrDefault())
         let amountRounded: Double = Double(round(pow(10, decimalPlaces) * Double(targetAmount)) / pow(10, decimalPlaces))
         let amountString = String(format: "%\(decimalPlaces / 10)f", amountRounded)
         return NSDecimalNumber(string: amountString)
     }
}

// MARK: Tracking usage
internal extension PXAmountHelper {
    func getDiscountCouponAmountForTracking() -> Decimal {
        guard let couponAmount = paymentData.getDiscount()?.getCouponAmount()?.decimalValue else { return 0 }
        if let amPaymentDataAmount = splitAccountMoney?.getDiscount()?.getCouponAmount() {
            return amPaymentDataAmount.decimalValue + couponAmount
        }
        return couponAmount
    }
}
<commit_msg>fix: Using NSDecimalNumbers for calculations on PXAmountHelper<commit_after>//
//  PXAmountHelper.swift
//  MercadoPagoSDK
//
//  Created by <NAME> on 29/5/18.
//  Copyright © 2018 MercadoPago. All rights reserved.
//

import Foundation

internal struct PXAmountHelper {

    internal let preference: PXCheckoutPreference
    private let paymentData: PXPaymentData
    internal let chargeRules: [PXPaymentTypeChargeRule]?
    internal let paymentConfigurationService: PXPaymentConfigurationServices
    internal var splitAccountMoney: PXPaymentData?

    init (preference: PXCheckoutPreference, paymentData: PXPaymentData, chargeRules: [PXPaymentTypeChargeRule]?, paymentConfigurationService: PXPaymentConfigurationServices, splitAccountMoney: PXPaymentData?) {
        self.preference = preference
        self.paymentData = paymentData
        self.chargeRules = chargeRules
        self.paymentConfigurationService = paymentConfigurationService
        self.splitAccountMoney = splitAccountMoney
    }

    internal var consumedDiscount: Bool {
        return paymentData.consumedDiscount ?? false
    }

    var discount: PXDiscount? {
        return paymentData.discount
    }

    var campaign: PXCampaign? {
        return paymentData.campaign
    }

    var preferenceAmount: Double {
        return self.preference.getTotalAmount()
    }

    var preferenceAmountWithCharges: Double {
        // preferenceAmount + chargeRuleAmount
        let decimalPreferenceAmount = PXAmountHelper.getRoundedAmountAsNsDecimalNumber(amount: preferenceAmount)
        let decimalChargeRuleAmount = PXAmountHelper.getRoundedAmountAsNsDecimalNumber(amount: chargeRuleAmount)
        
        let sum = decimalPreferenceAmount.adding(decimalChargeRuleAmount)
        
        guard let doubleSum = Double(sum.stringValue) else { return preferenceAmount + chargeRuleAmount }
        
        return doubleSum
    }
    
    var amount: Double? {
        return paymentData.amount
    }
    
    var taxFreeAmount: Double? {
        return paymentData.taxFreeAmount
    }
    
    var noDiscountAmount: Double? {
        return paymentData.noDiscountAmount
    }

    var amountToPay: Double {
        if let payerCost = paymentData.payerCost {
            return payerCost.totalAmount
        }
        return amountToPayWithoutPayerCost
    }

    var isSplitPayment: Bool {
        return splitAccountMoney != nil
    }

    func getAmountToPayWithoutPayerCost(paymentOptionID: String?, paymentMethodId: String?, paymentTypeId: String?) -> Double {
        guard let paymentMethodId = paymentMethodId,
              let amountFromPaymentMethod = paymentConfigurationService.getAmountToPayWithoutPayerCostForPaymentMethod(paymentOptionID: paymentOptionID, paymentMethodId: paymentMethodId, paymentTypeId: paymentTypeId) else {
            return amountToPayWithoutPayerCost
        }
        return amountFromPaymentMethod
    }

    private var amountToPayWithoutPayerCost: Double {
        if let couponAmount = paymentData.discount?.couponAmount {
            // preferenceAmount - couponAmount + chargeRuleAmount
            let decimalPreferenceAmount = PXAmountHelper.getRoundedAmountAsNsDecimalNumber(amount: preferenceAmount)
            let decimalCouponAmount = PXAmountHelper.getRoundedAmountAsNsDecimalNumber(amount: couponAmount)
            let decimalChargeRuleAmount = PXAmountHelper.getRoundedAmountAsNsDecimalNumber(amount: chargeRuleAmount)
            
            let sum = (decimalPreferenceAmount.subtracting(decimalCouponAmount)).adding(decimalChargeRuleAmount)
            
            guard let doubleSum = Double(sum.stringValue) else { return preferenceAmount + chargeRuleAmount }
            
            return doubleSum
        } else {
            // preferenceAmount + chargeRuleAmount
            return preferenceAmountWithCharges
        }
    }

    var amountOff: Double {
        guard let discount = self.paymentData.discount else {
            return 0
        }
        return discount.couponAmount
    }

    var maxCouponAmount: Double? {
        if let maxCouponAmount = paymentData.campaign?.maxCouponAmount, maxCouponAmount > 0.0 {
            return maxCouponAmount
        }
        return nil
    }

    internal var chargeRuleAmount: Double {
        guard let rules = chargeRules else {
            return 0
        }
        for rule in rules {
            if rule.paymentTypeId == paymentData.paymentMethod?.paymentTypeId {
                return rule.amountCharge
            }
        }
        return 0
    }

    internal var chargeRuleLabel: String? {
        guard let rules = chargeRules else {
            return nil
        }
        
        for rule in rules {
            if rule.paymentTypeId == paymentData.paymentMethod?.paymentTypeId {
                return rule.label
            }
        }
        return nil
    }
    
    internal func getPaymentData() -> PXPaymentData {

        // Set total card amount with charges without discount
        if paymentData.transactionAmount == nil || paymentData.transactionAmount == 0 {
            
            if let paymentMethod = paymentData.paymentMethod,
               let paymentOptionId = paymentData.paymentOptionId,
               let amount = paymentConfigurationService.getAmount(paymentOptionId: paymentOptionId, paymentMethodId: paymentMethod.id, paymentTypeId: paymentMethod.paymentTypeId),
               let taxFreeAmount = paymentConfigurationService.getTaxFreeAmount(paymentOptionId: paymentOptionId, paymentMethodId: paymentMethod.id, paymentTypeId: paymentMethod.paymentTypeId),
               let noDiscountAmount = paymentConfigurationService.getNoDiscountAmount(paymentOptionId: paymentOptionId, paymentMethodId: paymentMethod.id, paymentTypeId: paymentMethod.paymentTypeId) {
                self.paymentData.transactionAmount = NSDecimalNumber(string: String(taxFreeAmount))
                self.paymentData.amount = amount
                self.paymentData.taxFreeAmount = taxFreeAmount
                self.paymentData.noDiscountAmount = noDiscountAmount
            } else {
                self.paymentData.transactionAmount = NSDecimalNumber(string: String(preferenceAmountWithCharges))
            }
        }
        return paymentData
    }
}

internal extension PXAmountHelper {
    static func getRoundedAmountAsNsDecimalNumber(amount: Double?, forInit: Bool = false) -> NSDecimalNumber {
         guard let targetAmount = amount else { return 0 }
         let decimalPlaces: Double = forInit ? 2 : Double(SiteManager.shared.getCurrency().getDecimalPlacesOrDefault())
         let amountRounded: Double = Double(round(pow(10, decimalPlaces) * Double(targetAmount)) / pow(10, decimalPlaces))
         let amountString = String(format: "%\(decimalPlaces / 10)f", amountRounded)
         return NSDecimalNumber(string: amountString)
     }
}

// MARK: Tracking usage
internal extension PXAmountHelper {
    func getDiscountCouponAmountForTracking() -> Decimal {
        guard let couponAmount = paymentData.getDiscount()?.getCouponAmount()?.decimalValue else { return 0 }
        if let amPaymentDataAmount = splitAccountMoney?.getDiscount()?.getCouponAmount() {
            return amPaymentDataAmount.decimalValue + couponAmount
        }
        return couponAmount
    }
}

{'fraction_non_alphanumeric': 0.06398591135896683, 'fraction_numerical': 0.0035221602582917524, 'mean_word_length': 2.7597241379310344, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '673307', 'n_tokens_mistral': 3507, 'n_tokens_neox': 3391, 'n_words': 781}