/// <summary>
        /// Adds a given reason to both Shifts and the Database.
        /// </summary>
        /// <param name="allRequiredConfigurations">Object containign the AccessToken.</param>
        /// <param name="teamsId">MS Teams Id.</param>
        /// <param name="reason">The name of the reason.</param>
        private async Task AddSingleReason(SetupDetails allRequiredConfigurations, string teamsId, string reason)
        {
            (var success, TimeOffReasonResponse.TimeOffReason reasonToAdd) = await this.CreateTimeOffReasonAsync(allRequiredConfigurations, teamsId, reason).ConfigureAwait(false);

            if (success)
            {
                var paycodeMapping = new PayCodeToTimeOffReasonsMappingEntity
                {
                    PartitionKey    = teamsId,
                    RowKey          = reasonToAdd.DisplayName,
                    TimeOffReasonId = reasonToAdd.Id,
                };

                await this.azureTableStorageHelper.InsertOrMergeTableEntityAsync(paycodeMapping, "PayCodeToTimeOffReasonsMapping").ConfigureAwait(true);
            }
        }
        /// <summary>
        /// Update mapping reasons in storage.
        /// </summary>
        /// <param name="accessToken">Cached AccessToken.</param>
        /// <param name="teamsId">MS Teams Id.</param>
        /// <param name="tenantId">tenant Id.</param>
        /// <param name="kronosEndpoint">The Kronos WFC API endpoint.</param>
        /// <param name="kronosSession">The Kronos WFC Jsession.</param>
        /// <returns>List of TimeOffReasons.</returns>
        private async Task <dynamic> UpdateTimeOffReasonsAsync(
            string accessToken,
            string teamsId,
            string tenantId,
            string kronosEndpoint,
            string kronosSession)
        {
            this.telemetryClient.TrackTrace($"{MethodBase.GetCurrentMethod().Name}");
            var paycodeList = await this.payCodeActivity.FetchPayCodesAsync(new Uri(kronosEndpoint), kronosSession).ConfigureAwait(true);

            var lstTimeOffReasons = await this.GetTimeOffReasonAsync(accessToken, teamsId).ConfigureAwait(true);

            if (lstTimeOffReasons != null)
            {
                var reasonList = lstTimeOffReasons.Select(c => c.DisplayName).ToList();
                var newCodes   = paycodeList.Except(reasonList);

                foreach (var payCode in newCodes)
                {
                    await this.CreateTimeOffReasonAsync(accessToken, teamsId, payCode).ConfigureAwait(true);
                }

                var timeOffReasons = await this.GetTimeOffReasonAsync(accessToken, teamsId).ConfigureAwait(true);

                var mappedReasons = await this.timeOffReasonProvider.FetchReasonsForTeamsAsync(teamsId, tenantId).ConfigureAwait(true);

                foreach (var timeOffReason in timeOffReasons)
                {
                    if ((paycodeList.Contains(timeOffReason.DisplayName) && !mappedReasons.ContainsKey(timeOffReason.Id)) ||
                        (paycodeList.Contains(timeOffReason.DisplayName) &&
                         mappedReasons.ContainsKey(timeOffReason.Id) &&
                         !mappedReasons[timeOffReason.Id].Contains(timeOffReason.DisplayName, StringComparison.InvariantCulture)))
                    {
                        var payCodeToTimeOffReasonsMappingEntity = new PayCodeToTimeOffReasonsMappingEntity
                        {
                            PartitionKey    = teamsId,
                            RowKey          = timeOffReason.DisplayName,
                            TimeOffReasonId = timeOffReason.Id,
                        };

                        await this.azureTableStorageHelper.InsertOrMergeTableEntityAsync(payCodeToTimeOffReasonsMappingEntity, "PayCodeToTimeOffReasonsMapping").ConfigureAwait(true);
                    }
                }
            }

            return(null);
        }
        /// <summary>
        /// Creates a time off request that was requested in Teams.
        /// </summary>
        /// <param name="user">The user details of the time off requestor.</param>
        /// <param name="timeOffEntity">The time off to be created.</param>
        /// <param name="timeOffReason">The time off reason.</param>
        /// <param name="allRequiredConfigurations">Setup details.</param>
        /// <param name="kronosTimeZone">The kronos timezone.</param>
        /// <returns>Whether the time off request was created successfully or not.</returns>
        internal async Task <bool> CreateTimeOffRequestInKronosAsync(
            UserDetailsModel user,
            TimeOffRequestItem timeOffEntity,
            PayCodeToTimeOffReasonsMappingEntity timeOffReason,
            SetupDetails allRequiredConfigurations,
            string kronosTimeZone)
        {
            // Teams provides date times in UTC so convert to the local time.
            var localStartDateTime = this.utility.UTCToKronosTimeZone(timeOffEntity.StartDateTime, kronosTimeZone);
            var localEndDateTime   = this.utility.UTCToKronosTimeZone(timeOffEntity.EndDateTime, kronosTimeZone);

            // Construct the query date span for the Kronos request
            var queryStartDate = localStartDateTime.AddDays(
                -Convert.ToInt16(this.appSettings.CorrectedDateSpanForOutboundCalls, CultureInfo.InvariantCulture))
                                 .ToString(this.appSettings.KronosQueryDateSpanFormat, CultureInfo.InvariantCulture);

            var queryEndDate = localEndDateTime.AddDays(
                Convert.ToInt16(this.appSettings.CorrectedDateSpanForOutboundCalls, CultureInfo.InvariantCulture))
                               .ToString(this.appSettings.KronosQueryDateSpanFormat, CultureInfo.InvariantCulture);

            var timeOffReqQueryDateSpan = $"{queryStartDate}-{queryEndDate}";

            var commentTimeStamp = this.utility.UTCToKronosTimeZone(DateTime.UtcNow, kronosTimeZone).ToString(CultureInfo.InvariantCulture);
            var comments         = XmlHelper.GenerateKronosComments(timeOffEntity.SenderMessage, this.appSettings.SenderTimeOffRequestCommentText, commentTimeStamp);

            // Create the Kronos Time Off Request.
            var timeOffResponse = await this.timeOffActivity.CreateTimeOffRequestAsync(
                allRequiredConfigurations.KronosSession,
                localStartDateTime,
                localEndDateTime,
                timeOffReqQueryDateSpan,
                user.KronosPersonNumber,
                timeOffReason.RowKey,
                comments,
                new Uri(allRequiredConfigurations.WfmEndPoint)).ConfigureAwait(false);

            if (!string.IsNullOrWhiteSpace(timeOffResponse?.Error?.Message))
            {
                this.telemetryClient.TrackTrace($"Could not create the time off request : {timeOffResponse?.Error?.Message} ");
                return(false);
            }

            var submitTimeOffResponse = await this.timeOffActivity.SubmitTimeOffRequestAsync(
                allRequiredConfigurations.KronosSession,
                user.KronosPersonNumber,
                timeOffResponse?.EmployeeRequestMgm?.RequestItem?.GlobalTimeOffRequestItms?.FirstOrDefault()?.Id,
                timeOffReqQueryDateSpan,
                new Uri(allRequiredConfigurations.WfmEndPoint)).ConfigureAwait(false);

            TimeOffMappingEntity newTimeOffReq = new TimeOffMappingEntity();

            // IsActive represents whether the time off was successfully created.
            if (submitTimeOffResponse?.Status == ApiConstants.Failure)
            {
                newTimeOffReq.IsActive = false;
            }
            else
            {
                newTimeOffReq.IsActive = true;
            }

            newTimeOffReq.Duration           = timeOffResponse.EmployeeRequestMgm.RequestItem.GlobalTimeOffRequestItms.FirstOrDefault().TimeOffPeriodsList.TimeOffPerd.FirstOrDefault().Duration;
            newTimeOffReq.EndDate            = timeOffResponse.EmployeeRequestMgm.RequestItem.GlobalTimeOffRequestItms.FirstOrDefault().TimeOffPeriodsList.TimeOffPerd.FirstOrDefault().EndDate;
            newTimeOffReq.StartDate          = timeOffResponse.EmployeeRequestMgm.RequestItem.GlobalTimeOffRequestItms.FirstOrDefault().TimeOffPeriodsList.TimeOffPerd.FirstOrDefault().StartDate;
            newTimeOffReq.StartTime          = timeOffResponse.EmployeeRequestMgm.RequestItem.GlobalTimeOffRequestItms.FirstOrDefault().TimeOffPeriodsList.TimeOffPerd.FirstOrDefault().StartTime;
            newTimeOffReq.PayCodeName        = timeOffResponse.EmployeeRequestMgm.RequestItem.GlobalTimeOffRequestItms.FirstOrDefault().TimeOffPeriodsList.TimeOffPerd.FirstOrDefault().PayCodeName;
            newTimeOffReq.KronosPersonNumber = timeOffResponse.EmployeeRequestMgm.Employees.PersonIdentity.PersonNumber;
            newTimeOffReq.PartitionKey       = $"{localStartDateTime.Month}_{localStartDateTime.Year}";
            newTimeOffReq.RowKey             = timeOffResponse.EmployeeRequestMgm.RequestItem.GlobalTimeOffRequestItms.FirstOrDefault().Id;
            newTimeOffReq.ShiftsRequestId    = timeOffEntity.Id;
            newTimeOffReq.KronosRequestId    = timeOffResponse.EmployeeRequestMgm.RequestItem.GlobalTimeOffRequestItms.FirstOrDefault().Id;
            newTimeOffReq.KronosStatus       = ApiConstants.Submitted;
            newTimeOffReq.ShiftsStatus       = ApiConstants.Pending;

            this.AddorUpdateTimeOffMappingAsync(newTimeOffReq);

            // If isActive is false time off request was not submitted so return false and vice versa.
            return(newTimeOffReq.IsActive);
        }