namespace Hims.Infrastructure.Services
{
    using System;
    using System.Collections.Generic;
    using System.Threading.Tasks;
    using Dapper;
    using Domain.Entities;
    using Domain.Repositories.UnitOfWork;
    using Domain.Services;
    using Shared.EntityModels;
    using Shared.UserModels.ProviderScheduleVisits;

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

        /// <inheritdoc cref="IProviderScheduleVisitsService" />
        public ProviderSchduleVisitsService(IUnitOfWork unitOfWork) => this.unitOfWork = unitOfWork;

        /// <inheritdoc />
        public async Task<IEnumerable<ViewModel>> FetchAsync(FilterModel model)
        {
            var where = " WHERE 1 = 1 ";
            if (model.LocationId != null)
            {
                where += $@" AND PAS.""LocationId"" = {model.LocationId}";
            }
            if ((model.ProviderId != 0) && (model.ProviderId != null))
            {
                where += $@" AND PAS.""ProviderId"" = {model.ProviderId}";
            }
            if ((model.SpecializationId != 0) && (model.SpecializationId !=null))
            {
                where += $@" AND PAS.""SpecializationId"" = {model.SpecializationId}";
            }
            if (model.CreatedBy != null)
            {
                where += $@" AND PAS.""CreatedBy"" = {model.CreatedBy}";
            }
            if (model.Active ==true )
            {
                where += $@" AND PAS.""Active"" is true";
            }
            else
            {
                where += $@" AND PAS.""Active"" is false";

            }
            var query = $@"Select distinct COUNT(*) OVER () AS ""TotalItems"",string_agg(PAS.""ProviderAvailabilityVisitTypeId""::text,',') as ""SchedulerTypeId"",PAS.""Active"",PAS.""CreatedDate""::date,PAS.""ModifiedDate""::date, PAS.""SpecializationId"",PAS.""ProviderId"",string_agg(PAS.""VisitTypeId""::text, ', ') as ""VisitTypeIds"",
                            string_agg(ct.""VisitorName""::text,',')as ""VisitNames"",L.""Name""as ""LocationName"",
							p.""FullName"" as ""ProviderName"",
							PAS.""CreatedBy"",ac.""FullName"" as ""CreatedByName"",PAS.""ModifiedBy"",cc.""FullName"" as ""ModifiedByName"",
				                    sp.""SpecializationName""
                                    from ""ProviderAvailabilityVisitType"" PAS
									left join ""Account"" ac on ac.""AccountId"" = PAS.""CreatedBy""
									left join ""Account"" cc on cc.""AccountId"" = PAS.""ModifiedBy""
                                    left join ""Provider"" p on p.""ProviderId"" = PAS.""ProviderId""
                                    left join ""VisitType"" ct on ct.""VisitTypeId""= PAS.""VisitTypeId""
                                    left join ""Specialization"" sp on sp.""SpecializationId""=PAS.""SpecializationId""
                                    join ""Location"" L on L.""LocationId"" = PAS.""LocationId""
{where}
									group by PAS.""Active"",PAS.""CreatedDate""::date,PAS.""ModifiedDate""::date,PAS.""SpecializationId"",PAS.""ProviderId"",L.""Name"",p.""FullName"",
									sp.""SpecializationName"",PAS.""CreatedBy"",ac.""FullName"",PAS.""ModifiedBy"",cc.""FullName"""

                                    ;

            if (model.PageIndex <= 0)
            {
                _ = await this.unitOfWork.Current.QueryAsync<ViewModel>(query);
            }

            model.PageIndex -= 1;
            query += " LIMIT " + model.PageSize + " offset " + (model.PageIndex * model.PageSize);


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


        /// <inheritdoc />
        public async Task<int> AddAsync(InsertModel model)
        {
            try
            {
                var where = $@"where 1=1 ";
                if (model.ProviderId != null)
                {
                    where += $@" and ""ProviderId"" = {model.ProviderId}";
                }
                if (model.SpecializationId != null)
                {
                    where += $@" and ""SpecializationId"" = {model.SpecializationId}";
                }
                if (model.ProviderId == null)
                {
                    where += $@" and ""ProviderId"" is null ";
                }
                if (model.SpecializationId == null)
                {
                    where += $@" and ""SpecializationId"" is null";
                }
                if (model.LocationId != null)
                {
                    where += $@" and ""LocationId"" = {model.LocationId}";
                }
                var checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(*) FROM ""ProviderAvailabilityVisitType"" {where}");
                if (checkIf > 0)
                {
                    return -1;
                }
                //var checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(*) FROM ""ProviderAvailabilityVisitType"" WHERE ""ProviderId"" = {model.ProviderId} and ""SpecializationId"" = {model.SpecializationId}");
                //if (checkIf > 0)
                //{
                //    return -1;
                //}
                var cheking = model.Visits.Find(t => t.ProviderAvailabilityVisitTypeId != 0);

                if (cheking == null)
                {
                    //Provider Availability visits
                    foreach (var visit in model.Visits)
                    {
                        var providerAvailabilityVisitType = new ProviderAvailabilityVisitType
                        {
                            CreatedBy = model.CreatedBy,
                            CreatedDate = DateTime.UtcNow,
                            Active = true,
                            VisitTypeId = visit.Id,
                            Duration = visit.Value,
                            ProviderId = model.ProviderId,
                            LocationId = model.LocationId,
                            SpecializationId = model.SpecializationId,
                        };
                        var insertExecute = await this.unitOfWork.ProviderAvailabilityVisitType.InsertAsync(providerAvailabilityVisitType);

                    }
                }

                return 1;
            }
            catch (Exception e)
            {
                return -1;
            }


        }

        /// <inheritdoc />
        public async Task<int> UpdateAsync(VisitTypeModel model)
        {
            var checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(""VisitTypeId"") FROM ""VisitType"" WHERE TRIM(UPPER(""VisitorName"")) = '{model.VisitorName.ToUpper().Trim()}' AND ""VisitTypeId"" <> {model.VisitTypeId}");
            if (checkIf > 0)
            {
                return -1;
            }

            var visitType = await this.unitOfWork.Visitors.FindAsync(m => m.VisitTypeId == model.VisitTypeId);
            visitType.VisitorName = model.VisitorName;
            visitType.VisitTypeFor = model.VisitTypeFor;
            visitType.Active = true;
            visitType.ModifiedBy = model.ModifiedBy;
            visitType.ModifiedDate = DateTime.Now;
            return await this.unitOfWork.Visitors.UpdateAsync(visitType);
        }



        public async Task<int> UpdateAsync(InsertModel model)
        {

            using var transaction = this.unitOfWork.BeginTransaction();
            try
            {
                foreach (var visit in model.Visits)
                {
                    if (visit.ProviderAvailabilityVisitTypeId == 0)
                    {
                        var providerAvailabilityVisitTypeInsert = new ProviderAvailabilityVisitType
                        {
                            ModifiedBy = model.ModifiedBy,
                            ModifiedDate = DateTime.Now,
                            Active = true,
                            VisitTypeId = visit.Id,
                            Duration = visit.Value,
                            ProviderId = model.ProviderId,
                            LocationId = model.LocationId,
                            SpecializationId = model.SpecializationId
                        };
                        var dataInsert = await this.unitOfWork.ProviderAvailabilityVisitType.InsertAsync(providerAvailabilityVisitTypeInsert, transaction);
                        if (dataInsert == 0)
                        {
                            transaction.Rollback();
                            return new int();
                        }
                    }
                    else {
                        var find = await this.unitOfWork.ProviderAvailabilityVisitType.FindAsync(d => d.ProviderAvailabilityVisitTypeId == visit.ProviderAvailabilityVisitTypeId);
                        //var checkIf = await this.unitOfWork.ProviderAvailabilityVisitType.FindAsync(d => d.ProviderAvailabilityVisitTypeId == model.ProviderAvailabilityVisitTypeId);
                        var providerAvailabilityVisitType = new ProviderAvailabilityVisitType
                        {
                            ModifiedBy = model.ModifiedBy,
                            ModifiedDate = DateTime.Now,
                            Active = true,
                            VisitTypeId = visit.Id,
                            Duration = visit.Value,
                            ProviderId = find.ProviderId,
                            LocationId = find.LocationId,
                            SpecializationId = find.SpecializationId,
                            ProviderAvailabilityVisitTypeId = visit.ProviderAvailabilityVisitTypeId
                        };
                        var data = await this.unitOfWork.ProviderAvailabilityVisitType.UpdateAsync(providerAvailabilityVisitType, transaction);
                        if (data == 0)
                        {
                            transaction.Rollback();
                            return new int();
                        }
                    }
                    

                }
                transaction.Commit();
                return 1;
            }
            catch (Exception ex)
            {
                ex.Message.ToString();
                transaction.Rollback();
                return -1;
            }
        }

        public async Task<IEnumerable<ViewModel>> FetchDoctorChargeAsync(FilterModel model)
        {
            var where = " WHERE 1 = 1 ";
            if (model.LocationId != null)
            {
                where += $@" AND PAS.""LocationId"" = {model.LocationId}";
            }
            if (model.ProviderId != null)
            {
                where += $@" AND PAS.""ProviderId"" = {model.ProviderId}";
            }
            if (model.SpecializationId != null)
            {
                where += $@" AND PAS.""SpecializationId"" = {model.SpecializationId}";
            }
            var query = $@"Select distinct COUNT(*) OVER () AS ""TotalItems"",PAS.""PartsOfDayId"",PAS.""StartDate""::date,PAS.""EndDate""::date,PAS.""ProviderAvailabilityChargeTypeId"", PAS.""SpecializationId"",PAS.""ProviderId"",PAS.""Charge"",PAS.""Charge"" as ""Value"",PAS.""ChargeTypesId"",PAS.""ChargeTypesId"" as ""Id"",
                            ct.""ChargeName"",ct.""ChargeName"" as ""Name"",L.""Name""as ""LocationName"",
							p.""FullName"" as ""ProviderName"",
							PAS.""CreatedBy"",ac.""FullName"" as ""CreatedByName"",PAS.""ModifiedBy"",cc.""FullName"" as ""ModifiedByName"",
				                    sp.""SpecializationName""
                                    from ""ProviderAvailabilityChargeType"" PAS
									left join ""Account"" ac on ac.""AccountId"" = PAS.""CreatedBy""
									left join ""Account"" cc on cc.""AccountId"" = PAS.""ModifiedBy""
                                    left join ""Provider"" p on p.""ProviderId"" = PAS.""ProviderId""
                                    left join ""ChargeTypes"" ct on ct.""ChargeTypesId""= PAS.""ChargeTypesId""
                                    join ""Specialization"" sp on sp.""SpecializationId""=PAS.""SpecializationId""
                                    join ""Location"" L on L.""LocationId"" = PAS.""LocationId""
                                    {where}
									group by PAS.""ProviderAvailabilityChargeTypeId"",PAS.""SpecializationId"",PAS.""ProviderId"",PAS.""Charge"",PAS.""ChargeTypesId"",ct.""ChargeName"",L.""Name"",p.""FullName"",
									sp.""SpecializationName"",PAS.""CreatedBy"",ac.""FullName"",PAS.""ModifiedBy"",cc.""FullName""";
            return await this.unitOfWork.Current.QueryAsync<ViewModel>(query);
        }

