public string ExecuteCompleteCheckout(
            ParseUser loggedInUser,
            CheckoutViewModel checkoutViewModel, 
            string worldId, 
            Purchase[] userHistoricalPurchases, out string paymentUrl)
        {
            paymentUrl = string.Empty;
            var completeCheckoutLetterDto = new CompleteCheckoutLetterDto();
            var completeCheckoutStatementsArray = new List<CheckoutStatementBase>();
            var checkoutItemLetterDtoArray = new List<CheckoutItemLetterDto>();
            var accountStatementsBuilder = new AccountStatementBuilder(_repository, _currencyRetriver);
            var checkoutManager = new CheckoutManager(_repository, loggedInUser, checkoutViewModel);

            try
            {
                CreatePaymentRecords(checkoutViewModel, worldId, userHistoricalPurchases, checkoutManager,completeCheckoutLetterDto, checkoutItemLetterDtoArray, completeCheckoutStatementsArray);
                SetLetters(loggedInUser,checkoutViewModel,completeCheckoutStatementsArray,checkoutItemLetterDtoArray,completeCheckoutLetterDto,loggedInUser);

                if (checkoutViewModel.PaymentTransactionForCalc == 0)
                {
                    checkoutManager.SavePaymentRecords(completeCheckoutStatementsArray, accountStatementsBuilder, checkoutViewModel);
                    InitPuchaseView(loggedInUser, checkoutViewModel);
                }
                else
                {
                    _state.Set(STATE_KEY,completeCheckoutStatementsArray);

                    paymentUrl = GetPaymentUrl(checkoutManager,checkoutViewModel);
                }
            }
            catch(Exception ex)
            {
                BL.DomainServices.Log.LoggerFactory.GetLogger().LogError(ex);
                Mailer.SendCheckoutError(loggedInUser, checkoutManager.EventId, "Check out error");
                throw;
            }

            return checkoutManager.EventId;
        }
        public void ExecutePayment(string payerId, string paymentId, CheckoutViewModel checkoutViewModel, ParseUser loggedInUser)
        {
            var completeCheckoutStatementsArray = _state.Get<List<CheckoutStatementBase>>(STATE_KEY);
            var eventId = completeCheckoutStatementsArray.OfType<TransactionStatement>().First().Event;

            var executePayment = _payPalExpressCheckoutManager.ExecutePayment(payerId, paymentId);
            if (executePayment == null)
            {
                Mailer.SendCheckoutError(loggedInUser, eventId,"payment failed, database not upadated");
                return;
            }

            try
            {
                var accountStatementsBuilder = new AccountStatementBuilder(_repository, _currencyRetriver);
                var checkoutManager = new CheckoutManager(_repository, loggedInUser, checkoutViewModel);

                checkoutManager.EventId = eventId;
                checkoutManager.SavePaymentRecords(completeCheckoutStatementsArray, accountStatementsBuilder, checkoutViewModel, executePayment.ConvertToJson());
                InitPuchaseView(loggedInUser, checkoutViewModel);
            }
            catch (Exception ex)
            {
                Mailer.SendCheckoutError(loggedInUser, eventId, "Payment succeeded, Data Base update failed");
                BL.DomainServices.Log.LoggerFactory.GetLogger().LogError(ex);
                throw;
            }
        }
        private void SiteAccountCreditStatement(CreateCouponViewModel createCouponVm, AccountStatementBuilder accountStatementBuilder, double amountForSiteAccountCredit, List<AccountStatementDto> accountStatements, ParseUser siteAccount, CurrencyRetriver currencyRetriever)
        {
            var siteAccountCreditStatement =  accountStatementBuilder.SetAccountStatement(
                siteAccount,
                amountForSiteAccountCredit,
                0,
                TransactionType.CouponSiteCredit,
                DateTime.Now,
                currencyRetriever.GetCurrent());

            createCouponVm.SiteAccountBalance = siteAccount.GetPointerObject<UserAdminData>("adminData").Balance;
            accountStatements.Add(siteAccountCreditStatement);
        }
        private  AccountStatementDto TeacherAccountCreditStatement(CreateCouponViewModel createCouponVm, AccountStatementBuilder accountStatementBuilder, 
            double paymentTransaction, List<AccountStatementDto> accountStatements, ParseUser teacher, CurrencyRetriver currencyRetriever)
        {
            var currentCurrencyDto = currencyRetriever.GetCurrent();
            // add creding + debit record for teacher            
            var teacherAccountCreditStatement =  accountStatementBuilder.SetAccountStatement(
                teacher,
                paymentTransaction, 
                0,
                TransactionType.CouponBuyerPayment,
                DateTime.Now,
                currentCurrencyDto);

            var teacherAccountDebitStatement =  accountStatementBuilder.SetAccountStatement(
                teacher, 
                0,
                createCouponVm.TeacherData.GapToPay, 
                TransactionType.CouponBuyerDebit,
                DateTime.Now,
                currentCurrencyDto);

            createCouponVm.TeacherData.Balance = teacher.GetPointerObject<UserAdminData>("adminData").Balance;
            accountStatements.AddRange(new[]
            {
                teacherAccountCreditStatement,
                teacherAccountDebitStatement,
            });
            return teacherAccountCreditStatement;
        }
        private static void VatCreditStatement(CreateCouponViewModel createCouponVm, double maamPaymentCredit, AccountStatementBuilder accountStatementBuilder, List<AccountStatementDto> accountStatements, AccountStatementDto teacherAccountCreditStatement, double maam, double maamFromBalance, ParseUser maamAccount, ParseUser maamBalanceAccount, CurrencyRetriver currencyRetriever)
        {
            if (maamPaymentCredit > 0)
            {
                // add record for mamm account
                var maamAccountCreditStatement =  accountStatementBuilder.SetAccountStatement(
                   maamAccount,
                    maamPaymentCredit,
                    0,
                    TransactionType.CouponVatCredit,
                    DateTime.Now,
                    currencyRetriever.GetCurrent());
                
                accountStatements.Add(maamAccountCreditStatement);
                createCouponVm.MaamAccountBalance = maamAccount.GetPointerObject<UserAdminData>("adminData").Balance;
                if (teacherAccountCreditStatement != null)
                {
                    teacherAccountCreditStatement.IncludingVat = maam;
                }
            }

            if (maamFromBalance > 0)
            {
                // add record for mamm balance account מעמ יתרות
                var maamBalanceAccountCreditStatement =  accountStatementBuilder.SetAccountStatement(
                   maamBalanceAccount,
                    maamFromBalance,
                    0,
                    TransactionType.CouponVatCredit,
                    DateTime.Now,
                    currencyRetriever.GetCurrent());

                accountStatements.Add(maamBalanceAccountCreditStatement);
                createCouponVm.MaamBalanceAccountBalance = maamBalanceAccount.GetPointerObject<UserAdminData>("adminData").Balance;
            }
        }
        private void AgentCreditStatement(CreateCouponViewModel createCouponVm, IMyMentorRepository repository, ParseUser agentAccount, ParseUser teacher, ParseUser siteAccount, CommissionsDto globalCommissionsTable, AccountStatementBuilder accountStatementBuilder, List<AccountStatementDto> accountStatements, CurrencyRetriver currencyRetriever)
        {
            if (agentAccount == null)
            {
                //agent user not found ,send email
                //var missingAgent = teacher.GetPointerObject<UserAdminData>("adminData").GetPointerObject<ParseUser>("agent");
                SendErrorEmail(createCouponVm,MyMentorResources.couponErrAgentNotFound);
               // Mailer.SendAgentNotFound(missingAgent.Username,teacher.Username,createCouponVm.EventId);
                return;
            }

            var userAdminData = agentAccount.GetPointerObject<UserAdminData>("adminData");
            var acpAgentCommission = userAdminData.AcpTeacherCommission == Global.NoCommission ? 
                globalCommissionsTable.AgentCommission :
                userAdminData.AcpTeacherCommission;

            var agentSugOsek         = agentAccount.GetPointerObject<SugOsek>("sugOsek");
            var agentGetVat          = agentSugOsek.GetVat ? 1: 0;
            var agentPayVat          = agentSugOsek.PayVat ? 1 :0;
          
            var teacherSugOsek       = teacher.GetPointerObject<SugOsek>("sugOsek");
            var teacherPayVat        = teacherSugOsek.PayVat? 1: 0;            
            var maamFlag             = teacherPayVat == agentGetVat  ? 0 : 1;
            var teachertCurrency     = teacher.GetPointerObject<Currency>("currency").ConvertToCurrencyDto();
            var agentCurrency        = agentAccount.GetPointerObject<Currency>("currency").ConvertToCurrencyDto();
            var emlatHamaraFlag      = agentCurrency.ObjectId != teachertCurrency.ObjectId;
            var conversionCommission = globalCommissionsTable.ConversionCommission;
            var maam                 = globalCommissionsTable.Maam;
            var gapToPay             = createCouponVm.TeacherData.GapToPay;
            var totalCommission      = (gapToPay + (maamFlag * ((-gapToPay/(100 +maam)* maam * teacherPayVat)+(gapToPay*maam/100 *agentGetVat)))) * acpAgentCommission;            
            var agentIncludingVat    = agentSugOsek.GetVat ? maam : 0;
            double emlatHamara;
            
            // זיכוי סוכן
            var agentCreditAccountStatement = accountStatementBuilder.SetAccountStatement(
                agentAccount,
                totalCommission,
                0,
                TransactionType.CouponAgentCommission,
                DateTime.Now,
                currencyRetriever.GetCurrent(),
                includingVAT: agentIncludingVat);
            accountStatements.Add(agentCreditAccountStatement);
            createCouponVm.AgentBalance = agentAccount.GetPointerObject<UserAdminData>("adminData").Balance;

            // חיוב סוכן בעמלת המרה
            if (emlatHamaraFlag)
            {
                emlatHamara = totalCommission*conversionCommission;
                var agentDebitAccountStatement = accountStatementBuilder.SetAccountStatement(
                    agentAccount,
                    0,
                    emlatHamara,
                    TransactionType.CouponAgentExCommission,
                    DateTime.Now,
                    currencyRetriever.GetCurrent(),
                    includingVAT: agentIncludingVat);

                accountStatements.Add(agentDebitAccountStatement);
                createCouponVm.AgentBalance = agentAccount.GetPointerObject<UserAdminData>("adminData").Balance;
            }

            //חיוב האתר בעמלת הסוכן
            maamFlag = 0 == teacherPayVat ? 0 : 1;
            totalCommission = (gapToPay + (maamFlag * ((-gapToPay / (100 + maam) * maam * teacherPayVat) + (gapToPay * maam / 100 * 0)))) * acpAgentCommission;
            var siteAccountDebitStatement = accountStatementBuilder.SetAccountStatement(
                siteAccount,
                0,
                totalCommission,
                TransactionType.CouponAgentCommission,
                DateTime.Now,
                currencyRetriever.GetCurrent());
            accountStatements.Add(siteAccountDebitStatement);
            createCouponVm.SiteAccountBalance = siteAccount.GetPointerObject<UserAdminData>("adminData").Balance;

            if (emlatHamaraFlag)
            {
                //זיכוי האתר בעמלת המרה של הסוכן
                emlatHamara = totalCommission*conversionCommission;
                siteAccountDebitStatement = accountStatementBuilder.SetAccountStatement(
                    siteAccount,
                    emlatHamara,
                    0,
                    TransactionType.CouponAgentExCommission,
                    DateTime.Now,
                    currencyRetriever.GetCurrent());
                accountStatements.Add(siteAccountDebitStatement);
                createCouponVm.SiteAccountBalance = siteAccount.GetPointerObject<UserAdminData>("adminData").Balance; ;
            }
        }
        private async Task CreatePaymentTransaction(IMyMentorRepository repository, CreateCouponViewModel createCouponVm)
        {
            var currencyRetriever =  new CurrencyRetriver(HttpContext, Session, repository);
            var globalCommissionsTable =  repository.FindGlobalTeacherCommission();

            var maam                        = globalCommissionsTable.Maam;
            var paysMaam                    = createCouponVm.TeacherData.LivesInIsrael;
            var paymentTransaction          = createCouponVm.TeacherData.AmountForPayment;            

            var gapToPay                    = createCouponVm.TeacherData.GapToPay;
            var offsetFromBalance           = gapToPay > paymentTransaction ? gapToPay - paymentTransaction : 0;
            var maamPaymentCredit           = paysMaam ? (paymentTransaction * maam)/(100 + maam) : 0;
            var maamFromBalance             = paysMaam ? offsetFromBalance * maam / (100 + maam) : 0;
            var maamHitkabel                = maamFromBalance + maamPaymentCredit;
            var amountForSiteAccountCredit  = createCouponVm.TeacherData.GapToPay - maamHitkabel;
            
            var transactionUsers            = GetTransactionUsers(createCouponVm.TeacherData,globalCommissionsTable,repository);
            var teacher                     = transactionUsers.Single(x => x.ObjectId == createCouponVm.TeacherData.TeacherId);
            var siteAccount                 = transactionUsers.Single(x => x.ObjectId == globalCommissionsTable.SiteAccountId);
            var maamAccount                 = transactionUsers.Single(x => x.ObjectId == globalCommissionsTable.MaamAccountId);
            var maamBalanceAccount          = transactionUsers.Single(x=>x.ObjectId == globalCommissionsTable.MaamBalanceAccountId);
            var agentAccount                = transactionUsers.SingleOrDefault(x => x.ObjectId == createCouponVm.TeacherData.AgentId);

            var accountStatementBuilder     = new AccountStatementBuilder(repository, currencyRetriever)
            {
                CouponId = createCouponVm.CouponId,
                EventId = createCouponVm.EventId
            };

            var accountStatements = new List<AccountStatementDto>();
            var teacherAccountCreditStatement = TeacherAccountCreditStatement(createCouponVm, accountStatementBuilder, paymentTransaction, accountStatements, teacher, currencyRetriever);
            SiteAccountCreditStatement(createCouponVm, accountStatementBuilder, amountForSiteAccountCredit, accountStatements, siteAccount, currencyRetriever);

            if (paysMaam)
            {
                 VatCreditStatement(createCouponVm, maamPaymentCredit, accountStatementBuilder, accountStatements,teacherAccountCreditStatement, maam, maamFromBalance, maamAccount, maamBalanceAccount, currencyRetriever);
            }

            if (!string.IsNullOrEmpty(createCouponVm.TeacherData.AgentId))
            {
                AgentCreditStatement(createCouponVm, repository, agentAccount, teacher, siteAccount, globalCommissionsTable, accountStatementBuilder, accountStatements, currencyRetriever);
            }

            for (int i = 0; i < accountStatements.Count(); i++)
            {
                accountStatements[i].Order = i;
            }

            createCouponVm.AccountStatements = accountStatements.ToArray();
            await CreateUpdateEvent(createCouponVm, EventStatus.EventStarted, repository);
        }
        private AccountStatement AddStatementToUpdate(IMyMentorRepository repository, AccountStatementDto missingAccountStatement, AccountStatementBuilder accountStatementBuilder)
        {
            var asCurrency = repository.FindAllCurrencies(missingAccountStatement.CurrencyId);
            var statement = missingAccountStatement;
            var transactionCode = repository.FindTransactionTypesById(statement.TransactionTypeId).TransactionCode;
            AccountStatementDto accountStatementDto = null;
            var user = Task.Run(() => repository.FindUserWithAdminData(statement.UserId)).Result;

            accountStatementDto = Task.Run(() => accountStatementBuilder.SetAccountStatement(user,
               statement.Credit,
               statement.Debit,
               transactionCode,
               statement.DueDate,
               asCurrency.Single(),
               validationToken: statement.ValidationToken)).Result;

            //UserAdminData adminData;
            //using (var t = Task.Run(() => user.GetPointerObject<UserAdminData>("adminData")))
            //{
            //    adminData = t.Result;
            //}
            //adminData.Balance = accountStatementDto.Balance;
            //adminData.SaveAsync();
            return accountStatementDto.ConvertToAccountStatementDomain();
        }
        //public async Task FixRecords()
        //{
        //    var results =
        //        (await new ParseQuery<AccountStatement>().WhereEqualTo("currency", ParseObject.CreateWithoutData<Currency>("PrOfDBWHGg")).FindAsync()).ToArray();
        //    foreach (var result in results)
        //    {
        //        result["currency"] = ParseObject.CreateWithoutData<Currency>("K67StSNEYs");
        //        await result.SaveAsync();
        //    }            
        //}

        private void HandlePaymentRequiredRecovery(CreateCouponViewModel createCouponVm, EventDto eventsDto,
            IMyMentorRepository repository, CurrencyRetriver currencyRetriever, CurrencyDto currentCurrency)
        {
            if (createCouponVm.PaymentRequired && string.IsNullOrEmpty(createCouponVm.PaymentData))
            {
                var coupon = ParseObject.CreateWithoutData<Coupon>(createCouponVm.CouponId);
                coupon.CouponStatus = BL.Consts.CouponStatus.Pending;
                coupon.SaveAsync();
                UpdatePaymentNotRecieved(eventsDto, repository);
                return;
            }

            var accountStatementBuilder = new AccountStatementBuilder(repository, currencyRetriever)
            {
                CouponId = createCouponVm.CouponId,
                EventId = createCouponVm.EventId
            };

            var missingAccountStatements = GetMissingAccountStatements(createCouponVm, eventsDto);
            var accountStatementsByUser = missingAccountStatements.GroupBy(x=>x.UserId);
            var accountStatementsToUpdate = new List<AccountStatement>();
            foreach (var accountStatementByUser in accountStatementsByUser)
            {
                var user = Task.Run(() =>
                {
                    var userId = accountStatementByUser.Key;
                    return repository.FindUserWithAdminData(userId);
                }).Result;

                foreach (var accountStatement in accountStatementByUser)
                {
                    var transactionCode = repository.FindTransactionTypesById(accountStatement.TransactionTypeId).TransactionCode;
                    var asCurrency = repository.FindAllCurrencies(accountStatement.CurrencyId);
                    var accountStatementDto = Task.Run(() => accountStatementBuilder.SetAccountStatement(user,
                           accountStatement.Credit,
                           accountStatement.Debit,
                           transactionCode,
                           accountStatement.DueDate,
                           asCurrency.Single(),
                           validationToken: accountStatement.ValidationToken)).Result;
                    accountStatementDto.Order = accountStatement.Order;
                    accountStatementsToUpdate.Add(accountStatementDto.ConvertToAccountStatementDomain());
                }
            }            

            UpdateCouponAccountStatementsAndEvent(createCouponVm, repository, accountStatementsToUpdate, eventsDto);
        }