namespace Hims.Api.Controllers
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using AutoMapper;
    using Domain.Services;
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Mvc;
    using Models.Provider;
    using Newtonsoft.Json;
    using Npgsql;
    using Shared.DataFilters;
    using Shared.EntityModels;
    using Shared.UserModels;
    using Shared.UserModels.Filters;
    using Utilities;

    using Hims.Domain.Helpers;
    using Hims.Shared.Library.Enums;
    using Hims.Shared.UserModels.Common;
    using Hims.Api.Models;
    using Hims.Shared.UserModels.ProviderAvailability;
    using InsertModel = Shared.UserModels.ProviderLeave.InsertModel;
    using UpdateModel = Shared.UserModels.ProviderLeave.UpdateModel;
    using Hims.Shared.UserModels.ProviderLeave;

    /// <inheritdoc />
    [Route("api/provider-availability")]
    [Consumes("application/json")]
    [Produces("application/json")]
    public class ProviderAvailabilityController : BaseController
    {
        /// <summary>
        /// The provider location services.
        /// </summary>
        private readonly IProviderAvailabilityService providerAvailabilityServices;

        /// <summary>
        /// The provider leave services.
        /// </summary>
        private readonly IProviderLeaveService providerLeaveServices;

        /// <summary>
        /// The wallet services.
        /// </summary>
        private readonly IWalletService walletService;

        /// <summary>
        /// The AES helper.
        /// </summary>
        private readonly IAESHelper aesHelper;

        /// <summary>
        /// The mapper.
        /// </summary>
        private readonly IMapper mapper;

        /// <summary>
        /// The auditlog services.
        /// </summary>
        private readonly IAuditLogService auditLogServices;

        /// <summary>
        /// the provider service
        /// </summary>
        private readonly IProviderService providerServices;


        /// <inheritdoc />
        public ProviderAvailabilityController(
            IProviderAvailabilityService providerAvailabilityServices,
            IAESHelper aesHelper,
            IProviderLeaveService providerLeaveServices,
            IMapper mapper, IWalletService walletService, IProviderService providerServices, IAuditLogService auditLogServices)
        {
            this.providerAvailabilityServices = providerAvailabilityServices;
            this.providerLeaveServices = providerLeaveServices;
            this.aesHelper = aesHelper;
            this.mapper = mapper;
            this.walletService = walletService;
            this.providerServices = providerServices;
            this.auditLogServices = auditLogServices;
        }

        /// <summary>
        /// The fetch providerLocations.
        /// </summary>
        /// <param name="model">
        /// The providerLocation filter model.
        /// </param>
        /// <param name="location"></param>
        /// <returns>
        /// The list of providerLocations.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - List of providerLocations.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Authorize]
        [Route("fetch")]
        [ProducesResponseType(typeof(List<ProviderLocationModel>), 200)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> FetchAsync([FromBody] ProviderLocationFilterModel model, [FromHeader] LocationHeader location)
        {
            model = (ProviderLocationFilterModel)EmptyFilter.Handler(model);
            if (!string.IsNullOrEmpty(model.EncryptedProviderId))
            {
                model.ProviderId = Convert.ToInt32(this.aesHelper.Decode(model.EncryptedProviderId));
            }
            if (model.LocationId == null && string.IsNullOrEmpty(model.FetchingFrom))
            {
                model.LocationId = string.IsNullOrEmpty(location.LocationId) ? (int?)null : int.Parse(location.LocationId);
            }
            var providerLocations = await this.providerAvailabilityServices.FetchAsync(model.ProviderId, null, model.LocationId);
            return this.Success(providerLocations);
        }

        /// <summary>
        /// The fetch provider availabilities.
        /// </summary>
        /// <param name="model">
        /// The provider availability filter model.
        /// </param>
        /// <returns>
        /// The list of provider availabilities.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - List of provider availabilities.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Route("fetch-availabilities")] // unreachable API's
        [ProducesResponseType(typeof(List<FilterAvailabilityViewModel>), 200)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> FetchAvailabilitiesAsync([FromBody] FilterAvailabilityViewModel model)
        {
            model = (FilterAvailabilityViewModel)EmptyFilter.Handler(model);
            if (!string.IsNullOrEmpty(model.EncryptedProviderId))
            {
                model.ProviderId = Convert.ToInt32(this.aesHelper.Decode(model.EncryptedProviderId));
            }

            var response = await this.providerAvailabilityServices.FetchAvailabilitiesAsync(model);
            return this.Success(response);
        }

        /// <summary>
        /// The fetch provider availabilities.
        /// </summary>
        /// <param name="model">
        /// The provider availability filter model.
        /// </param>
        /// <returns>
        /// The list of provider availabilities.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - List of provider availabilities.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Route("fetch-availability-dates")]
        [ProducesResponseType(typeof(List<ProviderAvailabilityDatesModel>), 200)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> FetchAvailabilityDatesAsync([FromBody] ProviderAvailabilityFilterModel model)
        {
            model = (ProviderAvailabilityFilterModel)EmptyFilter.Handler(model);
            if (!string.IsNullOrEmpty(model.EncryptedProviderId))
            {
                model.ProviderId = Convert.ToInt32(this.aesHelper.Decode(model.EncryptedProviderId));
            }

            var response = await this.providerAvailabilityServices.FetchAvailabilityDatesAsync(model.ProviderId, model.LocationId);
            return this.Success(response);
        }

        /// <summary>
        /// Fetches the slots helper.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <returns></returns>
        public async Task<Shared.UserModels.Slots.Response<TimeSlotModel>> FetchSlotsHelper(Shared.UserModels.Slots.Request model)
        {
            var providerLocation = await this.providerAvailabilityServices.FindAsync(model.ProviderLocationId, model.PatientId, model.LocationId);
            if (providerLocation == null || providerLocation.ProviderLocationId == 0)
            {
                return new Shared.UserModels.Slots.Response<TimeSlotModel>
                {
                    Slots = new List<TimeSlotModel>(),
                    IsReturn = true
                };
            }

            var date = DateTimeFilter.ToNotNullDate(model.SlotDate);
            var day = ((int)(date.DayOfWeek + 6) % 7) + 1;
            if (!providerLocation.AvailableDays.Contains(day.ToString()))
            {
                return new Shared.UserModels.Slots.Response<TimeSlotModel>
                {
                    Slots = new List<TimeSlotModel>(),
                    IsReturn = true
                };
            }

            var isInLeave = await this.providerLeaveServices.CheckAsync(date.ToString("yyyy-MM-dd"), providerLocation.ProviderId, model.ProviderLocationId);
            if (isInLeave > 0)
            {
                return new Shared.UserModels.Slots.Response<TimeSlotModel>
                {
                    Slots = new List<TimeSlotModel>(),
                    IsReturn = true
                };
            }
            var slots = new List<ProviderShiftModel>();
            if (!string.IsNullOrEmpty(providerLocation.Availability))
            {
                slots = JsonConvert.DeserializeObject<List<ProviderShiftModel>>(providerLocation.Availability);
                if (slots == null)
                {
                    return new Shared.UserModels.Slots.Response<TimeSlotModel>
                    {
                        Slots = new List<TimeSlotModel>(),
                        IsReturn = true,
                        IsBadRequest = true,
                        BadRequestMessage = "No slots found."
                    };
                }
            }
            else
            {
                return new Shared.UserModels.Slots.Response<TimeSlotModel>
                {
                    Slots = new List<TimeSlotModel>(),
                    IsReturn = true,
                    IsBadRequest = true,
                    BadRequestMessage = "No slots found."
                };
            }
            //new changes start

            var durations = JsonConvert.DeserializeObject<List<VisitChargeModel>>(providerLocation.Duration);
            var findDuration = durations != null
                ? durations.Find(m => m.TypeId == model.VisitTypeId)
                : null;

            var duration = 0;
            if (findDuration != null && !string.IsNullOrEmpty(findDuration.Value.ToString()))
            {
                duration = findDuration.Value;
            }

            if (duration == 0)
            {
                return new Shared.UserModels.Slots.Response<TimeSlotModel>
                {
                    Slots = new List<TimeSlotModel>(),
                    IsReturn = true,
                    IsBadRequest = true,
                    BadRequestMessage = "No slots found."
                };
            }

            //new changes end

            //switch (model.VisitType)
            //{
            //    case 'P':
            //        duration = Convert.ToInt16(providerLocation.ConsultationDuration);
            //        break;
            //    case 'T':
            //        duration = Convert.ToInt16(providerLocation.TelemedicineDuration);
            //        break;
            //    case 'I':
            //        duration = Convert.ToInt16(providerLocation.InPatientDuration);
            //        break;
            //    case 'O':
            //        duration = Convert.ToInt16(providerLocation.OutPatientDuration);
            //        break;
            //    case 'C':
            //        duration = Convert.ToInt16(providerLocation.CasualtyDuration);
            //        break;
            //}
            var bookedAppointmentTimes = await this.providerAvailabilityServices.FetchAppointmentTimesAsync(model.ProviderLocationId, date);

            var availability = slots.FirstOrDefault(m => m.Day == day) ?? new ProviderShiftModel();
            var specializationAvailability = availability.Slots.Where(t => t.Specializations.Contains(model.SpecializationId));
            availability.Slots = specializationAvailability.ToList();
            var timeZones = TimeZoneInfo.GetSystemTimeZones();
            TimeSpan offset;
            if (model.Offset.StartsWith("-"))
            {
                offset = -TimeSpan.Parse(model.Offset.Substring(1));
                offset = offset.Subtract(TimeSpan.Parse("01:00"));
            }
            else
            {
                offset = TimeSpan.Parse(model.Offset.Substring(1));
            }

            var timeZone = timeZones.FirstOrDefault(m => m.DaylightName == model.TimeZone || m.Id == model.TimeZone);

            if (timeZone == null)
            {
                timeZone = timeZones.First(m => m.BaseUtcOffset == offset);
            }

            var currentDateTime = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, timeZone);
            var currentTime = currentDateTime.ToString("H:mm");
            var appointmentTimes = bookedAppointmentTimes.ToList();

            var newTimeSlots = new List<TimeSlotModel>();

            foreach (var item in availability.Slots)
            {
                var fromTime = TimeSpan.Parse(item.FromTime ?? string.Empty);
                var toTime = TimeSpan.Parse(item.ToTime ?? string.Empty);
                var timeSpan = TimeSpan.Parse(currentTime);

                // timeSpan = currentTime[1] == "PM" ? timeSpan + TimeSpan.FromHours(12) : timeSpan;
                var timeDifference = toTime - fromTime;
                var slotsCount = ((timeDifference.Hours * 60) + timeDifference.Minutes) / duration;
                var timeSlots = new List<TimeSlotModel>
                {
                (TimeSpan.Compare(timeSpan, fromTime) == 1 || TimeSpan.Compare(timeSpan, fromTime) == 0) && (DateTime.Compare(date, DateTime.Now) == 0 || DateTime.Compare(date, DateTime.Now) == -1)
                    ? new TimeSlotModel { SlotTime = fromTime, SlotValue = new DateTime(fromTime.Ticks).ToString("HH:mm"), SlotName = new DateTime(fromTime.Ticks).ToString("hh:mm tt"), Status = SlotStatus.Expired }
                    : new TimeSlotModel { SlotTime = fromTime, SlotValue = new DateTime(fromTime.Ticks).ToString("HH:mm"), SlotName = new DateTime(fromTime.Ticks).ToString("hh:mm tt"), Status = SlotStatus.Available }
                };

                if (appointmentTimes.Any(a => a == fromTime))
                {
                    timeSlots[0].Status = SlotStatus.Booked;
                }

                newTimeSlots.Add(timeSlots[0]);
                for (var i = 0; i < slotsCount; i++)
                {
                    var slotTime = timeSlots[i].SlotTime + TimeSpan.FromMinutes(duration);
                    var slotDateTime = new DateTime(slotTime.Ticks);
                    var timeSlot = new TimeSlotModel
                    {
                        SlotTime = slotTime,
                        SlotValue = slotDateTime.ToString("HH:mm"),
                        SlotName = slotDateTime.ToString("hh:mm tt")
                    };

                    if (appointmentTimes.Any(a => a == timeSlot.SlotTime))
                    {
                        timeSlot.Status = SlotStatus.Booked;
                    }
                    //else if ((TimeSpan.Compare(timeSpan, timeSlot.SlotTime) == 1 || TimeSpan.Compare(timeSpan, timeSlot.SlotTime) == 0) && (DateTime.Compare(date, DateTime.UtcNow) == 0 || DateTime.Compare(date, DateTime.UtcNow) == -1))
                    //{
                    //    timeSlot.Status = SlotStatus.Expired;
                    //}
                    else
                    {
                        timeSlot.Status = SlotStatus.Available;
                    }

                    timeSlots.Add(timeSlot);
                    newTimeSlots.Add(timeSlot);
                }
            }

            for (var i = 0; i < newTimeSlots.Count(); i++)
            {
                newTimeSlots[i].TokenNumber = i + 1;
            }
            return new Shared.UserModels.Slots.Response<TimeSlotModel>
            {
                Slots = newTimeSlots.Where(m => m.Status != SlotStatus.Expired).ToList(),
                Availability = availability,
                IsReturn = false
            };
        }


        /// <summary>
        /// The fetch slots.
        /// </summary>
        /// <param name="model">
        /// The providerLocation filter model.
        /// </param>
        /// <returns>
        /// The list of TimeSlotModel.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - List of TimeSlotModel.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Authorize]
        [Route("fetch-slots-only")]
        [ProducesResponseType(typeof(List<TimeSlotModel>), 200)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> FetchSlotsOnlyAsync([FromBody] Shared.UserModels.Slots.Request model)
        {
            try
            {
                var data = await this.FetchSlotsHelper(model);
                if (data.Slots.Count > 0)
                {
                    var diff = data.Slots.Count > 1 ? Convert.ToInt32((data.Slots[1].SlotTime - data.Slots[0].SlotTime).TotalMinutes) : 0;
                    var timeTokens = model.Time.Split(":");
                    var selectedTime = new TimeSpan(Convert.ToInt32(timeTokens[0]), Convert.ToInt32(timeTokens[1]), Convert.ToInt32(timeTokens[2]));
                    var startTime = selectedTime.Subtract(new TimeSpan(0, diff, 0));
                    var endTime = selectedTime.Add(new TimeSpan(0, diff, 0));
                    var rangeSlots = data.Slots.Where(x => x.SlotTime >= startTime && x.SlotTime <= endTime);
                    var count = rangeSlots.Count();
                    if (count <= 3)
                    {
                        data.Slots = rangeSlots.ToList();
                    }
                    else
                    {
                        data.Slots = rangeSlots.Skip(Convert.ToInt32(Math.Floor((decimal)(count / 2)) - 2)).Take((count % 2) == 0 ? 4 : 3).ToList();
                    }
                }
                else
                {

                }

                return this.Success(data.Slots);
            }
            catch (Exception ex)
            {
                return this.Success(new { slots = new List<TimeSlotModel>() });
            }
        }

        /// <summary>
        /// The fetch slots.
        /// </summary>
        /// <param name="model">
        /// The providerLocation filter model.
        /// </param>
        /// <returns>
        /// The list of TimeSlotModel.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - List of TimeSlotModel.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Authorize]
        [Route("fetch-slots")]
        [ProducesResponseType(typeof(List<TimeSlotModel>), 200)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> FetchSlotsAsync([FromBody] ProviderLocationFilterModel model)
        {
            var data = await this.FetchSlotsHelper(new Shared.UserModels.Slots.Request
            {
                ProviderLocationId = model.ProviderLocationId,
                PatientId = model.PatientId,
                VisitTypeId = model.VisitTypeId,
                Offset = model.Offset,
                SlotDate = model.SlotDate,
                TimeZone = model.TimeZone,
                SpecializationId = model.SpecializationId,
                LocationId = model.LocationId
            });

            if (data.IsReturn)
            {
                if (data.IsBadRequest)
                {
                    return this.BadRequest(data.BadRequestMessage);
                }
                else
                {
                    return this.Success(new { slots = data.Slots, telemedicineCharges = 0, consultationCharges = 0, appChargesAmount = 0 });
                }
            }

            //var providerCharges = new ProviderCharges
            //{
            //    FollowUpDaysChargesForOp = !providerLocation.IsFallowUp ? availability.ChargeType : (availability.FollowUpDaysChargesForOp)
            //}
            //var providerCharges = new ProviderCharges
            //{
            //    TelemedicineCharges = !providerLocation.IsFallowUp ? availability.TelemedicineCharges : (availability.TelemedicineFallowUpCharges == null ? availability.TelemedicineCharges : availability.TelemedicineFallowUpCharges),
            //    ConsultationCharges = !providerLocation.IsFallowUp ? availability.ConsultationCharges : (availability.ConsultationFallowUpCharges == null ? availability.ConsultationCharges : availability.ConsultationFallowUpCharges),
            //    InPatientCharges = !providerLocation.IsFallowUp ? availability.InPatientCharges : (availability.InPatientFallowUpCharges == null ? availability.InPatientCharges : availability.InPatientFallowUpCharges),
            //    OutPatientCharges = !providerLocation.IsFallowUp ? availability.OutPatientCharges : (availability.OutPatientFallowUpCharges == null ? availability.OutPatientCharges : availability.OutPatientFallowUpCharges),
            //    CasualtyCharges = !providerLocation.IsFallowUp ? availability.CasualtyCharges : (availability.CasualtyFallowUpCharges == null ? availability.CasualtyCharges : availability.CasualtyFallowUpCharges),
            //    CovidCharges = availability.CovidCharges == null ? 0 : availability.CovidCharges,
            //    SpecialCharges = availability.SpecialCharges == null ? 0 : availability.SpecialCharges,
            //    //followUpDaysCharges = !providerLocation.IsFallowUp ? availability.FollowUpDaysChargesForOp : (availability.FollowUpDaysChargesForOp == null ? availability.FollowUpDaysChargesForOp)
            //};

            //new changes start
            var chargeTypes = data.Availability.ChargeType;
            var findCharges = chargeTypes.Find(m => m.TypeId == model.ChargeTypesId);
            var providerCharge = 0;
            if (findCharges != null && !string.IsNullOrEmpty(findCharges.Value.ToString()))
            {
                providerCharge = findCharges.Value;
            }
            //var providerCharge = Convert.ToInt16(findCharges.Value);
            //new changes end

            var taxes = await this.providerAvailabilityServices.FetchTaxAsync();
            //decimal providerCharge = 0;
            //switch (model.VisitType)
            //{
            //    case "P":
            //        providerCharge = Convert.ToDecimal(providerCharges.ConsultationCharges);
            //        break;
            //    case "T":
            //        providerCharge = Convert.ToDecimal(providerCharges.TelemedicineCharges);
            //        break;
            //    case "I":
            //        providerCharge = Convert.ToDecimal(providerCharges.InPatientCharges);
            //        break;
            //    case "O":
            //        providerCharge = Convert.ToDecimal(providerCharges.OutPatientCharges);
            //        break;
            //    case "C":
            //        providerCharge = Convert.ToDecimal(providerCharges.CasualtyCharges);
            //        break;
            //}

            //if (model.ChargeType == 'C')
            //{
            //    providerCharge = Convert.ToDecimal(providerCharges.CovidCharges);
            //}
            //else if (model.ChargeType == 'S')
            //{
            //    providerCharge = Convert.ToDecimal(providerCharges.SpecialCharges);
            //}

            decimal? appChargesAmount = 0;
            if (providerCharge != 0)
            {
                var appChargesValue = providerCharge * Convert.ToDecimal(taxes.AppCharges.Replace("%", "")) / 100;
                appChargesAmount = Convert.ToDecimal(string.Format("{0:0.00}", appChargesValue));
            }

            var wallet = await this.walletService.FindAsync(model.PatientId);
            if (wallet == null)
            {
                wallet = new WalletModel();
                wallet.TotalCredits = 0.00;
                wallet.TotalDebits = 0.00;
            }

            wallet.TotalAvailable = wallet.TotalCredits - wallet.TotalDebits;

            return this.Success(new { slots = data.Slots, ProviderCharges = providerCharge, AppChargesAmount = appChargesAmount, Wallet = wallet });
        }



        /// <summary>
        /// The add provider location.
        /// </summary>
        /// <param name="request">
        /// The request.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - Provider location added successfully.
        /// - 409 - Provider location already exist.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Authorize]
        [Route("modify")]
        [ProducesResponseType(typeof(string), 200)]
        [ProducesResponseType(409)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> ModifyAsync([FromBody] ModifyAvailabilityModel request, [FromHeader] LocationHeader header)
        {
            request = (ModifyAvailabilityModel)EmptyFilter.Handler(request);
            var model = this.mapper.Map<ModifyAvailabilityModel>(request);

            var provider = await this.providerServices.FindProviderByProviderId(request.ProviderId);
            //var response = model.ProviderAvailabilityId == 0
            //                   ? await this.providerAvailabilityServices.AddAsync(model)
                             //  : await this.providerAvailabilityServices.UpdateAsync(model);
            var response =
                await this.providerAvailabilityServices.AddAsync(model);
            switch (response)
            {
                case -1:
                    return this.Conflict("Given availability has already been exists with us.");
                case 0:
                    return this.ServerError();
                default:

                    var auditLogModel = new AuditLogModel
                    {
                        AccountId = request.ModifiedBy,
                        LogTypeId = (int)LogTypes.ProviderAvailability,
                        LogFrom = (short)request.LoginRoleId,
                        LogDate = DateTime.UtcNow.AddMinutes(330),
                        //LogDescription = $"{model.SpecializationName} specialization has been added."
                        LogDescription = model.ProviderAvailabilityId == 0 ? $"{request.FullName} has added Availability for the Doctor {provider.FullName} on {DateTime.UtcNow.AddMinutes(330)}" : $"{request.FullName} has modified the Available days and Slots for Doctor  {provider.FullName} on {DateTime.UtcNow.AddMinutes(330)}",
                        LocationId = Convert.ToInt32(header.LocationId)
                    };
                    await this.auditLogServices.LogAsync(auditLogModel);


                    return this.Success($"Your availability has been {(model.ProviderAvailabilityId == 0 ? "added" : "updated")} successfully.");
            }
        }

        /// <summary>
        /// The delete provider location.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - ProviderLocation deleted successfully.
        /// - 409 - ProviderLocation can not be deleted.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpDelete]
        [Authorize]
        [Route("delete")]
        [ProducesResponseType(typeof(string), 200)]
        [ProducesResponseType(409)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> DeleteAsync(FindProviderLocationRequest model, [FromHeader] LocationHeader header)
        {
            try
            {
                model = (FindProviderLocationRequest)EmptyFilter.Handler(model);

                var ProviderFullName = await this.providerAvailabilityServices.FindProviderByProviderLocationId(model.ProviderLocationId);

                var response = await this.providerAvailabilityServices.DeleteAsync(model.ProviderLocationId);

                //var provider = await this.providerServices.FindProviderByProviderId(model.ProviderId);

                if (response == 0)
                {
                    return this.ServerError();
                }

                var auditLogModel = new AuditLogModel
                {
                    AccountId = model.ModifiedBy,
                    LogTypeId = (int)LogTypes.Provider,
                    LogFrom = (short)model.RoleId,
                    LogDate = DateTime.UtcNow.AddMinutes(330),
                    //LogDescription = $"{model.SpecializationName} specialization has been added."
                    LogDescription = $"{model.FullName} has Deleted Practice for Doctor {ProviderFullName} on {DateTime.UtcNow.AddMinutes(330)}",
                    LocationId = Convert.ToInt32(header.LocationId)
                };
                await this.auditLogServices.LogAsync(auditLogModel);


                return this.Success("Your availability has been deleted successfully.");
            }
            catch (NpgsqlException exception)
            {
                if (exception.Message.Contains("violates foreign key constraint"))
                {
                    return this.Conflict("Provider location can't be deleted. Please contact administrator.");
                }

                return this.ServerError();
            }
        }

        /// <summary>
        /// The modify status of provider location.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - ProviderLocation status updated successfully.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPut]
        [Authorize]
        [Route("modify-status")]
        [ProducesResponseType(typeof(string), 200)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> ModifyStatusAsync([FromBody] ProviderLocationModel model, [FromHeader] LocationHeader header)
        {
            model = (ProviderLocationModel)EmptyFilter.Handler(model);
            var response = await this.providerAvailabilityServices.ModifyStatusAsync(model.ProviderLocationId, Convert.ToInt32(model.ModifiedBy), model.Active);
            var ProviderFullName = await this.providerAvailabilityServices.FindProviderByProviderLocationId(model.ProviderLocationId);
            if (response == 0)
            {
                return this.ServerError();
            }

            var auditLogModel = new AuditLogModel
            {
                AccountId = model.ModifiedBy,
                LogTypeId = (int)LogTypes.Provider,
                LogFrom = (short)model.RoleId,
                LogDate = DateTime.UtcNow.AddMinutes(330),
                //LogDescription = $"{model.SpecializationName} specialization has been added."
                LogDescription = model.Active == true ? $"{model.FullName} has enabled the Doctor  {ProviderFullName} on {DateTime.UtcNow.AddMinutes(330)}" : $"{model.FullName} has disabled the Doctor  {ProviderFullName} on {DateTime.UtcNow.AddMinutes(330)}",
                LocationId = Convert.ToInt32(header.LocationId)

            };
            await this.auditLogServices.LogAsync(auditLogModel);

            return this.Success("Your availability status has been updated successfully.");
        }

        /// <summary>
        /// Fetches the discharge instructions asynchronous.
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        [Authorize]
        [Route("fetch-visit")]
        public async Task<ActionResult> FetchVisitTypesAsync()
        {
            try
            {
                var response = await this.providerAvailabilityServices.FetchVisitTypesAsync().ConfigureAwait(false);
                return Ok(new GenericResponse
                {
                    Status = GenericStatus.Success,
                    Data = response
                });
            }
            catch (Exception ex)
            {
                return Ok(new GenericResponse
                {
                    Status = GenericStatus.Error,
                    Message = ex.Message
                });
            }
        }

        /// <summary>
        /// Fetches the discharge instructions asynchronous.
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        [Authorize]
        [Route("fetch-visit-ip")]
        public async Task<ActionResult> FetchVisitTypeIpAsync()
        {
            try
            {
                var response = await this.providerAvailabilityServices.FetchVisitTypeIpAsync().ConfigureAwait(false);
                return Ok(new GenericResponse
                {
                    Status = GenericStatus.Success,
                    Data = response
                });
            }
            catch (Exception ex)
            {
                return Ok(new GenericResponse
                {
                    Status = GenericStatus.Error,
                    Message = ex.Message
                });
            }
        }

        /// <summary>
        /// Fetches the discharge instructions asynchronous.
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        [Authorize]
        [Route("fetch-charge-type")]
        public async Task<ActionResult> FetchChargeTypesAsync()
        {
            try
            {
                var response = await this.providerAvailabilityServices.FetchChargeTypesAsync().ConfigureAwait(false);
                return Ok(new GenericResponse
                {
                    Status = GenericStatus.Success,
                    Data = response
                });
            }
            catch (Exception ex)
            {
                return Ok(new GenericResponse
                {
                    Status = GenericStatus.Error,
                    Message = ex.Message
                });
            }
        }

        /// <summary>
        /// Modifies the provider operation location asynchronous.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <returns></returns>
        [HttpPost]
        [Authorize]
        [Route("modify-operation-availability")]
        public async Task<ActionResult> ModifyProviderOperationLocationAsync([FromBody] ProviderLocationModel model)
        {
            model = (ProviderLocationModel)EmptyFilter.Handler(model);
            var response = model.ProviderLocationOperationId > 0 ? await this.providerAvailabilityServices.UpdateOperationAvailabilityAsync(model) : await this.providerAvailabilityServices.AddOperationAvailabilityAsync(model);
            return this.Success(response);
        }

        /// <summary>
        /// Fetches the operation availability asynchronous.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <returns></returns>
        [HttpPost]
        [Authorize]
        [Route("fetch-operation-availability")]
        [ProducesResponseType(typeof(List<ProviderLocationModel>), 200)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> FetchOperationAvailabilityAsync([FromBody] ProviderLocationFilterModel model)
        {
            model = (ProviderLocationFilterModel)EmptyFilter.Handler(model);
            if (!string.IsNullOrEmpty(model.EncryptedProviderId))
            {
                model.ProviderId = Convert.ToInt32(this.aesHelper.Decode(model.EncryptedProviderId));
            }
            var providerLocations = await this.providerAvailabilityServices.FetchProviderOperationAvailabiltyAsync(model.ProviderId, null);
            return this.Success(providerLocations);
        }

        /// <summary>
        /// addes the add provider leave.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <returns></returns>
        [HttpPost]
        [Authorize]
        [Route("add-provider-leave")]
        [ProducesResponseType(typeof(List<InsertModel>), 200)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> AddProviderLeaveAsync([FromBody] InsertModel model)
        {
            model = (InsertModel)EmptyFilter.Handler(model);

            var providerLeave = await this.providerAvailabilityServices.AddProviderLeaveAsync(model);
            switch (providerLeave)
            {
                case -1:
                    return this.Conflict("Given Provider Leave was already on Leave.");
                case 0:
                    return this.ServerError();
            }

            var auditLogModel = new AuditLogModel
            {
                AccountId = model.CreatedBy,
                LogTypeId = (int)LogTypes.ProviderAvailability,
                LogFrom = (int)AccountType.Administrator,
                LogDate = DateTime.UtcNow.AddMinutes(330),
                LogDescription = $" { model.ModifiedByName } has applied Leave - {model.LeaveDates} on { DateTime.UtcNow.AddMinutes(330) }",
                LocationId = Convert.ToInt32(model.LeaveLocationId)
            };
            await this.auditLogServices.LogAsync(auditLogModel);

            return this.Success("Leave has been applied successfully.");
        }


        /// <summary>
        /// addes the add provider leave.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <returns></returns>
        [HttpPost]
        [Authorize]
        [Route("update-provider-leave")]
        [ProducesResponseType(typeof(List<InsertModel>), 200)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> UpdateProviderLeaveAsync([FromBody] UpdateModel model)
        {
            try
            {
                model = (UpdateModel)EmptyFilter.Handler(model);
                var providerLeave = await this.providerAvailabilityServices.UpdateProviderLeaveAsync(model);
                switch (providerLeave)
                {
                    case -1:
                        return this.Conflict("Leave Date has already been exists with us.");
                    case 0:
                        return this.ServerError();
                }

                var auditLogModel = new AuditLogModel
                {
                    AccountId = model.ModifiedBy,
                    LogTypeId = (int)LogTypes.ProviderAvailability,
                    LogFrom = (int)AccountType.Administrator,
                    LogDate = DateTime.UtcNow.AddMinutes(330),
                    LogDescription = $" { model.ModifiedByName } has updated Leave - {model.LeaveDates} on { DateTime.UtcNow.AddMinutes(330) }",
                    LocationId = Convert.ToInt32(model.LeaveLocationId)
                };
                await this.auditLogServices.LogAsync(auditLogModel);

                return this.Success("Leave has been updated successfully.");

            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// The delete charge type.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - charge type deleted successfully.
        /// - 409 - charge type can not be deleted.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Route("cancel-provider-leave")]
        [ProducesResponseType(typeof(string), 200)]
        [ProducesResponseType(409)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> CancelAsync([FromBody] CancelModel model, [FromHeader] LocationHeader header)
        {
            try
            {
                model = (CancelModel)EmptyFilter.Handler(model);
                model.LocationId = Convert.ToInt32(header.LocationId);

                var leaveDate = await this.providerAvailabilityServices.FindDateByProviderLeaveId(model.ProviderLeaveId);
                var response = await this.providerAvailabilityServices.CancelAsync(model.ProviderLeaveId);
                if (response == 0)
                {
                    return this.ServerError();
                }

                var auditLogModel = new AuditLogModel
                {
                    AccountId = model.ModifiedBy,
                    LogTypeId = (int)LogTypes.ProviderAvailability,
                    LogFrom = (int)AccountType.Administrator,
                    LogDate = DateTime.UtcNow.AddMinutes(330),
                    LogDescription = $" { model.ModifiedByName } has Cancelled Leave Date - {leaveDate} on { DateTime.UtcNow.AddMinutes(330) }",
                    LocationId = model.LocationId
                };
                await this.auditLogServices.LogAsync(auditLogModel);

                return this.Success("Leave has been Cancelled successfully.");
            }
            catch (NpgsqlException exception)
            {
                if (exception.Message.Contains("violates foreign key constraint"))
                {
                    return this.Conflict("Leave cant be cancelled. Please contact Administrator.");
                }

                return this.ServerError();
            }
        }

        /// <summary>
        /// The delete charge type.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - charge type deleted successfully.
        /// - 409 - charge type can not be deleted.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Route("cancel-provider-availability")]
        [ProducesResponseType(typeof(string), 200)]
        [ProducesResponseType(409)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> CancelAvailabilityAsync([FromBody] CancelModel model, [FromHeader] LocationHeader header)
        {
            try
            {
                model = (CancelModel)EmptyFilter.Handler(model);
                model.LocationId = Convert.ToInt32(header.LocationId);

                var availableDate = await this.providerAvailabilityServices.FindDateByProviderAvailabilityId(model.ProviderAvailabilityId);
                var response = await this.providerAvailabilityServices.CancelAvailabilityAsync(model.ProviderAvailabilityId);
                if (response == 0)
                {
                    return this.ServerError();
                }

                var auditLogModel = new AuditLogModel
                {
                    AccountId = model.ModifiedBy,
                    LogTypeId = (int)LogTypes.ProviderAvailability,
                    LogFrom = (int)AccountType.Administrator,
                    LogDate = DateTime.UtcNow.AddMinutes(330),
                    LogDescription = $" { model.ModifiedByName } has Cancelled Available Date - {availableDate} on { DateTime.UtcNow.AddMinutes(330) }",
                    LocationId = model.LocationId
                };
                await this.auditLogServices.LogAsync(auditLogModel);

                return this.Success("Availability has been Cancelled successfully.");
            }
            catch (NpgsqlException exception)
            {
                if (exception.Message.Contains("violates foreign key constraint"))
                {
                    return this.Conflict("Availability cant be cancelled. Please contact Administrator.");
                }

                return this.ServerError();
            }
        }

        /// <summary>
        /// The delete charge type.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - charge type deleted successfully.
        /// - 409 - charge type can not be deleted.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Route("cancel-provider-break")]
        [ProducesResponseType(typeof(string), 200)]
        [ProducesResponseType(409)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> CancelBreakAsync([FromBody] CancelModel model, [FromHeader] LocationHeader header)
        {
            try
            {
                model = (CancelModel)EmptyFilter.Handler(model);
                model.LocationId = Convert.ToInt32(header.LocationId);

                var availableDate = await this.providerAvailabilityServices.FindDateByProviderBreakId(model.ProviderBreakId);
                var response = await this.providerAvailabilityServices.CancelBreakAsync(model.ProviderBreakId);
                if (response == 0)
                {
                    return this.ServerError();
                }

                var auditLogModel = new AuditLogModel
                {
                    AccountId = model.ModifiedBy,
                    LogTypeId = (int)LogTypes.ProviderAvailability,
                    LogFrom = (int)AccountType.Administrator,
                    LogDate = DateTime.UtcNow.AddMinutes(330),
                    LogDescription = $" { model.ModifiedByName } has Cancelled Break Date - {availableDate} on { DateTime.UtcNow.AddMinutes(330) }",
                    LocationId = model.LocationId
                };
                await this.auditLogServices.LogAsync(auditLogModel);

                return this.Success("Break has been Cancelled successfully.");
            }
            catch (NpgsqlException exception)
            {
                if (exception.Message.Contains("violates foreign key constraint"))
                {
                    return this.Conflict("Break cant be cancelled. Please contact Administrator.");
                }

                return this.ServerError();
            }
        }
    }
}