        public async Task<IEnumerable<ViewModel>> FetchDoctorVisitsAsync(FilterModel model)
        {
            var where = " WHERE 1 = 1 ";
            if (model.LocationId != null)
            {
                where += $@" AND PAS.""LocationId"" = {model.LocationId}";
            }
            if (model.ProviderId != null)
            {
                where += $@" AND PAS.""ProviderId"" = {model.ProviderId}";
            }
            if (model.SpecializationId != null)
            {
                where += $@" AND PAS.""SpecializationId"" = {model.SpecializationId}";
            }
            if (model.LocationId != null)
            {
                where += $@" AND PAS.""LocationId"" = {model.LocationId}";
            }
            if (model.ProviderId == null)
            {
                where += $@" and PAS.""ProviderId"" is null ";
            }
            if (model.SpecializationId == null)
            {
                where += $@" and PAS.""SpecializationId"" is null";
            }
            var query = $@"Select distinct COUNT(*) OVER () AS ""TotalItems"",PAS.""LocationId"",PAS.""ProviderAvailabilityVisitTypeId"",PAS.""Active"", PAS.""SpecializationId"",PAS.""ProviderId"",PAS.""Duration"",PAS.""Duration"" as ""Value"",PAS.""VisitTypeId"",PAS.""VisitTypeId"" as ""Id"",
                            ct.""VisitorName"" as ""VisitName"",ct.""VisitorName"" as ""Name"",L.""Name""as ""LocationName"",
							p.""FullName"" as ""ProviderName"",
							PAS.""CreatedBy"",ac.""FullName"" as ""CreatedByName"",PAS.""ModifiedBy"",cc.""FullName"" as ""ModifiedByName"",
				                    sp.""SpecializationName""
                                    from ""ProviderAvailabilityVisitType"" PAS
									left join ""Account"" ac on ac.""AccountId"" = PAS.""CreatedBy""
									left join ""Account"" cc on cc.""AccountId"" = PAS.""ModifiedBy""
                                    left join ""Provider"" p on p.""ProviderId"" = PAS.""ProviderId""
                                    left join ""VisitType"" ct on ct.""VisitTypeId""= PAS.""VisitTypeId""
                                    left join ""Specialization"" sp on sp.""SpecializationId""=PAS.""SpecializationId""
                                    join ""Location"" L on L.""LocationId"" = PAS.""LocationId""
                                    {where}
									group by PAS.""ProviderAvailabilityVisitTypeId"",PAS.""Active"",PAS.""SpecializationId"",PAS.""ProviderId"",PAS.""Duration"",PAS.""VisitTypeId"",ct.""VisitorName"",L.""Name"",p.""FullName"",
									sp.""SpecializationName"",PAS.""CreatedBy"",ac.""FullName"",PAS.""ModifiedBy"",cc.""FullName""";
            return await this.unitOfWork.Current.QueryAsync<ViewModel>(query);
        }

        public Task<int> DeleteAsync(int visitTypeId)
        {
            throw new NotImplementedException();
        }

        public Task<string> FindNameByVisitorId(int visitTypeId)
        {
            throw new NotImplementedException();
        }


        public async Task<int> ModifyStatusAsync(InsertModel model)
        {
            try
            {

                var whereStr = $@"";
                var typeId = model.SchedulerTypeId.Split(',');

                for (var i = 0; i < typeId.Length; i++)
                {
                    whereStr += @$"'{typeId[i]}'";
                    if (i < typeId.Length - 1)
                    {
                        whereStr += $@",";
                    }
                }

                var query = $@"UPDATE ""ProviderAvailabilityVisitType"" SET ""Active"" = {model.Active}, ""ModifiedBy""  = {model.ModifiedBy} , ""ModifiedDate"" = '{DateTime.Now}' WHERE ""ProviderAvailabilityVisitTypeId"" in ({whereStr})";

                var num = await this.unitOfWork.Current.QueryAsync<int>(query);

                return 1;
            }
            catch (Exception e)
            {
                e.Message.ToString();
            }

            return -1;

        }
        public Task<IEnumerable<ViewModel>> FetchProvidersWithSpecializationAsync(int locationId)
        {
            var where = $@" where 1=1";
            if ((locationId != 0) && (locationId != null))
            {
                where += $@" AND prl.""LocationId"" = {locationId}";
            }
            var query = $@"SELECT DISTINCT  prl.""ProviderId"" ,pr2.""FullName"" ,  
 s. ""SpecializationId"", s.""SpecializationName"", prl.""LocationId""
                           FROM ""ProviderAvailabilityVisitType"" prl
                           JOIN ""Location"" pral on pral.""LocationId"" = prl.""LocationId"" AND pral.""Active"" IS TRUE					   
                         
                           JOIN ""Provider"" pr2 on pr2.""ProviderId"" = prl.""ProviderId"" and pr2.""Active"" is true
						   join ""Specialization"" s on s.""SpecializationId"" = prl.""SpecializationId"" {where}";
            return this.unitOfWork.Current.QueryAsync<ViewModel>(query);
        }
    }
}