using AutoMapper;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using PacificPlace.ApiGateway.Config;
using PacificPlace.ApiGateway.Crm.Common;
using PacificPlace.ApiGateway.Crm.GraphQLQueries;
using PacificPlace.ApiGateway.Crm.Member.Models;
using PacificPlace.ApiGateway.Crm.Member.Services;
using PacificPlace.ApiGateway.Crm.Merchants.Models;
using PacificPlace.ApiGateway.Crm.Promotions.Services;
using PacificPlace.ApiGateway.Crm.Redemption.Models;
using PacificPlace.ApiGateway.Crm.Redemption.Queries;
using PacificPlace.ApiGateway.Crm.Redemption.QueryCriterias;
using PacificPlace.ApiGateway.Crm.Redemption.Repositories;
using PacificPlace.ApiGateway.Exceptions;
using PacificPlace.ApiGateway.Extensions;
using PacificPlace.ApiGateway.Infrastructure.ServiceClients.GraphQL;
using PacificPlace.ApiGateway.Infrastructure.ServiceClients.Hk1Crm;
using PacificPlace.ApiGateway.Infrastructure.ServiceClients.Tracking;
using SmartFormat;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace PacificPlace.ApiGateway.Crm.Redemption.Services
{
    public class RewardBySpendingService : IRewardBySpendingService
    {
        private readonly Hk1CrmClient _client;
        private readonly ILogger<RedemptionService> _logger;
        private readonly IMapper _mapper;
        private readonly IRedeemableItemTagManager _tagManager;
        private readonly IMemberService _memberService;
        private readonly IConfigurationDataRepository _configurationDataRepository;
        private readonly ITrackingServiceClient _trackingServiceClient;
        private readonly IGraphQLRunner _graphQLRunner;
        private readonly IResponseAggregatorService _computeService;
        private readonly IHk1CrmTokenContext _hk1CrmTokenContext;
        private readonly IPromotionService _promotionService;
        private readonly Hk1CrmConfig _config;

        public RewardBySpendingService(Hk1CrmClient client, IRedeemableItemTagManager tagManager, IMemberService memberService,
        IConfigurationDataRepository configurationDataRepository, ILogger<RedemptionService> logger, IMapper mapper,
        ITrackingServiceClient trackingServiceClient, IGraphQLRunner graphQLRunner, IResponseAggregatorService computeService,
        IHk1CrmTokenContext hk1CrmTokenContext, IPromotionService promotionService, IOptions<Hk1CrmConfig> config)
        {
            _client = client;
            _logger = logger;
            _mapper = mapper;
            _tagManager = tagManager;
            _memberService = memberService;
            _configurationDataRepository = configurationDataRepository;
            _trackingServiceClient = trackingServiceClient;
            _graphQLRunner = graphQLRunner;
            _computeService = computeService;
            _hk1CrmTokenContext = hk1CrmTokenContext;
            _promotionService = promotionService;
            _config = config.Value;
        }

        public async Task<IEnumerable<RewardBySpendingModel>> GetRewardsBySpending(RewardBySpendingQueryCriteria queryCriteria, string lang, string contactIdentifier, IEnumerable<string> cmsIds = null, GetRewardsBySpendingParams parameters = null, string channel = null)
        {
            var response = await _client.GetRewardBySpendingAsync(parameters);

            if (response.IsError)
            {
                throw new Hk1CrmException(response.Message, response.Error);
            }

            if (response.Data == null || !response.Data.Any())
            {
                return Enumerable.Empty<RewardBySpendingModel>();
            }
            
            var crmRewardBySpendings = FilterOutUnavailableReasons(response);
            if (CrmConstants.Channels.WMP.Equals(channel, StringComparison.InvariantCultureIgnoreCase))
            {
                crmRewardBySpendings = FilterOutOfficeExclusive(crmRewardBySpendings).ToList();
                crmRewardBySpendings = FilterOutEVCoupon(crmRewardBySpendings).ToList();
            }


            if (!crmRewardBySpendings.HasElement())
            {
                return Enumerable.Empty<RewardBySpendingModel>();
            }

            var earningCriteriaIds = crmRewardBySpendings.Where(x => x != null).Select(x => x.EarningCriteriaId).Distinct();
            var query = new RewardBySpendingPredicate { Ids = earningCriteriaIds };
            if (cmsIds != null && cmsIds.Any())
            {
                query.CmsIds = cmsIds;
            }

            _mapper.Map(queryCriteria, query);
            var unavailableReasons = crmRewardBySpendings.Select(x => x.UnavailableReason).Distinct().ToList();
            var predicate = new Predicate(ConditionDefaults.Or);
            var memberInfo = await _memberService.GetMemberInfoAsync();

            await _computeService.CreatePredicateAsync(lang, predicate, memberInfo);

            var categoryQuery = new CategoryQuery { Category = "SUB_CATEGORY", Type = "GIFT" };
            var builder = new GraphQueryBuilder()
            .From<RewardBySpendingQueryObject>()
            .SetLanguage(lang)
            .SetPredicate<RewardBySpendingPredicate>(RewardBySpendingQueryObject.Predicate, query, predicate)
            .SetQuery<GiftQuery>(RewardBySpendingQueryObject.Gifts, cfg => cfg.Ids = crmRewardBySpendings.SelectMany(x => x.GiftList.Select(x => x.GiftId)).Distinct())
            .SetQuery<ErrorCodeQuery>(RewardBySpendingQueryObject.ErrorCodes, cfg => cfg.Codes = unavailableReasons)
            .SetQuery<CategoryQuery>(RewardBySpendingQueryObject.Categories, categoryQuery)
            .SetQuery<MerchantQuery>(RewardBySpendingQueryObject.Merchants, cfg => cfg.Ids = crmRewardBySpendings.SelectMany(x => x.GiftList.Where(x => x.ProviderId.HasValue).Select(x => x.ProviderId.Value)).Distinct());
            if (queryCriteria != null)
            {
                builder.SetOrderBy("orderBy", new GraphOrderBy { OrderBy = queryCriteria.OrderBy, OrderMethod = queryCriteria.Order });
            }

            var graphQuery = builder.Build();
            var getCmsContent = _graphQLRunner.SendQueryAsync(graphQuery);
            var getFavoriteTask = _computeService.GetFavoriteCmsIds(contactIdentifier);
            await Task.WhenAll(getCmsContent, getFavoriteTask);
            var result = await getCmsContent;
            if (result.Status == GraphQLResult.Failed)
            {
                throw new BadRequestException(result.GetErrors());
            }

            var cmsRewardsBySpending = result.GetList<RewardBySpending>("..rewardBySpending.items.[*]");
            var cmsFilterDiplayChannels = cmsRewardsBySpending.FilterDisplayChannel(channel, _config);
            var cmsGifts = result.GetList<Gift>("..gifts.items.[*]");
            var cmsErrorCodes = result.GetList<ErrorCode>("..errorCodes.[*]");
            var cmsSubCategories = result.GetList<Category>("..categories.[*]");
            var cmsMemberTiers = result.GetList<Tier>("..memberTiers.items.[*]");
            var cmsMerchants = _computeService.GetAllMerchant(result);
            var favoriteItemIds = await getFavoriteTask;
            var filteredRbsList = new List<RewardBySpendingModel>();
            var crmRewardAsDict = crmRewardBySpendings.GroupBy(x => x.EarningCriteriaId).Select(x => x.First()).ToDictionary(x => x.EarningCriteriaId);
            var crmPromotionIds = crmRewardBySpendings.Select(r => r.PromotionId);
            var cmsPromotions = await _promotionService.GetCmsPromotions(lang, crmPromotionIds, null);

            foreach (var cmsRewardBySpending in cmsFilterDiplayChannels)
            {
                if (!crmRewardAsDict.ContainsKey(cmsRewardBySpending.CrmId.Value))
                {
                    continue;
                }

                var apiReward = crmRewardAsDict[cmsRewardBySpending.CrmId.Value];
                DateTime.TryParse(cmsRewardBySpending.DisplayStartDate, out var displayStartDateFormarted);
                if (displayStartDateFormarted != DateTime.MinValue && displayStartDateFormarted.ConvertToDateFromDateTime() > DateTime.Now.ConvertToDateFromDateTime())
                {
                    continue;
                }

                DateTime.TryParse(cmsRewardBySpending.DisplayEndDate, out var displayEndDateFormarted);
                if (displayEndDateFormarted != DateTime.MinValue && displayEndDateFormarted.ConvertToDateFromDateTime() < DateTime.Now.ConvertToDateFromDateTime())
                {
                    continue;
                }

                var rModel = _computeService.ComputeRewardBySpendingModel(cmsRewardBySpending, apiReward, cmsGifts, favoriteItemIds);
                if (queryCriteria != null && queryCriteria.GiftSubCategory != null && queryCriteria.GiftSubCategory.Any())
                {
                    if (rModel.Gifts == null || !rModel.Gifts.Any(gift => queryCriteria.GiftSubCategory.Any(criteriaCategory => criteriaCategory == gift.SubCategory)))
                    {
                        continue;
                    }
                }

                if (queryCriteria != null && queryCriteria.ProviderId != null && queryCriteria.ProviderId.Any() && !_computeService.IsContainsProviderId(rModel.Gifts, queryCriteria.ProviderId))
                {
                    continue;
                }

                GetGiftSetItems(rModel, cmsSubCategories, cmsMerchants);

                if (!string.IsNullOrEmpty(rModel.UnavailableReason))
                {
                    var displayErrorMessage = cmsErrorCodes?.FirstOrDefault(x => x.Code.Equals(rModel.UnavailableReason))?.DisplayErrorMessage;
                    rModel.DisplayUnavailableReason = displayErrorMessage;
                }

                rModel.TierList = _computeService.TierList(rModel.EligibleTierList, cmsMemberTiers);
                var promotion = cmsPromotions?.FirstOrDefault(p => p.PromotionId == rModel.PromotionId);
                if (promotion != null)
                {
                    rModel.PromotionName = promotion.DisplayPromotionName;
                    rModel.CmsId_Happening = promotion.RelatedHappeningArticle?.ItemId;
                    rModel.Url_Happening = promotion.RelatedHappeningArticle?.Url.ToSEOFriendlyString();
                }

                filteredRbsList.Add(rModel);
            }

            await ComputeTagsAsync(filteredRbsList, memberInfo, lang);
            //Computer Sorting level base on Tag, OverallAvailability and UnavailableReason
            filteredRbsList.ForEach(rbs => rbs.ComputeSortingLevel());

            var sortedRbsList = string.IsNullOrWhiteSpace(queryCriteria?.OrderBy)
                    ? ApplyDefaultSorting(filteredRbsList)
                    : filteredRbsList;

            return sortedRbsList;
        }
        private List<ApiRewardBySpending> FilterOutUnavailableReasons(Hk1CrmDefaultResponse<ICollection<ApiRewardBySpending>> response)
        {
            var unavailableReasons = new List<string>()
            {
                CrmConstants.UnavailableReasons.WelcomeOfferNotMatch,
                CrmConstants.UnavailableReasons.BirthDayOfferNotMatch,
                CrmConstants.UnavailableReasons.NotInAlowedMemberList,
                CrmConstants.UnavailableReasons.RedemptionCycleSequenceNotMatch,
                CrmConstants.UnavailableReasons.InvalidRequest,
                CrmConstants.UnavailableReasons.GiftExpired,
                CrmConstants.UnavailableReasons.UnexpectedServerError,
                CrmConstants.UnavailableReasons.ReceiptIsUsed,
                CrmConstants.UnavailableReasons.ExceededMaxNumberOfReceipt,
                CrmConstants.UnavailableReasons.OfficeWorkerOnly,
                CrmConstants.UnavailableReasons.GeneralShopperOnly,
                CrmConstants.UnavailableReasons.QueryNotMatch,
                CrmConstants.UnavailableReasons.HasReservedCycleRBS,
                CrmConstants.UnavailableReasons.FromAssociatedMember,
                CrmConstants.UnavailableReasons.Overridden
            };

            return response.Data.Where(x => !unavailableReasons.Any(u => u.Equals(x.UnavailableReason, StringComparison.InvariantCultureIgnoreCase))).ToList();
        }

        public async Task<IEnumerable<RewardBySpendingModel>> GetRewardsBySpendingWithoutLogin(RewardBySpendingQueryCriteria queryCriteria, string lang, string contactIdentifier, IEnumerable<string> cmsIds = null, GetRewardsBySpendingParams parameters = null, string channel = null)
        {
            var query = new RewardBySpendingPredicate();
            if (cmsIds != null && cmsIds.Any())
            {
                query.CmsIds = cmsIds;
            }

            _mapper.Map(queryCriteria, query);
            var categoryQuery = new CategoryQuery { Category = "SUB_CATEGORY", Type = "GIFT" };
            var builder = new GraphQueryBuilder()
            .From<RewardBySpendingQueryObject>()
            .SetLanguage(lang)
            .SetPredicate<RewardBySpendingPredicate>(RewardBySpendingQueryObject.Predicate, query)
            .SetQuery<CategoryQuery>(RewardBySpendingQueryObject.Categories, categoryQuery);
            if (queryCriteria != null)
            {
                builder.SetOrderBy("orderBy", new GraphOrderBy { OrderBy = queryCriteria.OrderBy, OrderMethod = queryCriteria.Order });
            }

            var graphQuery = builder.Build();
            var getCmsContent = _graphQLRunner.SendQueryAsync(graphQuery);
            await Task.WhenAll(getCmsContent);
            var result = await getCmsContent;
            if (result.Status == GraphQLResult.Failed)
            {
                throw new BadRequestException(result.GetErrors());
            }

            var cmsRewardsBySpendings = result.GetList<RewardBySpending>("..rewardBySpending.items.[*]");
            var cmsFilters = cmsRewardsBySpendings.FilterDisplayChannel(channel, _config);
            var cmsGifts = result.GetList<Gift>("..gifts.items.[*]");
            var cmsErrorCodes = result.GetList<ErrorCode>("..errorCodes.[*]");
            var cmsSubCategories = result.GetList<Category>("..categories.[*]");
            var cmsMemberTiers = result.GetList<Tier>("..memberTiers.items.[*]");
            var cmsMerchants = _computeService.GetAllMerchant(result);
            var orderedList = new List<RewardBySpendingModel>();
            var cmsPromotionIds = cmsFilters.Where(x => x.PromotionId.HasValue).Select(r => r.PromotionId.Value);
            var cmsPromotions = await _promotionService.GetCmsPromotions(lang, cmsPromotionIds, null);
            var tags = await _configurationDataRepository.GetTagsAsync(lang);
            if (CrmConstants.Channels.WMP.Equals(channel, StringComparison.InvariantCultureIgnoreCase))
            {
                cmsFilters = FilterOutOfficeExclusiveNoneLogin(cmsRewardsBySpendings).ToList();
            }

            if (cmsFilters != null)
            {
                foreach (var cmsRewardBySpending in cmsFilters)
                {
                    DateTime.TryParse(cmsRewardBySpending.DisplayStartDate, out var displayStartDateFormarted);
                    if (displayStartDateFormarted != DateTime.MinValue && displayStartDateFormarted.ConvertToDateFromDateTime() > DateTime.Now.ConvertToDateFromDateTime())
                    {
                        continue;
                    }

                    DateTime.TryParse(cmsRewardBySpending.DisplayEndDate, out var displayEndDateFormarted);
                    if (displayEndDateFormarted != DateTime.MinValue && displayEndDateFormarted.ConvertToDateFromDateTime() < DateTime.Now.ConvertToDateFromDateTime())
                    {
                        continue;
                    }

                    var rModel = _mapper.Map<RewardBySpendingModel>(cmsRewardBySpending);
                    rModel.EarningCriteriaEligibleGuestType = cmsRewardBySpending?.PromotionEarningCriteriaEligibleGuestType?.Split(",").ToList();
                    if (queryCriteria != null && queryCriteria.GiftSubCategory != null && queryCriteria.GiftSubCategory.Any())
                    {
                        if (rModel.Gifts == null || !rModel.Gifts.Any(gift => queryCriteria.GiftSubCategory.Any(criteriaCategory => criteriaCategory == gift.SubCategory)))
                        {
                            continue;
                        }
                    }

                    if (queryCriteria != null && queryCriteria.ProviderId != null && queryCriteria.ProviderId.Any() && !_computeService.IsContainsProviderId(rModel.Gifts, queryCriteria.ProviderId))
                    {
                        continue;
                    }

                    var giftList = new List<GiftSetItemModel>();
                    foreach (var gift in rModel.Gifts)
                    {
                        var cmsGift = cmsGifts.FirstOrDefault(x => x.CrmId == gift.CrmId);
                        if (cmsGift != null)
                        {
                            giftList.Add(_mapper.Map<GiftSetItemModel>(cmsGift));
                        }
                    }

                    rModel.Gifts = giftList;
                    GetGiftSetItems(rModel, cmsSubCategories, cmsMerchants);
                    rModel.FavouriteStatus = true;
                    if (!string.IsNullOrEmpty(rModel.UnavailableReason))
                    {
                        var displayErrorMessage = cmsErrorCodes?.FirstOrDefault(x => x.Code.Equals(rModel.UnavailableReason))?.DisplayErrorMessage;
                        rModel.DisplayUnavailableReason = displayErrorMessage;
                    }

                    rModel.TierList = _computeService.TierList(rModel.EligibleTierList, cmsMemberTiers);
                    var promotion = cmsPromotions?.FirstOrDefault(p => p.PromotionId == rModel.PromotionId);
                    if (promotion != null)
                    {
                        rModel.PromotionName = promotion.DisplayPromotionName;
                        rModel.CmsId_Happening = promotion.RelatedHappeningArticle?.ItemId;
                        rModel.Url_Happening = promotion.RelatedHappeningArticle?.Url.ToSEOFriendlyString();
                    }

                    var rewardTags = new List<ItemTagModel>();
                    var rewardTagEnums = _tagManager.ComputeRewardTags(rModel);
                    foreach (var tagEnum in rewardTagEnums)
                    {
                        rewardTags.Add(MapToEcTag(tagEnum, tags));
                    }

                    rModel.RewardTags = rewardTags;

                    orderedList.Add(rModel);
                }
            }

            return orderedList;
        }

        public async Task<RewardBySpendingModel> GetRewardBySpending(int id, string lang, string contactIdentifier)
        {
            var response = await _client.GetRewardBySpendingAsync();

            if (response.IsError)
            {
                throw new Hk1CrmException(response.Message, response.Error);
            }

            if (response.Data == null || !response.Data.Any())
            {
                return null;
            }

            var rewardsBySpending = response.Data;
            var crmRewardBySpending = rewardsBySpending.FirstOrDefault(x => x != null && x.EarningCriteriaId == id);
            if (crmRewardBySpending == null)
            {
                return null;
            }

            var earningCriteriaIds = new List<long> { crmRewardBySpending.EarningCriteriaId };
            var giftIds = crmRewardBySpending?.GiftList.Select(x => x.GiftId).Distinct();
            var providerIds = crmRewardBySpending?.GiftList.Where(x => x.ProviderId.HasValue).Select(x => x.ProviderId.Value).Distinct();
            var memberInfo = await _memberService.GetMemberInfoAsync();
            var getFavoriteTask = _computeService.GetFavoriteCmsIds(contactIdentifier);
            var categoryQuery = new CategoryQuery { Category = "SUB_CATEGORY", Type = "GIFT" };

            var query = new GraphQueryBuilder()
            .From<RewardBySpendingQueryObject>()
            .SetLanguage(lang)
            .SetQuery<RewardBySpendingQuery>(RewardBySpendingQueryObject.RewardsBySpending, cfg => cfg.Ids = earningCriteriaIds)
            .SetQuery<GiftQuery>(RewardBySpendingQueryObject.Gifts, cfg => cfg.Ids = giftIds)
            .SetQuery<MerchantQuery>(RewardBySpendingQueryObject.Merchants, cfg => cfg.Ids = providerIds)
            .SetQuery<CategoryQuery>(RewardBySpendingQueryObject.Categories, categoryQuery)
            .Build();

            var getCmsContent = _graphQLRunner.SendQueryAsync(query);
            await Task.WhenAll(getCmsContent, getFavoriteTask);
            var result = await getCmsContent;
            var cmsRewardBySpending = result.GetList<RewardBySpending>("..rewardBySpending.items.[*]").FirstOrDefault();
            if (cmsRewardBySpending == null)
            {
                return null;
            }

            var cmsMerchants = _computeService.GetAllMerchant(result);
            var cmsGifts = result.GetList<Gift>("..gifts.items.[*]");
            var cmsSubCategories = result.GetList<Category>("..categories.[*]");
            var favoriteItemIds = await getFavoriteTask;
            var rModel = _computeService.ComputeRewardBySpendingModel(cmsRewardBySpending, crmRewardBySpending, cmsGifts, favoriteItemIds);
            await ComputeTagsAsync(new RewardBySpendingModel[] { rModel }, memberInfo, lang);
            var crmPromotionIds = new List<long>() { crmRewardBySpending.PromotionId };
            var cmsPromotions = await _promotionService.GetCmsPromotions(lang, crmPromotionIds, null);

            GetGiftSetItems(rModel, cmsSubCategories, cmsMerchants);

            var promotion = cmsPromotions?.FirstOrDefault(p => p.PromotionId == rModel.PromotionId);
            if (promotion != null)
            {
                rModel.PromotionName = promotion.DisplayPromotionName;
                rModel.CmsId_Happening = promotion.RelatedHappeningArticle?.ItemId;
                rModel.Url_Happening = promotion.RelatedHappeningArticle?.Url.ToSEOFriendlyString();
            }

            return rModel;
        }

        private async Task ComputeTagsAsync(IEnumerable<RewardBySpendingModel> rewards, MemberInfo memberInfo, string lang)
        {
            var tags = await _configurationDataRepository.GetTagsAsync(lang);
            var tiers = (await _configurationDataRepository.GetTiersAsync(lang)).OrderBy(x => x.Level);

            foreach (var reward in rewards)
            {
                if (reward == null)
                    continue;

                var computedTags = new List<ItemTagModel>();
                var computedTagEnums = _tagManager.ComputeRewardBySpendingTags(reward, memberInfo);
                foreach (var tagEnum in computedTagEnums)
                {
                    computedTags.Add(MapToEcTag(tagEnum, reward, memberInfo, tags, tiers));
                }

                reward.ComputedTags = computedTags;

                var rewardTags = new List<ItemTagModel>();
                var rewardTagEnums = _tagManager.ComputeRewardTags(reward);
                foreach (var tagEnum in rewardTagEnums)
                {
                    rewardTags.Add(MapToEcTag(tagEnum, tags));
                }

                reward.RewardTags = rewardTags;
            }
        }

        private ItemTagModel MapToEcTag(RewardTagEnum tagEnum, IEnumerable<RedeemableItemTag> tags)
        {
            var tag = tags.FirstOrDefault(r => string.Compare(r.Name, tagEnum.Name, true) == 0);

            if (tag == null)
            {
                return new ItemTagModel { Name = tagEnum.Name, DisplayName = tagEnum.Name };
            }

            return new ItemTagModel { Name = tag.Name, DisplayName = tag.DisplayName };
        }

        private ItemTagModel MapToEcTag(RewardBySpendingTagEnum tagEnum, RewardBySpendingModel rewardBySpending,
            MemberInfo memberInfo, IEnumerable<RedeemableItemTag> tags, IEnumerable<Tier> tiers)
        {
            var tag = tags.FirstOrDefault(r => string.Compare(r.Name, tagEnum.Name, true) == 0);

            if (tag == null)
            {
                return new ItemTagModel { Name = tagEnum.Name, DisplayName = tagEnum.Name };
            }

            var result = new ItemTagModel() { Name = tag.Name, DisplayName = tag.DisplayName, BackgroundImage = tag.BackgroundImage, CssClassDetailPage = tag.CssClassDetailPage, CssClassListingPage = tag.CssClassListingPage };

            if (tagEnum == RewardBySpendingTagEnum.EndSoon)
            {
                var daysLeft = (DateTimeOffset.Parse(rewardBySpending.PromotionActiveEndDate).Date - DateTimeOffset.Now.Date).TotalDays + 1;
                result.DisplayName = string.IsNullOrEmpty(tag.DisplayName) ? string.Empty : Smart.Format(tag.DisplayName, daysLeft);

                return result;
            }
            else if (tagEnum == RewardBySpendingTagEnum.NextTier)
            {
                var eligibleTiersStringList = rewardBySpending.EligibleTier?.ToLower().Split(',', StringSplitOptions.RemoveEmptyEntries);
                var currentTier = tiers.FirstOrDefault(x => string.Equals(x.Code, memberInfo?.CurrentTier, StringComparison.OrdinalIgnoreCase));
                foreach (var tier in tiers)
                {
                    if (!eligibleTiersStringList.Contains(tier.Code.ToLower()))
                    {
                        continue;
                    }

                    if (currentTier != null && tier.Level < currentTier.Level)
                    {
                        continue;
                    }

                    if (tier.Code.Equals(CrmConstants.MemberTierCodes.Blue))
                    {
                        var nextTierTag = tags.FirstOrDefault(x => x.Name.Equals(CrmConstants.Tags.NextTierBlue));
                        if (nextTierTag == null)
                        {
                            continue;
                        }

                        return _mapper.Map<ItemTagModel>(nextTierTag);
                    }

                    if (tier.Code.Equals(CrmConstants.MemberTierCodes.Gold))
                    {
                        var nextTierTag = tags.FirstOrDefault(x => x.Name.Equals(CrmConstants.Tags.NextTierGold));
                        if (nextTierTag == null)
                        {
                            continue;
                        }

                        return _mapper.Map<ItemTagModel>(nextTierTag);
                    }

                    if (tier.Code.Equals(CrmConstants.MemberTierCodes.Black))
                    {
                        var nextTierTag = tags.FirstOrDefault(x => x.Name.Equals(CrmConstants.Tags.NextTierBlack));
                        if (nextTierTag == null)
                        {
                            continue;
                        }

                        return _mapper.Map<ItemTagModel>(nextTierTag);
                    }

                    if (tier.Code.Equals(CrmConstants.MemberTierCodes.Platinum))
                    {
                        var nextTierTag = tags.FirstOrDefault(x => x.Name.Equals(CrmConstants.Tags.NextTierPlatinum));
                        if (nextTierTag == null)
                        {
                            continue;
                        }

                        return _mapper.Map<ItemTagModel>(nextTierTag);
                    }
                }

                //Eligible Tiers not matched Next Tier of member
                var nextTier = tiers.FirstOrDefault(x => string.Equals(x.Code, memberInfo?.NextTier, StringComparison.OrdinalIgnoreCase));
                if (nextTier == null || (nextTier != null && eligibleTiersStringList.HasElement() && !eligibleTiersStringList.Contains(nextTier.Code.ToLower())))
                {
                    var eligibleTiers = tiers.Where(t => eligibleTiersStringList.Contains(t.Code.ToLower())).Select(t => t);
                    var eligibleLowestTier = eligibleTiers?.OrderBy(t => t.Level).FirstOrDefault();
                    if (eligibleLowestTier != null)
                    {
                        RedeemableItemTag dedicatedTier = null;
                        if (eligibleLowestTier.Code.Equals(CrmConstants.MemberTierCodes.PreMember, StringComparison.OrdinalIgnoreCase))
                        {
                            dedicatedTier = tags.FirstOrDefault(x => x.Name.Equals(CrmConstants.Tags.DedicatedPrememberTier, StringComparison.OrdinalIgnoreCase));
                            if (dedicatedTier != null)
                            {
                                return _mapper.Map<ItemTagModel>(dedicatedTier);
                            }
                        }

                        if (eligibleLowestTier.Code.Equals(CrmConstants.MemberTierCodes.Blue, StringComparison.OrdinalIgnoreCase))
                        {
                            dedicatedTier = tags.FirstOrDefault(x => x.Name.Equals(CrmConstants.Tags.DedicatedBlueTier, StringComparison.OrdinalIgnoreCase));
                            if (dedicatedTier != null)
                            {
                                return _mapper.Map<ItemTagModel>(dedicatedTier);
                            }
                        }

                        if (eligibleLowestTier.Code.Equals(CrmConstants.MemberTierCodes.Gold, StringComparison.OrdinalIgnoreCase))
                        {
                            dedicatedTier = tags.FirstOrDefault(x => x.Name.Equals(CrmConstants.Tags.DedicatedGoldTier, StringComparison.OrdinalIgnoreCase));
                            if (dedicatedTier != null)
                            {
                                return _mapper.Map<ItemTagModel>(dedicatedTier);
                            }
                        }

                        if (eligibleLowestTier.Code.Equals(CrmConstants.MemberTierCodes.Platinum, StringComparison.OrdinalIgnoreCase))
                        {
                            dedicatedTier = tags.FirstOrDefault(x => x.Name.Equals(CrmConstants.Tags.DedicatedPlatinumTier, StringComparison.OrdinalIgnoreCase));
                            if (dedicatedTier != null)
                            {
                                return _mapper.Map<ItemTagModel>(dedicatedTier);
                            }
                        }

                        if (eligibleLowestTier.Code.Equals(CrmConstants.MemberTierCodes.Black, StringComparison.OrdinalIgnoreCase))
                        {
                            dedicatedTier = tags.FirstOrDefault(x => x.Name.Equals(CrmConstants.Tags.DedicatedBlackTier, StringComparison.OrdinalIgnoreCase));
                            if (dedicatedTier != null)
                            {
                                return _mapper.Map<ItemTagModel>(dedicatedTier);
                            }
                        }
                    }
                }

                return result;
            }
            else
            {
                return result;
            }
        }

        #region Private

        private void GetGiftSetItems(RewardBySpendingModel rModel, IEnumerable<Category> cmsSubCategories, IEnumerable<Merchant> cmsMerchants)
        {
            if (rModel.Gifts != null && rModel.Gifts.Any())
            {
                var providerItemIdList = new List<string>();
                var providerItemNameList = new List<string>();
                foreach (var gift in rModel.Gifts)
                {
                    if (!string.IsNullOrEmpty(gift.SubCategory))
                    {
                        var displaySubCategory = cmsSubCategories.FirstOrDefault(x => x.Code.Equals(gift.SubCategory))?.DisplayName;
                        gift.DisplaySubCategory = displaySubCategory;
                    }

                    var provider = cmsMerchants.FirstOrDefault(x => x.CrmId.HasValue && x.CrmId.Value == gift.ProviderId);
                    if (provider == null)
                    {
                        continue;
                    }

                    gift.ProviderName = provider.Title;
                    providerItemIdList.Add(provider.ItemId);
                    providerItemNameList.Add(provider.ItemName);
                }

                if (rModel.Gifts.Count() == 1)
                {
                    var provider = cmsMerchants.FirstOrDefault(x => x.CrmId.HasValue && x.CrmId.Value == rModel.Gifts.FirstOrDefault()?.ProviderId);
                    if (provider != null)
                    {
                        rModel.ProviderName = provider?.Title;
                    }
                }

                rModel.ItemTenantName = string.Join(",", providerItemNameList);
                rModel.ProviderItemId = string.Join("|", providerItemIdList);
            }
        }

        private IEnumerable<RewardBySpendingModel> ApplyDefaultSorting(IEnumerable<RewardBySpendingModel> rbsList)
        {
            rbsList = rbsList
                .OrderBy(x => x.SortingLevel)
                .ThenBy(x => x.RequiredLoyaltyCurrency)
                .ThenByDescending(x => x.DisplayStartDate);

            return rbsList;
        }

        private IEnumerable<ApiRewardBySpending> FilterOutOfficeExclusive(IEnumerable<ApiRewardBySpending> redeemableItems)
        {
            return redeemableItems.Where(x => !IsOfficeExclusive(x));
        }

        private IEnumerable<ApiRewardBySpending> FilterOutEVCoupon(IEnumerable<ApiRewardBySpending> redeemableItems)
        {
            return redeemableItems.Where(x => !x.GiftList.HasElement() || !x.GiftList.Any(ev => CrmConstants.GiftType.EVCoupon.Equals(ev.Type, StringComparison.InvariantCultureIgnoreCase)));
        }

        private bool IsOfficeExclusive(ApiRewardBySpending redeemableItems)
        {
            return redeemableItems.EarningCriteriaEligibleGuestType.HasElement() && redeemableItems.EarningCriteriaEligibleGuestType.Count == 1
            && CrmConstants.EarningCriteriaGuestType.OFFICE_WORKER.Equals(redeemableItems?.EarningCriteriaEligibleGuestType.FirstOrDefault(), StringComparison.OrdinalIgnoreCase);
        }

        private IEnumerable<T> FilterOutOfficeExclusiveNoneLogin<T>(IEnumerable<T> redeemableItems) where T : RewardBySpending
        {
            return redeemableItems.Where(x => !IsOfficeExclusiveNoneLogin(x));
        }

        private IEnumerable<RewardBySpendingModel> FilterOutEVCouponNoneLogin(IEnumerable<RewardBySpendingModel> redeemableItems)
        {
            return redeemableItems.Where(x => !x.Gifts.HasElement() || !x.Gifts.Any(ev => CrmConstants.GiftType.EVCoupon.Equals(ev.Type, StringComparison.InvariantCultureIgnoreCase)));
        }

        private bool IsOfficeExclusiveNoneLogin<T>(T redeemableItems) where T : RewardBySpending
        {
            if (redeemableItems.PromotionEarningCriteriaEligibleGuestType.HasTextValue())
            {
                var promotionEarningCriteriaEligibleGuestTypes = redeemableItems.PromotionEarningCriteriaEligibleGuestType.Split(",").ToList();
                return redeemableItems.PromotionEarningCriteriaEligibleGuestType.HasTextValue() && promotionEarningCriteriaEligibleGuestTypes.Count == 1
                    && CrmConstants.EarningCriteriaGuestType.OFFICE_WORKER.Equals(promotionEarningCriteriaEligibleGuestTypes.FirstOrDefault(), StringComparison.OrdinalIgnoreCase);
            }

            return false;
        }
        #endregion
    }
}