/// <summary>
        /// Calculate the tax due
        /// </summary>
        /// <param name="calculatorSetting"></param>
        /// <param name="merchandise"></param>
        /// <param name="shipping"></param>
        /// <param name="insurance"></param>
        /// <param name="duty"></param>
        /// <param name="fees"></param>
        /// <returns></returns>
        public decimal CalculateTax(CalculatorSetting calculatorSetting, decimal merchandise, decimal shipping,
                                    decimal insurance, decimal duty, decimal fees)
        {
            decimal tax   = 0m;
            decimal total = merchandise;

            if (calculatorSetting.TaxCalculationMethod == Enumerations.CalculationMethodEnum.Cif)
            {
                total = merchandise + shipping + insurance;
            }

            if (calculatorSetting.TaxIncludesDuty)
            {
                total += duty;
            }

            if (calculatorSetting.TaxIncludesFees)
            {
                total += fees;
            }

            tax = total * calculatorSetting.TaxRate;

            if (tax < calculatorSetting.MinimumTaxPayable)
            {
                tax = calculatorSetting.MinimumTaxPayable;
            }

            if (calculatorSetting.MaximumTaxPayable > 0 && tax > calculatorSetting.MaximumTaxPayable)
            {
                tax = calculatorSetting.MaximumTaxPayable;
            }

            return(tax);
        }
        /// <summary>
        /// Calculate the duty
        /// </summary>
        /// <param name="calculatorSetting"></param>
        /// <param name="merchandise"></param>
        /// <param name="shipping"></param>
        /// <param name="insurance"></param>
        /// <returns></returns>
        public decimal CalculateDuty(CalculatorSetting calculatorSetting, decimal merchandise, decimal shipping,
                                     decimal insurance)
        {
            decimal duty  = 0;
            decimal total = merchandise;

            if (calculatorSetting.DutyCalculationMethod == Enumerations.CalculationMethodEnum.Cif)
            {
                // includes shipping and insurance
                total = merchandise + shipping + insurance;
            }

            if (total > calculatorSetting.DutyThreshold)
            {
                duty = total * calculatorSetting.DutyRate;
            }

            if (duty < calculatorSetting.MinimumDutyPayable)
            {
                duty = calculatorSetting.MinimumDutyPayable;
            }

            if (calculatorSetting.MaximumDutyPayable > 0 && duty > calculatorSetting.MaximumDutyPayable)
            {
                duty = calculatorSetting.MaximumDutyPayable;
            }

            return(duty);
        }
        /// <summary>
        /// Calculate the fees
        /// </summary>
        /// <param name="calculatorSetting"></param>
        /// <param name="merchandise"></param>
        /// <returns></returns>
        public decimal CalculateFees(CalculatorSetting calculatorSetting, decimal merchandise)
        {
            if (calculatorSetting.Fee > 0)
            {
                return(calculatorSetting.Fee);
            }

            if (calculatorSetting.FeeRate > 0)
            {
                // TODO: Possible fee CIF or other combination

                return(merchandise * calculatorSetting.FeeRate);
            }

            return(0M);
        }
        /// <summary>
        /// Calculate
        /// </summary>
        /// <param name="requestDto"></param>
        /// <returns></returns>
        public CalculateResponseDto Calculate(CalculateRequestDto requestDto)
        {
            CalculateResponseDto response;

            _calculator        = GetCalculator(requestDto.ShippingToCountryIso);
            _calculatorSetting = _calculator.CalculatorSetting;

            // DutyRate override
            if (requestDto.DutyRate != null && requestDto.DutyRate > 0)
            {
                _calculatorSetting.DutyRate = requestDto.DutyRate.GetValueOrDefault();
            }

            // TaxRate override
            if (requestDto.TaxRate != null && requestDto.TaxRate > 0)
            {
                _calculatorSetting.TaxRate = requestDto.TaxRate.GetValueOrDefault();
            }

            // intra region? Don't charge duty
            if (_dbContext.Countries.FirstOrDefault(x => x.Iso == requestDto.ShippingFromCountryIso)?.RegionId ==
                _dbContext.Countries.FirstOrDefault(x => x.Iso == requestDto.ShippingToCountryIso)?.RegionId)
            {
                _calculatorSetting.DutyApplicable = false;
            }

            if (_calculator.CalculatorSetting.DutyApplicable)
            {
                _duty = CalculateDuty(_calculatorSetting, requestDto.MerchandiseTotal, requestDto.ShippingTotal,
                                      requestDto.InsuranceTotal.GetValueOrDefault());
            }

            if (_calculatorSetting.FeesApplicable)
            {
                _fees = CalculateFees(_calculatorSetting, requestDto.MerchandiseTotal);
            }

            if (_calculatorSetting.TaxApplicable)
            {
                _tax = CalculateTax(_calculatorSetting, requestDto.MerchandiseTotal, requestDto.ShippingTotal,
                                    requestDto.InsuranceTotal.GetValueOrDefault(), _duty, _fees);
            }



            // For Canada
            if (requestDto.ShippingToCountryIso.ToUpper() == Enumerations.CountryEnum.CA.ToString())
            {
                return(_mapper.Map(_calculatorSetting, new CanadaCalculateResponseDto()
                {
                    Duty = _duty,
                    Tax = CalculateCanadianTax(requestDto.CanadianProvinceCode, requestDto.MerchandiseTotal, _duty,
                                               Enumerations.CanadianTaxTypeEnum.Gst),
                    Pst = CalculateCanadianTax(requestDto.CanadianProvinceCode, requestDto.MerchandiseTotal, _duty,
                                               Enumerations.CanadianTaxTypeEnum.Pst),
                    PstRate = GetCanadianPstRate(requestDto.CanadianProvinceCode)
                }));
            }

            // Standard response
            return(_mapper.Map(_calculatorSetting, new CalculateResponseDto()
            {
                Duty = _duty,
                Tax = _tax,
                Fees = _fees
            }));
        }