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

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


        /// <inheritdoc cref="IDietSlotsMasterService" />
        public DietConditionHeaderServices(IUnitOfWork unitOfWork
           )
        {
            this.unitOfWork = unitOfWork;
        }

        public async Task<IEnumerable<DietConditionsModel>> FetchItemsAsync(DietConditionsModel model)
        {
            var query = $@"select di.""DietItemsId"",di.""DietItemsName"",di.""CreatedBy"",di.""CreatedDate"",di.""ModifiedBy"",di.""ModifiedDate"",
                        di.""MeasureId"",qm.""MeasureName"",a.""FullName"" as ""CreatedByName"", di.""Active"",b.""FullName"" as ""ModifiedByName""
                        from ""DietItems"" di
                        join ""QuantityMeasure"" qm on qm.""MeasureId""=di.""MeasureId""
                        join ""Account"" a on a.""AccountId"" = di.""CreatedBy""
                        left join  ""Account"" b on b.""AccountId"" = di.""ModifiedBy"" ";
            return await this.unitOfWork.Current.QueryAsync<DietConditionsModel>(query);
        }

        public async Task<IEnumerable<DietConditionsModel>> FetchMeasureAsync(DietConditionsModel model)
        {
            var query = $@"select * from ""QuantityMeasure"" ";
            return await this.unitOfWork.Current.QueryAsync<DietConditionsModel>(query);
        }

        public async Task<int> InsertItemsAsync(DietSlotsModel model, List<DietConditionsDetailModel> dietConditionsModel)
        {
            var transaction = this.unitOfWork.BeginTransaction();
            var checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(""ConditionId"") FROM ""DietConditionHeader"" WHERE TRIM(UPPER(""ConditionName"")) = '{model.ConditionName.ToUpper().Trim()}'");
            if (checkIf > 0)
            {
                return -1;
            }
            var dietHeader = new DietConditionHeader
            {
                ConditionName = model.ConditionName,
                CreatedBy = model.CreatedBy,
                CreatedDate = DateTime.Now,
                Active = true,
                ShiftIds = model.ShiftIds
            };
            dietHeader.ConditionId = await this.unitOfWork.DietConditionHeader.InsertAsync(dietHeader, transaction);
            if (dietHeader.ConditionId == 0)
            {
                transaction.Rollback();
                return 0;
            }
            var failureCount = 0;
            foreach (var item in dietConditionsModel)
            {
                var dietDetail = new DietConditionDetail
                {
                    DietItemsId = item.DietItemsId,
                    Quantity = item.Quantity,
                    MeasureId = item.MeasureId,
                    DietSlotId = item.DietSlotId,
                    ConditionId = dietHeader.ConditionId
                };

                dietDetail.ConditionDetailId = await this.unitOfWork.DietConditionDetail.InsertAsync(dietDetail, transaction);
                if (dietDetail.ConditionDetailId == 0)
                {
                    failureCount++;
                }
            }

            if (failureCount > 0)
            {
                transaction.Rollback();
                return 0;
            }

            transaction.Commit();
            return dietHeader.ConditionId;
        }
        public async Task<int> UpdateAsync(DietConditionsModel model)
        {

            var checkIf = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<int>($@"select count(*) from ""DietItems"" where  lower (""DietItemsName"") = '{model.ConditionName.ToLower()}' and ""DietItemsId"" <> {model.ConditionName}");
            if (checkIf > 0)
            {
                return -1;
            }
            var record = await this.unitOfWork.DietConditionHeader.FindAsync(m => m.ConditionId == model.ConditionId);
            if (record == null)
            {
                return -2;
            }
            record.ModifiedBy = model.CreatedBy;
            record.ModifiedDate = DateTime.Now;

            return await this.unitOfWork.DietConditionHeader.UpdateAsync(record);
        }
        public async Task<int> ModifyStatusAsync(DietConditionsModel model)
        {
            var query = $@"UPDATE ""DietItems"" SET ""ModifiedBy""={model.CreatedBy}, ""ModifiedDate""=now(), ""Active""= {model.Active}
	                           WHERE ""DietItemsId""= {model.ConditionId}";

            return await this.unitOfWork.Current.ExecuteAsync(query);
        }

        public async Task<IEnumerable<DietConditionsDetailModel>> FetchDietPackageAsync(DietConditionsDetailModel model)
        {
            var query = $@"select dh.""ConditionId"",dh.""ConditionName"",dh.""CreatedBy"",dh.""CreatedDate"",dh.""ModifiedBy"",dh.""ModifiedDate"",dh.""Active"",ac.""FullName"" as ""CreatedByName""
,a.""FullName"" as ""ModifiedByName"" from ""DietConditionHeader"" dh
left join ""Account"" ac on ac.""AccountId""=dh.""CreatedBy""
left join ""Account"" a on a.""AccountId""=dh.""ModifiedBy"" order by dh.""ConditionId"" desc";
            return await this.unitOfWork.Current.QueryAsync<DietConditionsDetailModel>(query);
        }

        public async Task<DietPackageViewModel> ViewAsync(int conditionId)
        {
            var query = $@"select dh.""ConditionId"",dh.""ConditionName"",dh.""ShiftIds"",dh.""CreatedBy"",dh.""CreatedDate"",dh.""ModifiedBy"",dh.""ModifiedDate"",dh.""Active"",
                            ac.""FullName"" as ""CreatedByName""
                            ,a.""FullName"" as ""ModifiedByName"" from ""DietConditionHeader"" dh
                            left join ""Account"" ac on ac.""AccountId""=dh.""CreatedBy""
                            left join ""Account"" a on a.""AccountId""=dh.""ModifiedBy""
                            WHERE dh.""ConditionId"" = {conditionId}";
            var packageModule = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<DietConditionsDetailModel>(query);
            var model = new DietPackageViewModel();
            if (packageModule.ConditionId > 0)
            {
                model.Conditions = packageModule;
                //package details
                //    var query1 = $@"select dd.""ConditionDetailId"",dd.""DietItemsId"",dt.""DietItemsName"",dd.""Quantity"",dd.""MeasureId"",qm.""MeasureName"",dd.""DietSlotId"",ds.""Name"" as ""DietShiftName"",dd.""ConditionId"" from ""DietConditionDetail"" dd
                //                 left join ""DietItems"" dt on dt.""DietItemsId""=dd.""DietItemsId""
                //                    left join ""QuantityMeasure"" qm on qm.""MeasureId""=dd.""MeasureId""
                //                    left join ""DietSlots"" ds on ds.""DietSlotId"" = dd.""DietSlotId""
                //where dd.""ConditionId""={model.Conditions.ConditionId}";
                var query1 = $@"select string_agg(concat(dt.""DietItemsName""::text,'-' ,dd.""Quantity""::text,qm.""MeasureName""::text)::text,' , ') ""DietItemsName"",ds.""Name"" as ""DietShiftName"" from ""DietConditionDetail"" dd
                             left join ""DietItems"" dt on dt.""DietItemsId""=dd.""DietItemsId""
                                left join ""QuantityMeasure"" qm on qm.""MeasureId""=dd.""MeasureId""
                                left join ""DietSlots"" ds on ds.""DietSlotId"" = dd.""DietSlotId"" 
where dd.""ConditionId""={model.Conditions.ConditionId} group by ds.""Name""";
                var modules = await this.unitOfWork.Current.QueryAsync<DietConditionsDetailModel>(query1);
                if (modules.Any())
                {
                    model.DietConditions = modules.ToList();
                }
            }

            return model;

        }

        public async Task<int> UpdateItemsAsync(DietSlotsModel model, List<DietConditionsDetailModel> dietConditionsModel)
        {
            var transaction = this.unitOfWork.BeginTransaction();
            var checkIf = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<int>($@"select count(*) from ""DietConditionHeader"" where TRIM(UPPER(""ConditionName"")) = '{model.ConditionName.ToUpper().Trim()}' and ""ConditionId"" <> {model.ConditionId}");
            if (checkIf > 0)
            {
                return -1;
            }
            var dietCondition = await this.unitOfWork.DietConditionHeader.FindAsync(m => m.ConditionId == model.ConditionId, transaction);
            if (dietCondition == null || dietCondition.ConditionId == 0)
            {
                transaction.Rollback();
                return 0;
            }
            dietCondition.ConditionName = model.ConditionName;
            dietCondition.ModifiedBy = model.CreatedBy;
            dietCondition.ModifiedDate = DateTime.Now;
            dietCondition.ShiftIds = model.ShiftIds;
            var updated = await this.unitOfWork.DietConditionHeader.UpdateAsync(dietCondition, transaction);
            if (updated == 0)
            {
                transaction.Rollback();
                return 0;
            }
            var failureCount = 0;
            // Delete diet package details
            var srcIds = await this.unitOfWork.Current.QueryAsync<int>($@"SELECT ""ConditionDetailId"" FROM ""DietConditionDetail"" WHERE ""ConditionId"" = {model.ConditionId}", transaction);
            var destIds = dietConditionsModel.Select(m => (int)m.ConditionDetailId).Where(m => m != 0);

            if (destIds.Any() && srcIds.Any())
            {
                var removedDetailIds = srcIds.Except(destIds);
                if (removedDetailIds.Any())
                {
                    var query = $@"DELETE FROM ""DietConditionDetail"" WHERE ""ConditionDetailId"" IN ({string.Join(",", removedDetailIds)})";
                    var deleted = await this.unitOfWork.Current.ExecuteAsync(query, transaction);
                    if (deleted != removedDetailIds.Count())
                    {
                        transaction.Rollback();
                        return 0;
                    }
                }
            }


            // Add/Update Diet Package Details
            foreach (var item in dietConditionsModel)
            {
                if (item.ConditionDetailId == 0)
                {
                    var dietDetail = new DietConditionDetail
                    {
                        DietItemsId = item.DietItemsId,
                        Quantity = item.Quantity,
                        MeasureId = item.MeasureId,
                        DietSlotId = item.DietSlotId,
                        ConditionId = dietCondition.ConditionId
                    };

                    dietDetail.ConditionDetailId = await this.unitOfWork.DietConditionDetail.InsertAsync(dietDetail, transaction);
                    if (dietDetail.ConditionDetailId == 0)
                    {
                        failureCount++;
                    }
                }
                else
                {
                    var dietDetail = await this.unitOfWork.DietConditionDetail.FindAsync(m => m.ConditionDetailId == item.ConditionDetailId, transaction);
                    if (dietDetail == null || dietDetail.ConditionDetailId == 0)
                    {
                        failureCount++;
                    }
                    else
                    {
                        dietDetail.DietItemsId = item.DietItemsId;
                        dietDetail.MeasureId = item.MeasureId;
                        dietDetail.DietSlotId = item.DietSlotId;
                        dietDetail.ConditionId = dietCondition.ConditionId;
                        dietDetail.Quantity = item.Quantity;
                        updated = await this.unitOfWork.DietConditionDetail.UpdateAsync(dietDetail, transaction);
                        if (updated == 0)
                        {
                            failureCount++;
                        }
                    }
                }

            }
            if (failureCount > 0)
            {
                transaction.Rollback();
                return 0;
            }

            transaction.Commit();
            return 1;
        }

        public async Task<int> ModifyPackageStatusAsync(DietConditionsModel model)
        {
            var query = $@"UPDATE ""DietConditionHeader"" SET ""ModifiedBy""={model.ModifiedBy}, ""ModifiedDate""=now(), ""Active""= {model.Active}
	                           WHERE ""ConditionId""= {model.ConditionId}";

            return await this.unitOfWork.Current.ExecuteAsync(query);
        }
    }
}
