using Dapper;
using Hims.Domain.Entities;
using Hims.Domain.Repositories.UnitOfWork;
using Hims.Domain.Services;
using Hims.Shared.DataFilters;
using Hims.Shared.UserModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Hims.Infrastructure.Services
{
    public class SessionService : ISessionService
    {
        /// <summary>
        /// The unit of work.
        /// </summary>
        private readonly IUnitOfWork unitOfWork;

        /// <summary>
        /// Initializes a new instance of the <see cref="SessionTypeServices"/> class.
        /// </summary>
        /// <param name="unitOfWork">The unit of work.</param>
        public SessionService(IUnitOfWork unitOfWork) => this.unitOfWork = unitOfWork;

        public Task<IEnumerable<SessionModel>> FetchAsync(SessionModel model)
        {
            var where = " WHERE 1 = 1 ";

            if (model.ProviderId > 0)
            {
                where += $@" and s.""ProviderId"" = {model.ProviderId}";
            }


            if (!string.IsNullOrEmpty(model.FromDate))
            {
                where += $@" and s.""Date""::date >= '{Convert.ToDateTime(model.FromDate):yyyy-MM-dd}'::date";
            }

            if (!string.IsNullOrEmpty(model.ToDate))
            {
                where += $@" and s.""Date""::date <= '{Convert.ToDateTime(model.ToDate):yyyy-MM-dd}'::date";
            }

            if (model.SessionTypeId > 0)
            {
                where += $@" and s.""SessionTypeId"" = {model.SessionTypeId}";
            }

            if (model.Active != null)
            {
                where += (bool)model.Active ? $@" and s.""Active"" is true " : $@" and s.""Active"" is false ";
            }

            var query = $@"SELECT COUNT(*) OVER () AS ""TotalItems"",  s.""SessionId"" ,s.""ProviderId"",s.""Date"",s.""StartTime"",s.""EndTime"",s.""AvailabilityCount"",s.""Banner"",s.""LocationId"",s.""SessionTypeId"",st.""Name"" as ""SessionTypeName"",
                                        p.""FullName"" as ""DoctorName"",CA.""FullName"" AS ""CreatedByName"" , MA.""FullName"" AS ""ModifiedByName"" , s.""CreatedDate"",s.""ModifiedDate"",s.""Active"", L.""Name"" as ""LocationName""
                                        FROM ""Session"" s
                                        join ""Provider"" p on p.""ProviderId""=s.""ProviderId""
                                        join ""SessionType"" st on st.""SessionTypeId"" = s.""SessionTypeId""
                                        join ""Location"" L on L.""LocationId"" = s.""LocationId""
                                        Join ""Account"" CA ON CA.""AccountId"" = s.""CreatedBy""
                                        Left Join ""Account"" MA ON MA.""AccountId"" = s.""ModifiedBy""
                                         {where}  Order by ""SessionId"" DESC ";



            if (model.PageIndex != null && model.PageSize != null)
            {
                model.PageIndex = model.PageIndex > 0 ? model.PageIndex - 1 : model.PageIndex;
                query += $@" limit {model.PageSize} offset {model.PageSize * model.PageIndex}";
            }
            return this.unitOfWork.Current.QueryAsync<SessionModel>(query);
        }


        /// <summary>
        /// Adds the asynchronous.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <returns></returns>
        public async Task<int> AddAsync(SessionAddModel model)
        {
            using var transaction = this.unitOfWork.BeginTransaction();
            int count = 0;
            if (model.Slots.Count == 0)
            {
                return -1;
            }
            if (string.IsNullOrEmpty(model.FromDate) || string.IsNullOrEmpty(model.ToDate))
            {
                return -1;
            }
            DateTime DateinCode = Convert.ToDateTime(model.FromDate);
            DateTime SubDate = Convert.ToDateTime(model.ToDate);
            //var numberOfDays = (DateinCode - SubDate).TotalDays;
            int days = (SubDate - DateinCode).Days;
            int nooftimes = 0;
            if (days == 0)
            {
                nooftimes = 1;
            }
            else
            {
                nooftimes = days + 1;
            }
            bool check = true;
            for (int i = 0; i < nooftimes; i++)
            {
                foreach (var slot in model.Slots)
                {
                    if (check == false)
                    {
                        break;
                    }
                    if (DateinCode <= Convert.ToDateTime(model.ToDate))
                    {
                        var sessionmodel = new Session
                        {
                            ProviderId = model.ProviderId,
                            LocationId = model.LocationId,
                            Active = true,
                            Date = DateinCode,
                            AvailabilityCount = slot.AvailabilityCount,
                            CreatedBy = model.CreatedBy,
                            CreatedDate = DateTime.Now,
                            SessionTypeId = slot.SessionTypeId,
                            StartTime = TimeSpan.Parse(slot.FromTime),
                            EndTime = TimeSpan.Parse(slot.ToTime),
                            //EndTime = Convert.ToDateTime(slot.ToTime),

                        };

                        //var query1 = $@"select * from ""Session""
                        //    where 1=1 and ""ProviderId""= {sessionmodel.ProviderId} and ""Date""::date = '{sessionmodel.Date.ToString("yyyy-MM-dd")}' 
                        //      and(((""StartTime"">='{sessionmodel.StartTime}' and ""StartTime""<='{sessionmodel.EndTime}') or 
                        //           (""EndTime"">='{sessionmodel.StartTime}' and ""EndTime""<='{sessionmodel.EndTime}')))";
                        var query = $@"select * from ""Session"" where  1=1 and ""ProviderId""= {sessionmodel.ProviderId} and ""Date""::date = '{sessionmodel.Date.ToString("yyyy-MM-dd")}'::date
                                            and ((""StartTime"" BETWEEN '{sessionmodel.StartTime}' AND '{sessionmodel.EndTime}')
                                             or (""EndTime""  BETWEEN '{sessionmodel.StartTime}' AND '{sessionmodel.EndTime}')
                                             or ('{sessionmodel.StartTime}' BETWEEN ""StartTime"" AND ""EndTime"")
                                             or ('{sessionmodel.EndTime}' BETWEEN ""StartTime"" AND ""EndTime""))";
                        var res = (await this.unitOfWork.Current.QueryAsync<Session>(query)).ToList();
                        if (res.ToList().Count >= 1)
                        {
                            check = false;
                            // return -1;
                            transaction.Rollback();
                            break;
                        }
                        sessionmodel.SessionId = await this.unitOfWork.Sessions.InsertAsync(sessionmodel, transaction);
                        count++;
                        if (sessionmodel.SessionId == 0)
                        {
                            transaction.Rollback();
                            return 0;
                        }

                    }

                }
                DateinCode = DateinCode.AddDays(1);
                if (check == false)
                {
                    break;
                }
            }
            if (check == true)
            {
                transaction.Commit();
                return count;
            }


            return -1;
        }

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

        ///<inheritdoc/>
        public async Task<int> ModifyStatusAsync(SessionModel model)
        {
            int num;

            var foundId = await this.unitOfWork.Sessions.FindAsync(m => m.SessionId == model.SessionId);
            foundId.Active = (bool)model.Active;
            foundId.ModifiedBy = model.ModifiedBy;
            foundId.ModifiedDate = DateTime.Now;
            num = await this.unitOfWork.Sessions.UpdateAsync(foundId);
            return num;

        }

        ///<inheritdoc/>
        public async Task<int> UpdateAsync(SessionUpdateModel model)
        {
            int num;

            //var query1 = $@"select * from ""Session""
            //            where 1=1 and ""ProviderId""= {model.ProviderId} and ""Date""::date = '{model.Date.ToString("yyyy-MM-dd")}' and ""SessionId"" != {model.SessionId}
            //              and(((""StartTime"">='{TimeSpan.Parse(model.StartTime)}' and ""StartTime""<='{TimeSpan.Parse(model.EndTime)}') or 
            //                   (""EndTime"">='{TimeSpan.Parse(model.StartTime)}' and ""EndTime""<='{TimeSpan.Parse(model.EndTime)}')))";
            var query = $@"select * from ""Session"" where  1=1 and ""ProviderId""= {model.ProviderId} and ""LocationId"" = {model.LocationId} and ""Date""::date = '{model.Date.ToString("yyyy-MM-dd")}'::date and ""SessionId"" != {model.SessionId}
                                            and ((""StartTime"" BETWEEN '{TimeSpan.Parse(model.StartTime)}' AND '{TimeSpan.Parse(model.EndTime)}')
                                             or (""EndTime""  BETWEEN '{TimeSpan.Parse(model.StartTime)}' AND '{TimeSpan.Parse(model.EndTime)}')
                                             or ('{TimeSpan.Parse(model.StartTime)}' BETWEEN ""StartTime"" AND ""EndTime"")
                                             or ('{TimeSpan.Parse(model.EndTime)}' BETWEEN ""StartTime"" AND ""EndTime""))";
            var res = (await this.unitOfWork.Current.QueryAsync<Session>(query)).ToList();
            //var lookup = await this.unitOfWork.Sessions.FindAllAsync(m => m.ProviderId == model.ProviderId &&  m.Date == model.Date && ((m.StartTime >= TimeSpan.Parse(model.StartTime) && m.StartTime <= TimeSpan.Parse(model.EndTime)) || (m.EndTime >= TimeSpan.Parse(model.StartTime) && m.EndTime <= TimeSpan.Parse(model.EndTime))) && m.SessionId != model.SessionId);
            if (res.ToList().Count >= 1)
            {
                return -1;
            }
            var foundId = await this.unitOfWork.Sessions.FindAsync(m => m.SessionId == model.SessionId);
            foundId.SessionTypeId = model.SessionTypeId;
            foundId.StartTime = TimeSpan.Parse(model.StartTime);
            foundId.EndTime = TimeSpan.Parse(model.EndTime);
            foundId.AvailabilityCount = model.AvailabilityCount;
            foundId.ModifiedBy = model.ModifiedBy;
            foundId.ModifiedDate = DateTime.Now;
            foundId.LocationId = model.LocationId;
            num = await this.unitOfWork.Sessions.UpdateAsync(foundId);
            return num;

        }
    }
}
