/// <summary>
        /// Merges unique DayParts from all StrikeWeights of certain SalesArea
        /// </summary>
        /// <param name="dayParts">
        /// DayParts from all StrikeWeights of certain SalesArea
        /// </param>
        /// <param name="mapper"></param>
        /// <returns>
        /// List of unique DayParts for SalesArea, with
        /// <see cref="AgDayPart.AgDayPartRequirement"/> being summed up
        /// </returns>
        protected List <AgDayPart> MergeUniqueDayParts(
            IReadOnlyCollection <AgDayPart> dayParts,
            AgParts agParts)
        {
            if ((dayParts?.Count ?? 0) == 0)
            {
                return(null);
            }

            return(dayParts
                   .GroupBy(d => d.UniqueTimeSlicesHash)
                   .Select(x =>
            {
                var dayPartIds = x.Select(d => d.DayPartNo).ToList();

                var groupedDayParts = x.Select(d => d).OrderBy(d => d.DayPartNo).ToList();
                var dayPart = groupedDayParts.First();
                decimal desiredPercentageSplit = 0,
                currentPercentageSplit = 0,
                campaignPrice = dayPart.CampaignPrice;

                foreach (var item in groupedDayParts)
                {
                    desiredPercentageSplit += item.AgDayPartRequirement.Required;
                    currentPercentageSplit += item.AgDayPartRequirement.Supplied;

                    if (item.CampaignPrice > campaignPrice)
                    {
                        campaignPrice = item.CampaignPrice;
                    }
                }

                dayPart.StartDate = "0";
                dayPart.EndDate = "0";
                dayPart.SpotMaxRatings = 0;
                dayPart.CampaignPrice = campaignPrice;
                dayPart.AgDayPartRequirement =
                    _mapper.Map <AgRequirement>(Tuple.Create(desiredPercentageSplit, currentPercentageSplit));

                foreach (var t in dayPart.AgTimeSlices)
                {
                    t.StartDate = "0";
                }

                var agPartsForUpdate = agParts.Where(d => dayPartIds.Contains(d.DayPartNo));
                foreach (var part in agPartsForUpdate)
                {
                    part.DayPartNo = dayPart.DayPartNo;
                }

                return dayPart;
            })
                   .ToList());
        }
        /// <summary>Loads the ag campaign sales areas.</summary>
        /// <param name="campaign">The campaign.</param>
        /// <param name="salesAreas">The sales areas.</param>
        /// <param name="agCampaignSalesArea">The ag campaign sales area.</param>
        /// <param name="dayPartNo">The day part no.</param>
        /// <param name="channelGroup">The channel group.</param>
        /// <returns></returns>
        protected List <AgCampaignSalesArea> LoadAgCampaignSalesAreas(
            Campaign campaign,
            IReadOnlyCollection <SalesArea> salesAreas,
            AgCampaignSalesArea agCampaignSalesArea,
            ref int dayPartNo,
            ref List <Tuple <int, int, SalesAreaGroup> > channelGroup)
        {
            if (campaign.SalesAreaCampaignTarget is null || campaign.SalesAreaCampaignTarget.Count == 0)
            {
                return(null);
            }

            var counter            = 1;
            var salesAreaTargetMap = salesAreas
                                     .Where(x => x.TargetAreaName != null)
                                     .Select(x => x.TargetAreaName)
                                     .Distinct()
                                     .ToDictionary(x => x, x => counter++);

            var agCampaignSalesAreaList = new List <AgCampaignSalesArea>();

            foreach (var salesAreaCampaignTarget in campaign.SalesAreaCampaignTarget)
            {
                int salesAreaNo;
                int channelGroupNo;

                if (_includeChannelGroup)
                {
                    // In this case we use old channel group calculation logic
                    // (assigning this value to both sales areas and channel group properties).

                    GetCampaignChannelGroup(salesAreaCampaignTarget.SalesAreaGroup, campaign.CustomId,
                                            ref channelGroup, out salesAreaNo);
                    channelGroupNo = salesAreaNo;
                }
                else
                {
                    // In this case we use logic which is to calculate ChannelGroup "saoc_data.tgt_sare_no" value
                    // using the SalesArea TargetAreaName property and use SalesArea CustomIds for everything else.

                    var salesArea = salesAreas.FirstOrDefault(s => s.Name.Equals(salesAreaCampaignTarget.SalesArea));

                    salesAreaNo    = salesArea?.CustomId ?? 0;
                    channelGroupNo = salesArea is null || salesArea.TargetAreaName is null
                        ? 0
                        : salesAreaTargetMap[salesArea.TargetAreaName];
                }

                var totalDesiredSplit = salesAreaCampaignTarget.Multiparts?.Sum(l => l.DesiredPercentageSplit) ?? 0;
                var totalCurrentSplit = salesAreaCampaignTarget.Multiparts?.Sum(l => l.CurrentPercentageSplit) ?? 0;

                var agLengthList = salesAreaCampaignTarget.Multiparts != null
                    ? _mapper.Map <List <AgLength> >(Tuple.Create(salesAreaCampaignTarget.Multiparts,
                                                                  campaign.CustomId, salesAreaNo))
                    : null;

                var agLengths = new AgLengths();

                if (!(agLengthList is null))
                {
                    agLengths.AddRange(agLengthList);
                }

                var strikeWeights = salesAreaCampaignTarget.CampaignTargets
                                    ?.Where(t => t.StrikeWeights != null && t.StrikeWeights.Any())
                                    .SelectMany(s => s.StrikeWeights).ToList();

                var agStrikeWeightList = strikeWeights != null && strikeWeights.Any()
                    ? _mapper.Map <List <AgStrikeWeight> >(Tuple.Create(strikeWeights, campaign.CustomId,
                                                                        salesAreaNo))
                    : null;

                var agStrikeWeights = new AgStrikeWeights();

                if (!(agStrikeWeightList is null))
                {
                    agStrikeWeights.AddRange(agStrikeWeightList);
                }

                var agDayPartList = LoadAgDayParts(
                    strikeWeights,
                    campaign.CustomId,
                    salesAreaNo,
                    ref dayPartNo);

                var agPartList = !(agDayPartList is null) && agDayPartList.Any()
                    ? _mapper.Map <List <AgPart> >(agDayPartList)
                    : null;

                var agParts = new AgParts();

                if (!(agPartList is null))
                {
                    agParts.AddRange(agPartList);
                }

                if (_mergeUniqueDayParts)
                {
                    agDayPartList = MergeUniqueDayParts(agDayPartList, agParts);
                }

                var agDayParts = new AgDayParts();

                if (!(agDayPartList is null))
                {
                    agDayParts.AddRange(agDayPartList);
                }

                var agDayPartLengths = agDayPartList?.Count > 0
                    ? agDayPartList.Select(x => x.AgDayPartLengths).ToList()
                    : null;
                var agPartLengthList = _mapper.Map <List <AgPartLength> >(agDayPartLengths);

                var agPartLengths = new AgPartLengths();

                if (!(agPartLengthList is null))
                {
                    agPartLengths.AddRange(agPartLengthList);
                }

                var agCampaignSalesAreaClone = agCampaignSalesArea.Clone();
                agCampaignSalesAreaClone.SalesAreaNo    = salesAreaNo;
                agCampaignSalesAreaClone.ChannelGroupNo = channelGroupNo;
                agCampaignSalesAreaClone.CampaignNo     = campaign.CustomId;
                agCampaignSalesAreaClone.AgSalesAreaCampaignRequirement
                    = _mapper.Map <AgRequirement>(Tuple.Create(totalDesiredSplit, totalCurrentSplit));
                agCampaignSalesAreaClone.AgCampaignSalesAreaPtrRef
                    = agCampaignSalesArea.AgCampaignSalesAreaPtrRef.Clone();
                agCampaignSalesAreaClone.AgCampaignSalesAreaPtrRef.SalesAreaNo = salesAreaNo;
                agCampaignSalesAreaClone.AgLengths          = agLengths;
                agCampaignSalesAreaClone.NbrAgLengths       = agLengthList?.Count ?? 0;
                agCampaignSalesAreaClone.AgStrikeWeights    = agStrikeWeights;
                agCampaignSalesAreaClone.NbrAgStrikeWeights = agStrikeWeightList?.Count ?? 0;
                agCampaignSalesAreaClone.AgDayParts         = agDayParts;
                agCampaignSalesAreaClone.NbrAgDayParts      = agDayPartList?.Count ?? 0;
                agCampaignSalesAreaClone.AgParts            = agParts;
                agCampaignSalesAreaClone.NbrParts           = agPartList?.Count ?? 0;
                agCampaignSalesAreaClone.NbrPartsLengths    = agPartLengthList?.Count ?? 0;
                agCampaignSalesAreaClone.AgPartsLengths     = agPartLengths;
                agCampaignSalesAreaClone.StopBooking        =
                    AgConversions.ToAgBooleanAsString(salesAreaCampaignTarget.StopBooking);

                agCampaignSalesAreaList.Add(agCampaignSalesAreaClone);
            }

            return(agCampaignSalesAreaList);
        }