namespace Hims.Infrastructure.Services
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Dapper;
    using Domain.Entities;
    using Domain.Repositories.UnitOfWork;
    using Domain.Services;
    using Hims.Shared.DataFilters;
    using Hims.Shared.Library.Enums;
    using AvailabilityModel = Hims.Shared.UserModels.ProviderAvailability;
    using Hims.Shared.UserModels.Slots;
    using Shared.EntityModels;
    using Shared.UserModels;
    using Resource = Hims.Shared.UserModels.Discharge.Resource;
    using System.Globalization;
    using Hims.Shared.UserModels.Labs;
    using Newtonsoft.Json.Linq;
    using Hims.Shared.UserModels.ProviderSchedule;
    using System.Collections;

    /// <inheritdoc />
    public class ProviderLocationServices : IProviderLocationService
    {
        /// <summary>
        /// The unit of work.
        /// </summary>
        private readonly IUnitOfWork unitOfWork;

        /// <inheritdoc cref="IProviderLocationService" />
        public ProviderLocationServices(IUnitOfWork unitOfWork) => this.unitOfWork = unitOfWork;


        public async Task<List<TimeSlotModel>> FetchNewSlots(SlotRequest model)
        {
            var checkExistingPatient = new Appointment();
            var foundNewPatientVisitType = false;

            var consultationTypeCondition = model.ConsultationTypeId != null ? $@" and ""ConsultationTypeId"" = {model.ConsultationTypeId} " : string.Empty;
            var availabilityQuery = $@"select ""ProviderAvailabilityId"",""ProviderId"", ""SpecializationId"", ""LocationId"", ""StartTime"", ""EndTime"", ""NoOfNewPatient"", ""AvailableDate"", ""NoOfOfflinePatient""
                                                FROM ""ProviderAvailability"" 
                                            WHERE ""ProviderId"" = {model.ProviderId} and ""SpecializationId"" = {model.SpecializationId} 
                                            and ""LocationId"" = {model.LocationId} {consultationTypeCondition}
                                            and ""StartDate""::Date <= '{model.SlotDate}'::Date and ""EndDate""::Date >= '{model.SlotDate}'::Date  and ""Active"" is true";

            var availabilityResponse = await this.unitOfWork.Current.QueryAsync<ProviderAvailability>(availabilityQuery);

            if (model.PatientId > 0)
            {
                checkExistingPatient = await this.unitOfWork.Appointments.FindAsync(m => m.PatientId == model.PatientId && m.Status != "C");
            }

            if (model.PatientId != null || model.VisitTypeId > 0)
            {
                var findVisitTypeId = await this.unitOfWork.Visitors.FindAsync(m => m.VisitTypeId == model.VisitTypeId && m.Active == true);
                foundNewPatientVisitType = findVisitTypeId.VisitorName.Trim().ToLower() == "New Patient".Trim().ToLower();
            }

            if (availabilityResponse == null || !availabilityResponse.Any())
            {
                return new List<TimeSlotModel>();
            }

            var charge = new List<AvailabilityModel.AvailabilityModel>();

            AvailabilityModel.AvailabilityModel obj = new AvailabilityModel.AvailabilityModel();
            if (model.ChargeTypesId != null || model.LocationId != null || !string.IsNullOrEmpty(model.SlotDate) || model.ConsultationTypeId != null)
            {

                charge = (List<AvailabilityModel.AvailabilityModel>)await this.GetNewChargesAsync(model.ProviderId, model.ChargeTypesId, model.SpecializationId, model.LocationId, model.SlotDate, model.ConsultationTypeId);

                if (charge.Count > 0)
                {
                    obj = charge[0];
                }
            }

            var visitType = new List<AvailabilityModel.AvailabilityModel>();

            AvailabilityModel.AvailabilityModel objVisit = new AvailabilityModel.AvailabilityModel();
            //visitType = (List<AvailabilityModel.AvailabilityModel>)await this.GetVisitTypes(model.ProviderId, model.LocationId, model.SpecializationId, model.VisitTypeId);
            visitType = (List<AvailabilityModel.AvailabilityModel>)await this.GetDuration(model.ProviderId, model.LocationId, model.SpecializationId, model.VisitTypeId);

            if (visitType.Count > 0)
            {
                objVisit = visitType[0];

                if (objVisit.Duration == 0 || objVisit.Duration == null)
                {
                    return new List<TimeSlotModel>();
                }
            }
            else
            {
                return new List<TimeSlotModel>();
            }

            var availabilitSlotsSorted = availabilityResponse.OrderBy(x => x.StartTime);
            var slotDate = new DateTime();

            var allSlots = new List<TimeSlotModel>(); // allslots includes newpatient general slots and offline slots.
            var newPatientSlots = new List<TimeSlotModel>(); // only new patient Slots.
            var otherVisiTypeSlots = new List<TimeSlotModel>(); // with out new Patient Slots.
            var offlineSlots = new List<TimeSlotModel>(); // only offline slots.
            var breakSlots = new List<TimeSlotModel>(); // only break slots.
            foreach (var availability in availabilitSlotsSorted)
            {

                if (!string.IsNullOrEmpty(model.SlotDate))
                {
                    slotDate = Convert.ToDateTime(model.SlotDate.Trim());
                }

                var bookeAppointmentTimes = await this.FetchAppointmentTimesAsync(availability.ProviderAvailabilityId ?? 0, slotDate);

                var appointmenTimes = bookeAppointmentTimes.ToList();
                var startTimeTokens = availability.StartTime.Split(":");
                var endTimeTokens = availability.EndTime.Split(":");
                string[] slotDays = availability.AvailableDate.Split(',');
                if (slotDays.Contains(slotDate.ToString("yyyy-MM-dd")))
                {
                    DateTime startTime = new DateTime(
                    DateTime.Now.Year,
                    DateTime.Now.Month,
                    DateTime.Now.Day,
                    Convert.ToInt32(startTimeTokens[0]),
                    Convert.ToInt32(startTimeTokens[1]),
                    0
                );
                    DateTime endTime = new DateTime(
                        DateTime.Now.Year,
                        DateTime.Now.Month,
                        DateTime.Now.Day,
                        Convert.ToInt32(endTimeTokens[0]),
                        Convert.ToInt32(endTimeTokens[1]),
                        0
                    );

                    var counter = 0;


                    while (startTime <= endTime)
                    {
                        ++counter;
                        var nextTime = startTime.AddMinutes((double)objVisit.Duration);

                        var otherVisitType = new TimeSlotModel
                        {
                            TokenNumber = counter,
                            SlotValue = startTime.ToString("HH:mm"),
                            SlotValue24HoursEnd = nextTime.ToString("HH:mm"),
                            SlotName = startTime.ToString("hh:mm tt"),
                            SlotTime = startTime.TimeOfDay,
                            SlotName12HoursEnd = nextTime.ToString("hh:mm tt"),
                            Charge = (obj.Charge == null) ? 0 : obj.Charge,
                            Duration = objVisit.Duration,
                            Status = appointmenTimes.Any(a => a == startTime.TimeOfDay) ? SlotStatus.Booked : SlotStatus.Available,
                            ProviderAvailabilityId = availability.ProviderAvailabilityId,
                            DoctorSpecializationChargeModuleDetailsId = obj.DoctorSpecializationChargeModuleDetailsId,
                            SlotType = SlotType.GeneralSLots

                        };

                        otherVisiTypeSlots.Add(otherVisitType);
                        startTime = nextTime;
                    }
                }
                otherVisiTypeSlots.RemoveAt(otherVisiTypeSlots.Count - 1);
                var breakQuery = $@"SELECT ""ProviderBreakId"", ""ProviderId"", ""LocationId"", ""ConsultationTypeId"", ""Active"", ""CreatedBy"", ""CreatedDate"", ""ModifiedBy"", ""ModifiedDate"", ""StartDate"", ""EndDate"", ""SpecializationId"", ""BreakDay"", ""StartTime"", ""EndTime"", ""BreakDate"", ""BreakType"", ""BreakBlock""

                            FROM    ""ProviderBreak""

                                WHERE ""ProviderId"" = {model.ProviderId} and ""SpecializationId"" = {model.SpecializationId} 
                                            and ""LocationId"" = {model.LocationId} {consultationTypeCondition}
                                            and ""StartDate""::Date <= '{model.SlotDate}'::Date and ""EndDate""::Date >= '{model.SlotDate}'::Date";
                var breakResponse = await this.unitOfWork.Current.QueryAsync<ProviderBreak>(breakQuery);


                if (breakResponse.Any())
                { // if doctor has breaks.
                    var breakSlotsSorted = breakResponse.OrderBy(x => x.StartTime);

                    foreach (var breakSlot in breakSlotsSorted)
                    {
                        var breakStartTimeTokens = breakSlot.StartTime.Split(":");
                        var breakEndTimeTokens = breakSlot.EndTime.Split(":");
                        string[] breakDays = breakSlot.BreakDate.Split(',');
                        if (breakDays.Contains(slotDate.ToString("yyyy-MM-dd")))
                        {
                            DateTime breakStartTime = new DateTime(
                       DateTime.Now.Year,
                       DateTime.Now.Month,
                       DateTime.Now.Day,
                       Convert.ToInt32(breakStartTimeTokens[0]),
                       Convert.ToInt32(breakStartTimeTokens[1]),
                       0
                   );
                            DateTime breakEndTime = new DateTime(
                                DateTime.Now.Year,
                                DateTime.Now.Month,
                                DateTime.Now.Day,
                                Convert.ToInt32(breakEndTimeTokens[0]),
                                Convert.ToInt32(breakEndTimeTokens[1]),
                                0
                            );

                            var breakCounter = 0;


                            while (breakStartTime <= breakEndTime)
                            {
                                ++breakCounter;
                                var nextBreakTime = breakStartTime.AddMinutes((double)objVisit.Duration);
                                var otherVisitType = new TimeSlotModel
                                {
                                    TokenNumber = breakCounter,
                                    SlotValue = breakStartTime.ToString("HH:mm"),
                                    SlotValue24HoursEnd = nextBreakTime.ToString("HH:mm"),
                                    SlotName = breakStartTime.ToString("hh:mm tt"),
                                    SlotTime = breakStartTime.TimeOfDay,
                                    SlotName12HoursEnd = nextBreakTime.ToString("hh:mm tt"),
                                    Charge = (obj.Charge == null) ? 0 : obj.Charge,
                                    Duration = objVisit.Duration,
                                    Status = appointmenTimes.Any(a => a == breakStartTime.TimeOfDay) ? SlotStatus.Booked : SlotStatus.Available,
                                    ProviderAvailabilityId = availability.ProviderAvailabilityId,
                                    DoctorSpecializationChargeModuleDetailsId = obj.DoctorSpecializationChargeModuleDetailsId,
                                    SlotType = SlotType.BreakSlots

                                };

                                breakSlots.Add(otherVisitType);
                                breakStartTime = nextBreakTime;
                            }
                        }
                        breakSlots.RemoveAt(breakSlots.Count - 1); // to remove last element from breakSlots.
                    }
                    var removedBreakSlots = otherVisiTypeSlots.FindAll(x =>
                    {
                        return breakSlots.FindIndex(t => t.SlotValue == x.SlotValue) == -1; // removing break slots from All Slots.
                    });
                    _ = removedBreakSlots;

                    int newPatientGroupSize = (int)availability.NoOfNewPatient;
                    double newPatientBreakgroups = 0;
                    if (newPatientGroupSize > 0)
                    {
                        newPatientBreakgroups = removedBreakSlots.Count / newPatientGroupSize; //total slots / no of new patient slots after getting co-efficient need to divide new patient slots
                        for (int i = 0; i < removedBreakSlots.Count; i++)
                        {
                            if ((i + 1) % Math.Round(newPatientBreakgroups) == 0)
                            {
                                removedBreakSlots[i].SlotType = SlotType.NewPatientSlots;
                            }
                        }
                    }


                    allSlots = removedBreakSlots;

                    //newPatientSlots = removedBreakSlots.Where((element, index) => (index + 1) % groups == 0).Select(element => element).ToList(); // to get only new patient slots.

                    //for (int i = 0; i < newPatientSlots.Count; i++)
                    //{
                    //    newPatientSlots[i].SlotType = SlotType.NewPatientSlots; // replacing genral slots with new patient slots.
                    //}
                    //_ = newPatientSlots;

                    var remainingBreakElements = removedBreakSlots.Where((element, index) => (index + 1) % newPatientBreakgroups != 0).ToList(); // after removing new patient slots,to get remaining slots.

                    int offlineBreakPatientSize = (int)availability.NoOfOfflinePatient;

                    if (offlineBreakPatientSize > 0)
                    {
                        double offlineBreakGroups = remainingBreakElements.Count / offlineBreakPatientSize;

                        for (int i = 0; i < remainingBreakElements.Count; i++)
                        {
                            if ((i + 1) % Math.Round(offlineBreakGroups) == 0)
                            {
                                remainingBreakElements[i].SlotType = SlotType.OfflinePatientSlots;
                            }
                        }
                    }

                    //offlineSlots = remainingElements.Where((element, index) => (index + 1) % offlineGroups == 0).Select(element => element).ToList(); // to get only offline slots.
                    //otherVisiTypeSlots = remainingElements.Where((element, index) => (index + 1) % offlineGroups != 0).Select(element => element).ToList(); // to get general slots.

                    otherVisiTypeSlots = remainingBreakElements; // to get general slots and offline slots with slot type difference for genral and offline slots.
                }
                else
                { // if doctor has no breaks
                    int groupSize = (int)availability.NoOfNewPatient;
                    double groups = 0;
                    if (groupSize > 0)
                    {
                        groups = otherVisiTypeSlots.Count / groupSize; //total slots / no of new patient slots after getting co-efficient need to divide new patient slots

                        for (int i = 0; i < otherVisiTypeSlots.Count; i++)
                        {
                            if ((i + 1) % Math.Round(groups) == 0)
                            {
                                otherVisiTypeSlots[i].SlotType = SlotType.NewPatientSlots;
                            }
                        }
                    }

                    allSlots = otherVisiTypeSlots;

                    //newPatientSlots = otherVisiTypeSlots.Where((element, index) => (index + 1) % Math.Round(groups) == 0).Select(element => element).ToList(); // to get only new patient slots.

                    //for (int i = 0; i < newPatientSlots.Count; i++)
                    //{
                    //    newPatientSlots[i].SlotType = SlotType.NewPatientSlots; // replacing genral slots with new patient slots.
                    //}
                    //_ = newPatientSlots;

                    var remainingElements = otherVisiTypeSlots.Where((element, index) => (index + 1) % groups != 0).ToList(); // after removing new patient slots getting remaining slots.

                    int offlinePatientSize = (int)availability.NoOfOfflinePatient;

                    if (offlinePatientSize > 0)
                    {
                        double offlineGroups = remainingElements.Count / offlinePatientSize;

                        for (int i = 0; i < remainingElements.Count; i++)
                        {
                            if ((i + 1) % Math.Round(offlineGroups) == 0)
                            {
                                remainingElements[i].SlotType = SlotType.OfflinePatientSlots;
                            }
                        }
                    }
                    //offlineSlots = remainingElements.Where((element, index) => (index + 1) % offlineGroups == 0).Select(element => element).ToList(); // to get only offline slots.
                    //otherVisiTypeSlots = remainingElements.Where((element, index) => (index + 1) % offlineGroups != 0).Select(element => element).ToList(); // to get general slots.

                    otherVisiTypeSlots = remainingElements;
                }

            }

            if (model.PatientId == 0 || checkExistingPatient == null || foundNewPatientVisitType == true)
            {
                return allSlots;
            }
            else
            {
                return otherVisiTypeSlots;
            }

        }

        public async Task<IEnumerable<AvailabilityModel.AvailabilityModel>> GetChargesAsync(int[] availabilityIds, int chargeTypeId)
        {
            try
            {
                var query = $@"select
                ""ProviderAvailabilitySlotId"",""ProviderAvailabilityChargeTypeId"",
                ""Charge""
                from ""ProviderAvailabilityChargeType""
                WHERE ""ProviderAvailabilitySlotId"" in ({string.Join(",", availabilityIds)})
                    and ""ChargeTypesId"" = {chargeTypeId}";

                return await this.unitOfWork.Current.QueryAsync<AvailabilityModel.AvailabilityModel>(query);
            }
            catch (Exception)
            {
                return new List<AvailabilityModel.AvailabilityModel>();
            }
        }

        /// <inheritdoc />
        public Task<IEnumerable<ProviderAvailabilityModel>> FetchAsync(int providerId, int? specializationId, bool? active, int? locationId)
        {
            var where = $@" WHERE prv.""ProviderId"" = {providerId}";
            if (active != null)
            {
                where += $@" and prv.""Active""={(bool)active} ";
            }
            if (locationId != null)
            {
                where += $@" and prv.""LocationId"" ={locationId}";
            }

            if (specializationId != null)
            {
                where += $@" and prv.""SpecializationId"" = {specializationId}";
            }

            var query = $@"with cte as(
                                select prv.""ProviderAvailabilityId"",prv. ""ProviderId"",prv. ""LocationId"",prv. ""ConsultationTypeId"",prv. ""Active"",prv. ""CreatedBy"",prv. ""CreatedDate"",prv. ""ModifiedBy"",prv. ""ModifiedDate"",prv. ""StartDate"",prv. ""EndDate"",prv. ""SpecializationId"",prv. ""AvailableDay"",prv. ""StartTime"",prv. ""EndTime""
                                --,prv. ""FreeFollowUpDaysLimit"",prv. ""FreeFollowUpDays""
                                --,prv. ""AvailableDate""
                                ,prv. ""LeaveStartDate"",prv. ""LeaveEndDate"",prv. ""IsLeave"",prv. ""NoOfNewPatient"", c.""CurrencySymbol"", pra.""PracticeId"", L.""Name"" AS ""PracticeLocationName"", pra.""FullName"" AS ""PracticeName"", prv.""NoOfOfflinePatient"",
                                pra.""TIN"" AS ""PracticeTIN""
								,unnest(string_to_array(prv.""AvailableDate"",','))::Date as ""AvailableDate""
                                FROM ""ProviderAvailability"" prv
								 join ""Location"" L on L.""LocationId"" = prv.""LocationId""                                
                                JOIN ""Practice"" pra ON pra.""PracticeId"" = L.""PracticeId"" AND pra.""Active"" IS TRUE
								JOIN ""Country"" c ON c.""CountryId"" = L.""CountryId""
                                 {where}	)
	                        select * from cte where cte.""AvailableDate"" >= current_date
	                        ORDER BY cte.""ProviderAvailabilityId"" desc";
            return this.unitOfWork.Current.QueryAsync<ProviderAvailabilityModel>(query);
        }

        /// <inheritdoc />
        public Task<ProviderLocationModel> FindAsync(int providerLocationId, int patientId, int? locationId)
        {
            var query = $@"Select  PL.""ProviderLocationId"", PL.""ProviderId"", PL.""LocationId"", c.""CurrencySymbol"",
                                PL.""ConsultationCharges"", PL.""TelemedicineCharges"", PL.""Duration"",
                                PL.""ConsultationDuration"",
                            PL.""TelemedicineDuration"", PL.""InPatientDuration"", PL.""OutPatientDuration"",
                        PL.""CasualtyDuration"", PL.""AvailableDays"", PL.""Availability"",

                                CASE WHEN (SELECT Count(""AppointmentId"") FROM ""Appointment"" WHERE ""ProviderId"" = PL.""ProviderId"" AND ""PatientId"" = 0 ) > 0 THEN TRUE ELSE FALSE END ""IsFallowUp""
                                
                                    from ""ProviderLocation"" PL
                                JOIN ""Location"" pral ON pral.""LocationId"" = pl.""LocationId"" AND pral.""Active"" IS TRUE
								JOIN ""Country"" c ON c.""CountryId"" = pral.""CountryId""
                                where PL.""ProviderLocationId"" = {providerLocationId} and PL.""Active"" IS True and  PL.""LocationId"" = {locationId} ";
            return this.unitOfWork.Current.QueryFirstOrDefaultAsync<ProviderLocationModel>(query);
        }

        /// <inheritdoc />
        public Task<TaxModel> FetchTaxAsync()
        {
            var query = $@"Select * from ""Tax"" where ""Active"" is true";
            return this.unitOfWork.Current.QueryFirstOrDefaultAsync<TaxModel>(query);
        }

        /// <inheritdoc />
        public Task<IEnumerable<ProviderAvailabilityModel>> FetchAvailabilitiesAsync(int providerId, int? providerLocationId, string fromDate, string toDate)
        {
            var query = providerLocationId == null
                            ? $@"SELECT * FROM ""udf_fetchAvailabilities""({providerId}, null, '{fromDate}', '{toDate}')"
                            : $@"SELECT * FROM ""udf_fetchAvailabilities""({providerId}, {providerLocationId}, '{fromDate}', '{toDate}')";

            return this.unitOfWork.Current.QueryAsync<ProviderAvailabilityModel>(query);
        }

        /// <inheritdoc />
        public Task<IEnumerable<ProviderAvailabilityDatesModel>> FetchAvailabilityDatesAsync(int providerId, int? locationId)
        {
            var query = $@"SELECT * FROM ""udf_fetchAvailableDates""({providerId},{locationId})";

            return this.unitOfWork.Current.QueryAsync<ProviderAvailabilityDatesModel>(query);
        }

        /// <inheritdoc />
        //public Task<IEnumerable<TimeSpan>> FetchAppointmentTimesAsync(int providerLocationId, DateTime appointmentDate)
        //{
        //    var query = $@"Select distinct ""AppointmentTime"" from ""Appointment"" where ""ProviderLocationId"" = '{providerLocationId}' AND ""AppointmentDate"" = '{appointmentDate.ToString("yyyy-MM-dd")}' AND ""Active"" IS TRUE";
        //    return this.unitOfWork.Current.QueryAsync<TimeSpan>(query);
        //}

        /// <inheritdoc />
        public Task<IEnumerable<TimeSpan>> FetchAppointmentTimesAsync(int providerAvailableId, DateTime appointmentDate)
        {
            var query = $@"Select distinct ""AppointmentTime"" from ""Appointment"" where ""ProviderAvailabilityId"" = '{providerAvailableId}' AND ""AppointmentDate"" = '{appointmentDate.ToString("yyyy-MM-dd")}' AND ""Active"" IS TRUE AND ""Status"" != 'C'";
            return this.unitOfWork.Current.QueryAsync<TimeSpan>(query);
        }

        /// <inheritdoc />
        public Task<IEnumerable<TimeSpan>> FetchSessionAppointmentTimesAsync(int sessionId, DateTime appointmentDate)
        {
            var query = $@"Select distinct ""AppointmentTime"" from ""Appointment"" where ""SessionId"" = '{sessionId}' AND ""AppointmentDate"" = '{appointmentDate.ToString("yyyy-MM-dd")}' AND ""Active"" IS TRUE AND ""Status"" != 'C'";
            return this.unitOfWork.Current.QueryAsync<TimeSpan>(query);
        }

        /// <inheritdoc />
        public async Task<int> AddAsync(ProviderLocationModel model)
        {
            var checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(""ProviderLocationId"") FROM ""ProviderLocation"" WHERE ""LocationId"" = '{model.LocationId}' AND  ""ProviderId"" = '{model.ProviderId}'");
            if (checkIf > 0)
            {
                return -1;
            }
            #region accountChecking
            var findProviderAccountId = await this.unitOfWork.Accounts.FindAsync(a => a.ReferenceId == model.ProviderId && a.RoleId == (int)Roles.Provider);
            if (findProviderAccountId == null)
            {
                return -1;
            }
            var findInAccountMap = await this.unitOfWork.LocationAccountMap.FindAsync(f => f.AccountId == findProviderAccountId.AccountId && f.LocationId == model.LocationId);
            if (findInAccountMap == null)
            {
                var accountMapModel = new LocationAccountMap
                {
                    AccountId = findProviderAccountId.AccountId,
                    LocationId = model.LocationId
                };
                await this.unitOfWork.LocationAccountMap.InsertAsync(accountMapModel);
            }
            #endregion


            var providerLocation = new ProviderLocation
            {
                Active = true,
                ProviderId = model.ProviderId,
                LocationId = model.LocationId,
                ConsultationDuration = model.ConsultationDuration,
                ConsultationCharges = model.ConsultationCharges,
                TelemedicineDuration = model.TelemedicineDuration,
                TelemedicineCharges = model.TelemedicineCharges,
                InPatientDuration = model.InPatientDuration,
                OutPatientDuration = model.OutPatientDuration,
                CasualtyDuration = model.CasualtyDuration,
                FollowUpDays = model.FollowUpDays,
                Availability = model.Availability,
                AvailableDays = model.AvailableDays,
                CreatedBy = model.CreatedBy,
                CreatedDate = DateTime.UtcNow,
                FollowUpDaysForIp = model.FollowUpDaysForIp,
                Duration = model.Duration,
                IsTelemedicine = model.IsTelemedicine,
                IsOnlineConsultation = model.IsOnlineConsultation,
            };

            return await this.unitOfWork.ProviderLocations.InsertAsync(providerLocation);
        }

        /// <inheritdoc />
        public async Task<int> UpdateAsync(ProviderLocationModel model)
        {
            var checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(""ProviderLocationId"") FROM ""ProviderLocation"" WHERE ""LocationId"" = '{model.LocationId}' AND  ""ProviderId"" = '{model.ProviderId}' AND ""ProviderLocationId"" <> {model.ProviderLocationId}");
            if (checkIf > 0)
            {
                return -1;
            }

            var providerLocation = await this.unitOfWork.ProviderLocations.FindAsync(m => m.ProviderLocationId == model.ProviderLocationId);
            providerLocation.ProviderId = model.ProviderId;
            providerLocation.LocationId = model.LocationId;
            providerLocation.ConsultationDuration = model.ConsultationDuration;
            providerLocation.ConsultationCharges = model.ConsultationCharges;
            providerLocation.TelemedicineDuration = model.TelemedicineDuration;
            providerLocation.TelemedicineCharges = model.TelemedicineCharges;
            providerLocation.InPatientDuration = model.InPatientDuration;
            providerLocation.OutPatientDuration = model.OutPatientDuration;
            providerLocation.CasualtyDuration = model.CasualtyDuration;
            providerLocation.FollowUpDays = model.FollowUpDays;
            providerLocation.AvailableDays = model.AvailableDays;
            providerLocation.Availability = model.Availability;
            providerLocation.ModifiedBy = model.ModifiedBy;
            providerLocation.ModifiedDate = DateTime.UtcNow;
            providerLocation.FollowUpDaysForIp = model.FollowUpDaysForIp;
            providerLocation.Duration = model.Duration;
            providerLocation.IsTelemedicine = model.IsTelemedicine;
            providerLocation.IsOnlineConsultation = model.IsOnlineConsultation;
            return await this.unitOfWork.ProviderLocations.UpdateAsync(providerLocation);
        }

        /// <inheritdoc />
        public Task<int> DeleteAsync(int providerLocationId)
        {
            var query = $@"DELETE FROM ""ProviderLocation"" WHERE ""ProviderLocationId""= {providerLocationId}";
            return this.unitOfWork.Current.ExecuteAsync(query);
        }

        /// <inheritdoc />
        public Task<int> ModifyStatusAsync(int providerLocationId, int modifiedBy, bool status)
        {
            var query = $@"UPDATE ""ProviderLocation"" SET ""Active"" = {status}, ""ModifiedBy"" = {modifiedBy}, ""ModifiedDate"" = NOW() AT TIME ZONE 'UTC' WHERE ""ProviderLocationId""= {providerLocationId}";
            return this.unitOfWork.Current.ExecuteAsync(query);
        }

        /// <inheritdoc />
        public async Task<string> FindProviderByProviderLocationId(int providerLocationId)
        {
            //var query = $@"SELECT ""FullName"" FROM ""ProviderLocation"" pl WHERE PL.""ProviderId"" = (SELECT ""ProviderId"" FROM ""ProviderLocation"" pl WHERE .""ProviderLocationId"" = {providerLocationId})";
            var query = $@"SELECT p.""FullName"" as ""ProviderName"" FROM ""ProviderLocation"" PL join ""Provider"" p on p.""ProviderId""=PL.""ProviderId"" WHERE ""ProviderLocationId"" ={providerLocationId} ";

            var response = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<string>(query);
            return response;
        }

        /// <inheritdoc />
        public async Task<IEnumerable<Resource.ViewModel>> FetchVisitTypesAsync()
        {
            var query = $@"SELECT
	                        ""VisitTypeId"" AS ""Id"",
	                        ""VisitorName"" AS ""Name"" 
                        FROM
	                        ""VisitType"" where ""VisitTypeFor"" = 'OP' and ""Active"" is true ";
            var records = await this.unitOfWork.Current.QueryAsync<Resource.ViewModel>(query);
            return records;
        }

        /// <inheritdoc />
        public async Task<IEnumerable<Resource.ViewModel>> FetchVisitTypeIpAsync()
        {
            var query = $@"SELECT
	                        ""VisitTypeId"" AS ""Id"",
	                        ""VisitorName"" AS ""Name"" 
                        FROM
	                        ""VisitType"" where ""VisitTypeFor"" = 'IP' and ""Active"" is true";
            var records = await this.unitOfWork.Current.QueryAsync<Resource.ViewModel>(query);
            return records;
        }

        /// <inheritdoc />
        public async Task<IEnumerable<Resource.ViewModel>> FetchChargeTypesAsync(ChargeTypesModel model)
        {
            var where = $@" WHERE 1 =1 ";

            if (model.ProviderId != null)
            {
                where += $@" and DSM.""ProviderId"" ={model.ProviderId} ";
            }
            if (model.SpecializationId != null)
            {
                where += $@" and DSM.""SpecializationId"" ={model.SpecializationId} ";
            }
            if (model.ConsultationTypeId != null)
            {
                where += $@" and DSM.""ConsultationTypeId"" ={model.ConsultationTypeId} ";
            }
            if (model.LocationId != null)
            {
                where += $@" and CMT.""LocationId"" ={model.LocationId} ";
            }
            if (model.AppointmentDate != null)
            {
                where += $@" and CMT.""StartDate""::Date <= '{model.AppointmentDate}'::Date 
                             and  CMT.""EndDate""::Date >= '{model.AppointmentDate}'::Date ";
            }


            var query = $@"select distinct  DSCC.""ChargeTypesId"" AS ""Id"" , CT.""ChargeName"" AS ""Name""
							from ""DoctorSpecializationMap"" DSM
							join ""DoctorSpecializationChargeModuleDetails"" DSCM on DSCM.""ReferenceId"" = DSM.""DoctorSpecializationMapId""
							join ""DoctorSpecializationChargeModuleCategory"" DSCC on DSCC.""DoctorSpecializationChargeModuleCategoryId"" = 													DSCM.""DoctorSpecializationChargeModuleCategoryId"" and DSCC.""Active"" is true
							join  ""ChargeTypes"" CT on CT.""ChargeTypesId"" = DSCC.""ChargeTypesId"" and  CT.""Active"" is true 
							join ""ChargeModuleTemplate"" CMT on CMT.""ChargeModuleTemplateId"" = DSCC.""ChargeModuleTemplateId"" and CMT.""IsInUse"" is true 
                            {where} ";

            var records = await this.unitOfWork.Current.QueryAsync<Resource.ViewModel>(query);
            return records;
        }

        /// <inheritdoc />
        public async Task<int> AddOperationAvailabilityAsync(ProviderLocationModel model)
        {
            var availability = new ProviderLocationOperation
            {
                Availability = model.Availability,
                AvailableDays = model.AvailableDays,
                CreatedBy = model.CreatedBy,
                CreatedDate = DateTime.Now,
                Duration = int.Parse(model.Duration),
                PracticeLocationId = model.LocationId,
                ProviderId = model.ProviderId
            };

            return await this.unitOfWork.ProviderLocationOperations.InsertAsync(availability);
        }

        /// <inheritdoc />
        public Task<IEnumerable<ProviderLocationModel>> FetchProviderOperationAvailabiltyAsync(int providerId, bool? active)
        {
            var where = $@" WHERE prlo.""ProviderId"" = {providerId} ";
            if (active != null)
            {
                where += $@" and prlo.""Active""={(bool)active} ";
            }

            var query = $@"SELECT prlo.*, c.""CurrencySymbol"", pra.""PracticeId"", pral.""FullName"" AS ""PracticeLocationName"", 
                                pra.""FullName"" AS ""PracticeName"", pra.""TIN"" AS ""PracticeTIN""
                                FROM ""ProviderLocationOperation"" prlo
                                JOIN ""PracticeLocation"" pral ON pral.""PracticeLocationId"" = prlo.""PracticeLocationId"" AND pral.""Active"" IS TRUE
                                JOIN ""Practice"" pra ON pra.""PracticeId"" = pral.""PracticeId"" AND pra.""Active"" IS TRUE
								JOIN ""Country"" c ON c.""CountryId"" = pral.""CountryId""
                                 {where}
                                ORDER BY prlo.""ProviderLocationOperationId"" DESC";
            return this.unitOfWork.Current.QueryAsync<ProviderLocationModel>(query);
        }

        public async Task<int> UpdateOperationAvailabilityAsync(ProviderLocationModel model)
        {
            var availability = await this.unitOfWork.ProviderLocationOperations.FindAsync(m => m.ProviderLocationOperationId == model.ProviderLocationOperationId);
            if (availability == null)
            {
                return -1;
            }

            availability.ModifiedBy = model.CreatedBy;
            availability.ModifiedDate = DateTime.Now;
            availability.Availability = model.Availability;
            availability.AvailableDays = model.AvailableDays;
            availability.Duration = int.Parse(model.Duration);
            availability.PracticeLocationId = model.LocationId;

            return await this.unitOfWork.ProviderLocationOperations.UpdateAsync(availability);
        }

        /// <inheritdoc />
        public Task<IEnumerable<ProviderAvailabilityDatesModel>> FetchProviderAvailabilityDatesAsync(int providerId, int? locationId, int? specializationId, string? startDate, string? endDate, int? consultationTypeId)
        {
            var query = $@"SELECT * FROM ""udf_fetchProviderAvailabilityDatesNew""({providerId}, {locationId}, {specializationId}, '{startDate}', '{endDate}', {consultationTypeId})";

            return this.unitOfWork.Current.QueryAsync<ProviderAvailabilityDatesModel>(query);
        }

        public async Task<IEnumerable<Resource.ViewModel>> FetchVisitTypesNewAsync(VisitTypesRequestModel model)
        {
            var query = $@"SELECT PA.""Duration"" as ""Value"" FROM ""DoctorAvailabilityVisitType"" PA
                            --JOIN ""VisitType"" VT ON VT.""VisitTypeId"" = PA.""VisitTypeId""
                            WHERE PA.""LocationId"" = '{model.LocationId}' AND PA.""ProviderId"" = '{model.ProviderId}' AND PA.""SpecializationId"" = '{model.SpecializationId}' and PA.""Active"" is true ";
            var records = await this.unitOfWork.Current.QueryAsync<Resource.ViewModel>(query);
            return records;
        }

        public async Task<IEnumerable<Resource.ViewModel>> FetchChargeTypesNewAsync(ChargeTypesRequestModel model)
        {
            var where = $@" where 1=1 ";
            if (model.ProviderId != null)
            {
                where += $@" and PA.""ProviderId"" = {model.ProviderId}";
            }
            if (model.SpecializationId != null)
            {
                where += $@" and PA.""SpecializationId"" = {model.SpecializationId}";
            }
            if (model.LocationId != null)
            {
                where += $@" and PA.""LocationId"" = {model.LocationId}";
            }
            if (model.AppointmentDate != null)
            {
                where += $@" AND PA.""StartDate""::DATE <= '{model.AppointmentDate}'::DATE and PA.""EndDate""::DATE >= '{model.AppointmentDate}'::DATE";
            }
            var query = $@"SELECT CT.""ChargeTypesId"" AS ""Id"", CT.""ChargeName"" AS ""Name"", PA.""Charge"" as ""Value""
                            FROM ""ProviderAvailabilityChargeType"" PA
                            JOIN ""ChargeTypes"" CT ON CT.""ChargeTypesId"" = PA.""ChargeTypesId""
                            {where}";
            var records = await this.unitOfWork.Current.QueryAsync<Resource.ViewModel>(query);
            return records;
        }


        public async Task<IEnumerable<AvailabilityModel.AvailabilityModel>> GetNewChargesAsync(int? providerId, int? chargeTypeId, int? specializationId, int? locationId, string? appointmentDate, int? consultationTypeId)
        {
            try
            {
                var where = $@" ";
                if (providerId != null)
                {
                    where += $@"{providerId}";
                }
                else
                {
                    where += $@"  null";
                }
                if (specializationId != null)
                {
                    where += $@",{specializationId}";
                }
                else
                {
                    where += $@",  null";
                }
                if (locationId != null)
                {
                    where += $@", {locationId}";
                }
                else
                {
                    where += $@",  null";
                }
                if (appointmentDate != null)
                {
                    where += $@", '{appointmentDate}'";
                }
                else
                {
                    where += $@",  null";
                }

                if (chargeTypeId != null)
                {
                    where += $@" ,{chargeTypeId}";
                }
                else
                {
                    where += $@",  null";
                }

                if (consultationTypeId != null)
                {
                    where += $@",{consultationTypeId}";
                }
                else
                {
                    where += $@",  null";
                }
                var query = $@"select * from ""UDF_Provider_Charges_From_Charge_Module""({where})";

                return await this.unitOfWork.Current.QueryAsync<AvailabilityModel.AvailabilityModel>(query);

            }
            catch (Exception ex)
            {
                ex.Message.ToString();
                return null;
            }
        }

        public async Task<IEnumerable<AvailabilityModel.AvailabilityModel>> GetVisitTypes(int? providerId, int? locationId, int? specializationId, int? visitTypeId)
        {
            try
            {
                var where = $@" ";
                if (providerId != null)
                {
                    where += $@"{providerId}";
                }
                else
                {
                    where += $@"  null";
                }
                if (specializationId != null)
                {
                    where += $@",{specializationId}";
                }
                else
                {
                    where += $@",  null";
                }
                if (locationId != null)
                {
                    where += $@", {locationId}";
                }
                else
                {
                    where += $@",  null";
                }
                if (visitTypeId != null)
                {
                    where += $@", {visitTypeId}";
                }
                else
                {
                    where += $@",  null";
                }

                var query = $@"select * from ""udf_provider_availability_visitType1""({where})";

                return await this.unitOfWork.Current.QueryAsync<AvailabilityModel.AvailabilityModel>(query);
                //if (records.Count() == 0) {
                //    return null;
                //}
                //return records;
            }
            catch (Exception ex)
            {
                ex.Message.ToString();
                return null;
            }
        }

        public async Task<IEnumerable<AvailabilityModel.AvailabilityModel>> GetDuration(int? providerId, int? locationId, int? specializationId, int? visitTypeId)
        {
            try
            {
                var where = " WHERE 1 = 1 ";
                if (locationId != null)
                {
                    where += $@" AND PAS.""LocationId"" = {locationId}";
                }
                if ((providerId != 0) && (providerId != null))
                {
                    where += $@" AND PAS.""ProviderId"" = {providerId}";
                }
                if ((specializationId != 0) && (specializationId != null))
                {
                    where += $@" AND PAS.""SpecializationId"" = {specializationId}";
                }

                var query = $@"select PAS.""DoctorAvailabilityVisitTypeId"" as ""ProviderAvailabilityVisitTypeId"", PAS.""Duration"" from ""DoctorAvailabilityVisitType"" PAS 
                    {where} and PAS.""Active"" is true";

                return await this.unitOfWork.Current.QueryAsync<AvailabilityModel.AvailabilityModel>(query);
                //if (records.Count() == 0) {
                //    return null;
                //}
                //return records;
            }
            catch (Exception ex)
            {
                ex.Message.ToString();
                return null;
            }
        }

        public async Task<List<MultipleDays>> FetchSlotsForMultipleDays(SlotRequest model)
        {
            DateTime dt = DateTime.Now;
            var date = dt.Date;
            var count = 0;
            if (model.FromDate == null && model.ToDate == null)
            {
                count = (int)model.Count;
            }
            else
            {
                var from = model.FromDate;
                DateTime convertFromDat = DateTime.Parse(from);
                var d = Convert.ToDateTime(convertFromDat).Date;
                var toDate = model.ToDate;
                DateTime convertToDate = DateTime.Parse(toDate);
                var f = Convert.ToDateTime(convertToDate).Date;
                var diff = f.Subtract(d);
                count = ((int)diff.TotalDays) + 1;
            }

            string dateString = null;
            var dayWiseSlots = new List<MultipleDays>();

            for (var n = 1; n <= count; n++)
            {
                if (model.FromDate != null)
                {
                    if (n == 1)
                    {
                        date = Convert.ToDateTime(model.FromDate);
                    }
                    dateString = date.ToString("yyyy-MM-dd");
                    date = date.AddDays(1);

                }
                else
                {
                    if (n == 1)
                    {
                        dt = date.AddDays(1);
                    }
                    else
                    {
                        dt = date.AddDays(n);
                    }
                    dateString = dt.ToString("yyyy-MM-dd");
                }
                var availabilityQuery = $@"with cts as (select ""ProviderAvailabilityId"",""ProviderId"", ""SpecializationId"", ""LocationId"", ""StartTime"", ""EndTime"", ""NoOfNewPatient"",""NoOfOfflinePatient"", ""SpecializationDuration""
			 ,unnest(string_to_array(""AvailableDate"", ','))::date as ""AvailableDate""
                                                FROM ""ProviderAvailability""
                                           WHERE ""ProviderId"" = {model.ProviderId} and ""Active"" is true and ""SpecializationId"" = {model.SpecializationId} 
                                          and ""LocationId"" = {model.LocationId} and ""ConsultationTypeId"" = {model.ConsultationTypeId})
										select* from ""cts""
                                       where ""AvailableDate""='{dateString}'";

                var availabilityResponse = await this.unitOfWork.Current.QueryAsync<ProviderAvailabilityForMultipleDays>(availabilityQuery);


                var chargeTypesModel = new ChargeTypesModel();
                chargeTypesModel.AppointmentDate = dateString;
                chargeTypesModel.ProviderId = model.ProviderId;
                chargeTypesModel.SpecializationId = model.SpecializationId;
                chargeTypesModel.ConsultationTypeId = model.ConsultationTypeId;
                chargeTypesModel.LocationId = model.LocationId;

                var chargeTypes = (List<Resource.ViewModel>)await this.FetchChargeTypesAsync(chargeTypesModel);
                if (chargeTypes.Count > 0)
                {
                    var chargeTypesId = chargeTypes.Find(s => s.Name == "Payable");
                    model.ChargeTypesId = chargeTypesId.Id;
                }
                model.AppointmentDate = dateString;
                var charge = new List<AvailabilityModel.AvailabilityModel>();
                AvailabilityModel.AvailabilityModel obj = new AvailabilityModel.AvailabilityModel();
                if (model.ChargeTypesId != null || model.LocationId != null || !string.IsNullOrEmpty(model.AppointmentDate) || model.ConsultationTypeId != null)
                {

                    charge = (List<AvailabilityModel.AvailabilityModel>)await this.GetNewChargesAsync(model.ProviderId, model.ChargeTypesId, model.SpecializationId, model.LocationId, model.AppointmentDate, model.ConsultationTypeId);

                    if (charge.Count > 0)
                    {
                        obj = charge[0];
                    }
                }

                var visitType = new List<AvailabilityModel.AvailabilityModel>();

                AvailabilityModel.AvailabilityModel objVisit = new AvailabilityModel.AvailabilityModel();
                visitType = (List<AvailabilityModel.AvailabilityModel>)await this.GetDuration(model.ProviderId, model.LocationId, model.SpecializationId, model.VisitTypeId);

                if (visitType.Count > 0)
                {
                    objVisit = visitType[0];

                    if (objVisit.Duration == 0 || objVisit.Duration == null)
                    {
                        return new List<MultipleDays>();
                    }
                }
                else
                {
                    return new List<MultipleDays>();
                }

                var availabilitSlotsSorted = availabilityResponse.OrderBy(x => x.StartTime);
                var slotDate = new DateTime();

                var slots = new List<TimeSlotModel>();
                var otherVisiTypeSlots = new List<TimeSlotModel>();
                var offlineSlots = new List<TimeSlotModel>();
                var breakSlots = new List<TimeSlotModel>();
                var allSlots = new List<TimeSlotModel>();

                var num = 0;
                if (availabilityResponse.Any())
                {
                    foreach (var availability in availabilitSlotsSorted)
                    {
                        num = num + 100;
                        otherVisiTypeSlots = new List<TimeSlotModel>();
                        if (!string.IsNullOrEmpty(model.SlotDate))
                        {
                            slotDate = Convert.ToDateTime(model.SlotDate.Trim());
                        }
                        var bookeAppointmentTimes = await this.FetchAppointmentTimesAsync(availability.ProviderAvailabilityId ?? 0, availability.AvailableDate);
                        var appointmenTimes = bookeAppointmentTimes.ToList();
                        var startTimeTokens = availability.StartTime.Split(":");
                        var endTimeTokens = availability.EndTime.Split(":");
                        DateTime startTime = new DateTime(
                        DateTime.Now.Year,
                        DateTime.Now.Month,
                        DateTime.Now.Day,
                        Convert.ToInt32(startTimeTokens[0]),
                        Convert.ToInt32(startTimeTokens[1]),
                        0
                    );
                        DateTime endTime = new DateTime(
                            DateTime.Now.Year,
                            DateTime.Now.Month,
                            DateTime.Now.Day,
                            Convert.ToInt32(endTimeTokens[0]),
                            Convert.ToInt32(endTimeTokens[1]),
                            0
                        );
                        var counter = 0;
                        while (startTime <= endTime)
                        {
                            ++counter;
                            var nextTime = startTime.AddMinutes((double)availability.SpecializationDuration);
                            var otherVisitType = new TimeSlotModel
                            {
                                TokenNumber = counter,
                                SlotValue = startTime.ToString("HH:mm"),
                                SlotValue24HoursEnd = nextTime.ToString("HH:mm"),
                                SlotName = startTime.ToString("hh:mm tt"),
                                SlotTime = startTime.TimeOfDay,
                                SlotName12HoursEnd = nextTime.ToString("hh:mm tt"),
                                Charge = (obj.Charge == null) ? 0 : obj.Charge,
                                Duration = availability.SpecializationDuration,
                                Status = appointmenTimes.Any(a => a == startTime.TimeOfDay) ? SlotStatus.Booked : SlotStatus.Available,
                                ProviderAvailabilityId = availability.ProviderAvailabilityId,
                                AvailableDate = dateString,
                                Id = num + "" + counter,
                                ChargeTypesId = model.ChargeTypesId
                            };
                            otherVisiTypeSlots.Add(otherVisitType);
                            startTime = nextTime;
                        }
                        otherVisiTypeSlots.RemoveAt(otherVisiTypeSlots.Count - 1);
                        var breakQuery = $@"with break as (select ""ProviderBreakId"", ""ProviderId"", ""LocationId"", ""ConsultationTypeId"", ""Active"", ""CreatedBy"", ""CreatedDate"", 
                                ""ModifiedBy"", ""ModifiedDate"", ""StartDate"", ""EndDate"", ""SpecializationId"", ""BreakDay"", ""StartTime"", ""EndTime"",
                                    ""BreakType"", ""BreakBlock""
			                    ,unnest(string_to_array(""BreakDate"", ','))::date as ""BreakDate""
                                                FROM ""ProviderBreak""
                                             WHERE ""ProviderId"" = {model.ProviderId} and ""SpecializationId"" = {model.SpecializationId} 
                                            and ""LocationId"" = {model.LocationId} and ""ConsultationTypeId"" ={model.ConsultationTypeId})
										select * from ""break""
                                       where ""BreakDate""='{dateString}'";
                        var breakResponse = await this.unitOfWork.Current.QueryAsync<ProviderBreak>(breakQuery);
                        if (breakResponse.Any())
                        {
                            var breakSlotsSorted = breakResponse.OrderBy(x => x.StartTime);

                            foreach (var breakSlot in breakSlotsSorted)
                            {
                                var breakStartTimeTokens = breakSlot.StartTime.Split(":");
                                var breakEndTimeTokens = breakSlot.EndTime.Split(":");
                                string[] breakDays = breakSlot.BreakDate.Split(',');

                                DateTime breakStartTime = new DateTime(
                                DateTime.Now.Year,
                                DateTime.Now.Month,
                                DateTime.Now.Day,
                                Convert.ToInt32(breakStartTimeTokens[0]),
                                Convert.ToInt32(breakStartTimeTokens[1]),
                                0);
                                DateTime breakEndTime = new DateTime(
                                    DateTime.Now.Year,
                                    DateTime.Now.Month,
                                    DateTime.Now.Day,
                                    Convert.ToInt32(breakEndTimeTokens[0]),
                                    Convert.ToInt32(breakEndTimeTokens[1]),
                                    0
                                );

                                var breakCounter = 0;


                                while (breakStartTime <= breakEndTime)
                                {
                                    ++breakCounter;
                                    var nextBreakTime = breakStartTime.AddMinutes((double)availability.SpecializationDuration);
                                    var otherVisitType = new TimeSlotModel
                                    {
                                        TokenNumber = breakCounter,
                                        SlotValue = breakStartTime.ToString("HH:mm"),
                                        SlotValue24HoursEnd = nextBreakTime.ToString("HH:mm"),
                                        SlotName = breakStartTime.ToString("hh:mm tt"),
                                        SlotTime = breakStartTime.TimeOfDay,
                                        SlotName12HoursEnd = nextBreakTime.ToString("hh:mm tt"),
                                        Charge = (obj.Charge == null) ? 0 : obj.Charge,
                                        Duration = availability.SpecializationDuration,
                                        Status = appointmenTimes.Any(a => a == breakStartTime.TimeOfDay) ? SlotStatus.Booked : SlotStatus.Available,
                                        ProviderAvailabilityId = availability.ProviderAvailabilityId,
                                        DoctorSpecializationChargeModuleDetailsId = obj.DoctorSpecializationChargeModuleDetailsId,
                                        SlotType = SlotType.BreakSlots

                                    };

                                    breakSlots.Add(otherVisitType);
                                    breakStartTime = nextBreakTime;
                                }

                                breakSlots.RemoveAt(breakSlots.Count - 1);
                            }

                            var removedBreakSlots = otherVisiTypeSlots.FindAll(x =>
                            {
                                return breakSlots.FindIndex(t => t.SlotValue == x.SlotValue) == -1;
                            });
                            _ = removedBreakSlots;

                            int newPatientGroupSize = (int)availability.NoOfNewPatient;
                            double newPatientBreakgroups = 0;
                            if (newPatientGroupSize > 0)
                            {
                                newPatientBreakgroups = removedBreakSlots.Count / newPatientGroupSize;

                            }

                            var remainingBreakElements = removedBreakSlots.Where((element, index) => (index + 1) % newPatientBreakgroups != 0).ToList();
                            int offlineBreakPatientSize = (int)availability.NoOfOfflinePatient;
                            double offlineBreakGroups = 0;
                            if (offlineBreakPatientSize > 0)
                            {
                                offlineBreakGroups = remainingBreakElements.Count / offlineBreakPatientSize;

                            }
                            var afterRemovingOfflineSlots = remainingBreakElements.Where((element, index) => (index + 1) % offlineBreakGroups != 0).ToList();
                            otherVisiTypeSlots = afterRemovingOfflineSlots;

                        }
                        else
                        {
                            int groupSize = (int)availability.NoOfNewPatient;
                            double groups = 0;
                            if (groupSize > 0)
                            {
                                groups = otherVisiTypeSlots.Count / groupSize;
                            }


                            var remainingElements = otherVisiTypeSlots.Where((element, index) => (index + 1) % groups != 0).ToList();
                            int offlinePatientSize = (int)availability.NoOfOfflinePatient;

                            if (offlinePatientSize > 0)
                            {
                                double offlineGroups = remainingElements.Count / offlinePatientSize;
                                var afterRemovingOfflineSlotsss = remainingElements.Where((element, index) => (index + 1) % offlineGroups != 0).ToList();

                                otherVisiTypeSlots = afterRemovingOfflineSlotsss;
                            }

                        }
                        var multipleDays = new MultipleDays();
                        multipleDays.Slots = new List<TimeSlotModel>();
                        var convertedDate = Convert.ToDateTime(dateString);
                        multipleDays.Date = Convert.ToDateTime(convertedDate).Date;
                        multipleDays.Slots.AddRange(otherVisiTypeSlots);
                        dayWiseSlots.Add(multipleDays);
                    }

                }
                else
                {
                    var noSlots = new MultipleDays();
                    var convertedDate = Convert.ToDateTime(dateString);
                    noSlots.Date = Convert.ToDateTime(convertedDate).Date;
                    noSlots.message = "There is not slots available for this date";
                    dayWiseSlots.Add(noSlots);
                }
            }
            return dayWiseSlots;
        }


        public async Task<List<SessionTimings>> FetchSessionSlots(SlotRequest model)
        {
            var availabilityQuery = $@"select ""ProviderId"", ""SpecializationId"", ""LocationId"",""SessionId"", ""SessionTypeId"",""StartTime"",""EndTime"",
                                    ""AvailabilityCount"", ""Date""
                                    FROM ""Session""									
                                            WHERE ""ProviderId"" = {model.ProviderId} 
                                            --and ""SpecializationId"" = {model.SpecializationId} 
                                            --and ""LocationId"" = {model.LocationId}                                            
											and ""Date""::Date = '{model.SlotDate}'::Date
											and ""Active"" is true";

            var availabilityResponse = await this.unitOfWork.Current.QueryAsync<Session>(availabilityQuery);

            if (availabilityResponse == null)
            {
                return new List<SessionTimings>();
            }

            var providerAvailability = await this.unitOfWork.ProviderAvailability.FindAsync(x => x.ProviderId == model.ProviderId && x.SpecializationId == model.SpecializationId && x.LocationId == model.LocationId);

            if (providerAvailability == null)
            {
                return new List<SessionTimings>();
            }

            var charge = new List<AvailabilityModel.AvailabilityModel>();

            AvailabilityModel.AvailabilityModel obj = new AvailabilityModel.AvailabilityModel();
            if (model.ChargeTypesId != null || model.LocationId != null || !string.IsNullOrEmpty(model.SlotDate))
            {

                charge = (List<AvailabilityModel.AvailabilityModel>)await this.GetNewChargesAsync(model.ProviderId, model.ChargeTypesId, model.SpecializationId, model.LocationId, model.SlotDate, 1);

                if (charge.Count > 0)
                {
                    obj = charge[0];
                }
            }

            var timeWiseSlots = new List<SessionTimings>();

            foreach (var availability in availabilityResponse)
            {
                if (availability.AvailabilityCount > 0)
                {
                    var objVisit = availability.AvailabilityCount;

                }
                else
                {
                    return new List<SessionTimings>();
                }

                var slotDate = new DateTime();

                if (!string.IsNullOrEmpty(model.SlotDate))
                {
                    slotDate = Convert.ToDateTime(model.SlotDate.Trim());
                }

                var bookeAppointmentTimes = await this.FetchAppointmentTimesAsync(providerAvailability.ProviderAvailabilityId ?? 0, slotDate);
                var otherVisiTypeSlots = new List<TimeSlotModel>(); // with out new Patient Slots.
                var appointmenTimes = bookeAppointmentTimes.ToList();
                var startTimeTokens = availability.StartTime.ToString().Split(":");
                var endTimeTokens = availability.EndTime.ToString().Split(":");
                string[] slotDays = availability.Date.ToString("yyyy-MM-dd").Split(',');
                if (slotDays.Contains(slotDate.ToString("yyyy-MM-dd")))
                {
                    DateTime startTime = new DateTime(
                    DateTime.Now.Year,
                    DateTime.Now.Month,
                    DateTime.Now.Day,
                    Convert.ToInt32(startTimeTokens[0]),
                    Convert.ToInt32(startTimeTokens[1]),
                    0
                );
                    DateTime endTime = new DateTime(
                        DateTime.Now.Year,
                        DateTime.Now.Month,
                        DateTime.Now.Day,
                        Convert.ToInt32(endTimeTokens[0]),
                        Convert.ToInt32(endTimeTokens[1]),
                        0
                    );

                    var counter = 0;

                    // Calculate the time interval between slots
                    TimeSpan slotInterval = endTime - startTime;
                    int numberOfSlots = availability.AvailabilityCount;

                    // Calculate the duration of each slot
                    TimeSpan slotDuration = TimeSpan.FromTicks(slotInterval.Ticks / numberOfSlots);

                    // Initialize a current time variable
                    DateTime currentTime = Convert.ToDateTime(startTime);

                    // Use a while loop to generate and print time slots
                    while (currentTime < endTime)
                    {
                        DateTime slotEndTime = currentTime + slotDuration;
                        ++counter;

                        var otherVisitType = new TimeSlotModel
                        {
                            TokenNumber = counter,
                            SlotValue = currentTime.ToString("HH:mm"),
                            SlotName = currentTime.ToString("hh:mm tt"),
                            SlotTime = currentTime.TimeOfDay,
                            Charge = (obj.Charge == null) ? 0 : obj.Charge,
                            Duration = Convert.ToInt32(slotDuration.TotalMinutes),
                            Status = appointmenTimes.Any(a => a.ToString().Substring(0, 4) == currentTime.TimeOfDay.ToString().Substring(0, 4)) ? SlotStatus.Booked : SlotStatus.Available,
                            SessionId = availability.SessionId,
                            DoctorSpecializationChargeModuleDetailsId = obj.DoctorSpecializationChargeModuleDetailsId,
                            SlotType = SlotType.GeneralSLots,
                            ProviderAvailabilityId = providerAvailability.ProviderAvailabilityId
                        };
                        otherVisiTypeSlots.Add(otherVisitType);
                        currentTime = slotEndTime;
                    }

                    var multipleTimings = new SessionTimings();
                    multipleTimings.Slots = new List<TimeSlotModel>();
                    multipleTimings.Time = $@"{startTime.ToString("hh:mm tt")} to {endTime.ToString("hh:mm tt")}";
                    multipleTimings.Slots.AddRange(otherVisiTypeSlots);
                    timeWiseSlots.Add(multipleTimings);
                }
            }

            return timeWiseSlots;

        }
    }
}
