

namespace Hims.Infrastructure.Services
{
    using Dapper;
    using Hims.Domain.Entities;
    using Hims.Domain.Entities.Vaccine;
    using Hims.Domain.Repositories.UnitOfWork;
    using Hims.Domain.Services;
    using Hims.Shared.UserModels.Vaccine;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    public class VaccineServices : IVaccineService
    {
        /// <summary>
        /// The unit of work.
        /// </summary>
        private readonly IUnitOfWork unitOfWork;

        /// <inheritdoc cref="IVaccineService" />
        public VaccineServices(IUnitOfWork unitOfWork) => this.unitOfWork = unitOfWork;

        /// <inheritdoc />
        public async Task<int> ModifyVaccineGroupAsync(VaccineGroupModel model)
        {
            var checkIfQuery = $@"select count(*) from ""VaccineGroup"" where lower(""VaccineGroupName"") = lower('{model.VaccineGroupName}')";
            var checkIf = 0;
            if (model.VaccineGroupId > 0)
            {
                checkIfQuery += $@"  and ""VaccineGroupId"" <> {model.VaccineGroupId}";
                checkIf = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<int>(checkIfQuery);
                if (checkIf > 0)
                {
                    return -1;
                }

                var findOld = await this.unitOfWork.VaccineGroups.FindAsync(x => x.VaccineGroupId == model.VaccineGroupId);
                if (findOld == null)
                {
                    return 0;
                }

                findOld.VaccineGroupName = model.VaccineGroupName;
                findOld.ModifiedBy = model.CreatedBy;
                findOld.ModifiedDate = DateTime.Now;
                findOld.Description = model.Description;

                return await this.unitOfWork.VaccineGroups.UpdateAsync(findOld);
            }

            checkIf = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<int>(checkIfQuery);
            if (checkIf > 0)
            {
                return -1;
            }

            var iModel = new VaccineGroup
            {
                CreatedBy = model.CreatedBy,
                CreatedDate = DateTime.Now,
                Description = model.Description,
                VaccineGroupName = model.VaccineGroupName
            };

            return await this.unitOfWork.VaccineGroups.InsertAsync(iModel);

        }

        /// <inheritdoc />
        public async Task<IEnumerable<VaccineGroupModel>> GetAllVaccineGroupsAsync(VaccineGroupModel model)
        {
            var where = "where 1=1";

            if (!string.IsNullOrEmpty(model.VaccineGroupName))
            {
                where += $@" and vg.""VaccineGroupName"" ilike '{model.VaccineGroupName}'";
            }

            var query = $@"SELECT vg.""VaccineGroupId"", vg.""VaccineGroupName"", vg.""Description"", vg.""CreatedBy"", vg.""CreatedDate"", vg.""ModifiedBy"", vg.""ModifiedDate"",
		                            cr.""FullName"" as ""CreatedByName"", m.""FullName"" as ""ModifiedByName""
	                            FROM ""VaccineGroup"" vg
	                            left join ""Account"" cr on cr.""AccountId"" = vg.""CreatedBy"" 
	                            left join ""Account"" m ON m.""AccountId"" =vg.""ModifiedBy"" 
                                {where}
	                            order by vg.""CreatedDate"" desc";

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

        /// <inheritdoc />
        public async Task<int> DeleteVaccineGroupAsync(int groupId)
        {
            var query = $@"delete from  ""VaccineGroup"" where ""VaccineGroupId""= {groupId}";
            return await this.unitOfWork.Current.ExecuteAsync(query);
        }

        /// <inheritdoc />
        public async Task<int> ModifyVaccineMaster(VaccineMasterModel model)
        {
            var checkIfQuery = $@"select count(*) from ""VaccineMaster"" where lower(""VaccineName"") = lower('{model.VaccineName}') ";
            var checkIf = 0;
            if (model.VaccineMasterId > 0)
            {
                checkIfQuery += $@" and ""VaccineMasterId"" <> {model.VaccineMasterId}";
                checkIf = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<int>(checkIfQuery);
                if (checkIf > 0)
                {
                    return -1;
                }

                var findOld = await this.unitOfWork.VaccineMasters.FindAsync(x => x.VaccineMasterId == model.VaccineMasterId);
                if (findOld == null)
                {
                    return 0;
                }

                findOld.ExclusionDays = model.ExclusionDays;
                findOld.AllowedLapsDays = model.AllowedLapsDays;
                findOld.AllowedDays = model.AllowedDays;
                findOld.Order = model.Order;
                findOld.DisplayName = model.DisplayName;
                findOld.ModifiedBy = model.CreatedBy;
                findOld.ModifiedDate = DateTime.Now;
                findOld.VaccineInstruction = model.VaccineInstruction;
                findOld.VaccineGroupId = model.VaccineGroupId;
                findOld.VaccineTypeId = model.VaccineTypeId;
                findOld.AllowedType = model.AllowedType;
                findOld.VaccineAgeGroupId = model.VaccineAgeGroupId;
                return await this.unitOfWork.VaccineMasters.UpdateAsync(findOld);
            }

            var getOrder = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<int?>($@"select ""Order"" from ""VaccineMaster"" order by ""Order"" desc limit 1 ");

            var iModel = new VaccineMaster
            {
                AllowedDays = model.AllowedDays,
                AllowedLapsDays = model.AllowedLapsDays,
                CreatedBy = model.CreatedBy,
                CreatedDate = DateTime.Now,
                DisplayName = model.DisplayName,
                ExclusionDays = model.ExclusionDays,
                Order = getOrder != null ? (int)getOrder + 1 : 1,
                VaccineInstruction = model.VaccineInstruction,
                VaccineGroupId = model.VaccineGroupId,
                VaccineName = model.VaccineName,
                VaccineTypeId = model.VaccineTypeId,
                AllowedType = model.AllowedType,
                Active = true,
                VaccineAgeGroupId = model.VaccineAgeGroupId
            };

            return await this.unitOfWork.VaccineMasters.InsertAsync(iModel);
        }

        /// <inheritdoc />
        public async Task<IEnumerable<VaccineMasterModel>> FetchVaccineMastersAsync(VaccineMasterModel model)
        {
            var where = "where 1=1";
            if (!string.IsNullOrEmpty(model.DisplayName))
            {
                where += $@" and vm.""DisplayName"" ilike '%{model.DisplayName}%'";
            }

            if (!string.IsNullOrEmpty(model.VaccineName))
            {
                where += $@" and vm.""VaccineName"" ilike '%{model.VaccineName}%'";
            }

            if (model.Active != null)
            {
                where += $@" and vm.""Active"" is {model.Active}";
            }

            var query = $@"SELECT vm.""VaccineMasterId"", vm.""VaccineGroupId"", vm.""VaccineName"", vm.""DisplayName"", vm.""VaccineInstruction"", vm.""Order"", vm.""VaccineTypeId"", vm.""AllowedDays"", 
		                            vm.""Active"",vm.""AllowedType"", vm.""ExclusionDays"", vm.""AllowedLapsDays"", vm.""CreatedBy"", vm.""CreatedDate"", vm.""ModifiedBy"", vm.""ModifiedDate"",
		                            vg.""VaccineGroupName"" , vt.""TypeName"" ,vt.""RowColor"" , cr.""FullName"" as ""CreatedByName"", m.""FullName"" as ""ModifiedByName"",
                                    vm.""VaccineAgeGroupId"", vag.""AgeGroupName""
	                            FROM ""VaccineMaster"" vm
	                            join ""VaccineGroup"" vg ON vg.""VaccineGroupId"" = vm.""VaccineGroupId"" 
	                            join ""VaccineType"" vt ON vt.""VaccineTypeId"" = vm.""VaccineTypeId"" 
                                left join ""VaccineAgeGroup"" vag on vag.""VaccineAgeGroupId"" = vm.""VaccineAgeGroupId""
	                            left join ""Account"" cr on cr.""AccountId"" = vg.""CreatedBy"" 
	                            left join ""Account"" m ON m.""AccountId"" = vg.""ModifiedBy"" 
                                {where}
	                            order by vm.""Order"" asc";

            var response = await this.unitOfWork.Current.QueryAsync<VaccineMasterModel>(query);
            if (model.PatientId != null && model.PatientId > 0)
            {
                foreach (var item in response)
                {
                    item.Immunization = new ImmunizationHistoryModel();
                    var subQuery = $@"select ih.""ImmunizationHistoryId"" , ih.""PharmacyProductId"" , ih.""ProductName"" ,ih.""BillNumber"" ,ih.""BatchNumber"" ,ih.""ExpiryDate"" ,
 		                            ih.""CreatedDate"" ,ihc.""FullName"" as ""OrderBy"",ih.""Status"" ,ih.""PatientId"", ih.""VaccineGivenBy"" , ih.""VaccineGivenDate"",
 		                            g.""FullName"" as ""VaccineGivenByName""
 		                            from ""ImmunizationHistory"" ih
 		                            join ""Account"" ihc on ihc.""AccountId"" = ih.""CreatedBy"" 
 		                            left join ""Account"" g on g.""AccountId"" = ih.""VaccineGivenBy"" 
 		                            where ih.""PatientId"" = {model.PatientId} and ih.""VaccineMasterId"" = {item.VaccineMasterId}";
                    item.Immunization = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<ImmunizationHistoryModel>(subQuery);
                }
            }
            return response;
        }

        /// <inheritdoc />
        public async Task<IEnumerable<VaccineTypeModel>> FetchAllVaccineTypesAsync()
        {
            var query = $@"select * from ""VaccineType""";
            return await this.unitOfWork.Current.QueryAsync<VaccineTypeModel>(query);
        }

        /// <inheritdoc />
        public async Task<int> SetVaccineMasterOrderAsync(List<VaccineMasterModel> model)
        {
            var response = 0;
            foreach (var item in model)
            {
                var updateQuery = $@"update ""VaccineMaster"" set ""Order"" = {item.Order} where ""VaccineMasterId"" = {item.VaccineMasterId}";
                response += await this.unitOfWork.Current.ExecuteAsync(updateQuery);
            }

            return response;
        }

        /// <inheritdoc />
        public async Task<int> ChangeVaccineActiveStatusAsync(VaccineMasterModel model)
        {
            var query = $@"update ""VaccineMaster"" set ""Active"" = {model.Active}  where ""VaccineMasterId"" = {model.VaccineMasterId}";
            return await this.unitOfWork.Current.ExecuteAsync(query);
        }

        /// <inheritdoc />
        public async Task<IEnumerable<VaccineAgeGroupModel>> FetchVaccineAgeGroupAsync()
        {
            var query = $@"select * from public.""VaccineAgeGroup"" order by 1 asc";
            return await this.unitOfWork.Current.QueryAsync<VaccineAgeGroupModel>(query);
        }

        /// <inheritdoc />
        public async Task<int> ModifyVaccinePharmacyLinkAsync(VaccinePharmacyLinkHeaderModel model)
        {
            var header = new VaccinePharmacyLinkHeader
            {
                CreatedBy = model.CreatedBy,
                CreatedDate = DateTime.Now,
                PharmacyProductId = model.PharmacyProductId
            };

            using var transaction = this.unitOfWork.BeginTransaction();

            if (model.VaccinePharmacyLinkHeaderId > 0)
            {
                var findOldHeader = await this.unitOfWork.VaccinePharmacyLinkHeaders.FindAsync(h => h.VaccinePharmacyLinkHeaderId == model.VaccinePharmacyLinkHeaderId);
                if (findOldHeader == null)
                {
                    transaction.Rollback();
                    return -1;
                }

                findOldHeader.PharmacyProductId = header.PharmacyProductId;
                findOldHeader.ModifiedBy = header.CreatedBy;
                findOldHeader.ModifiedDate = DateTime.Now;
                header.VaccinePharmacyLinkHeaderId = findOldHeader.VaccinePharmacyLinkHeaderId;
                var res = await this.unitOfWork.VaccinePharmacyLinkHeaders.UpdateAsync(findOldHeader, transaction);
                if (res == 0)
                {
                    transaction.Rollback();
                    return -1;
                }

                var deleteQuery = $@"delete from ""VaccinePharmacyLinkDetail"" where ""VaccinePharmacyLinkHeaderId"" = {findOldHeader.VaccinePharmacyLinkHeaderId}";
                var delRes = await this.unitOfWork.Current.ExecuteAsync(deleteQuery, transaction);
                if (delRes == 0)
                {
                    transaction.Rollback();
                    return -1;
                }
            }
            else
            {
                header.VaccinePharmacyLinkHeaderId = await this.unitOfWork.VaccinePharmacyLinkHeaders.InsertAsync(header, transaction);
                if (header.VaccinePharmacyLinkHeaderId == 0)
                {
                    transaction.Rollback();
                    return -1;
                }
            }

            var details = model.Vaccines.Select(d => new VaccinePharmacyLinkDetail
            {
                VaccineMasterId = d.VaccineMasterId,
                VaccinePharmacyLinkHeaderId = header.VaccinePharmacyLinkHeaderId
            });

            var detRes = await this.unitOfWork.VaccinePharmacyLinkDetails.BulkInsertAsync(details, transaction);
            if (detRes == 0)
            {
                transaction.Rollback();
                return -1;
            }

            transaction.Commit();
            return header.VaccinePharmacyLinkHeaderId;
        }

        /// <inheritdoc />
        public async Task<IEnumerable<VaccinePharmacyLinkHeaderModel>> FetchVaccinePharmaLinkAsync(VaccinePharmacyLinkHeaderModel model)
        {
            var where = "where 1=1";
            var headQuery = $@"SELECT count(vh.*) over() as ""TotalItems"", vh.""VaccinePharmacyLinkHeaderId"", vh.""PharmacyProductId"", vh.""CreatedBy"", vh.""CreatedDate"", vh.""ModifiedBy"", vh.""ModifiedDate"",
		                            c.""FullName"" as ""CreatedByName"", m.""FullName"" as ""ModifiedByName"",pp.""ProductName"" ,pp.""GenericName"", cm.""Name"" as ""CompanyName"",
		                            pp.""ExpertAdvice"" ,pp.""CommonSideEffects"" 
	                            FROM public.""VaccinePharmacyLinkHeader"" vh
	                            join ""PharmacyProduct"" pp on pp.""PharmacyProductId"" = vh.""PharmacyProductId"" 
	                            join ""Company"" cm on cm.""CompanyId"" = pp.""CompanyId"" 
	                            join ""Account"" c on c.""AccountId"" = vh.""CreatedBy"" 
	                            left join ""Account"" m on m.""AccountId"" = vh.""ModifiedBy""  
                                {where}
	                            order by  vh.""CreatedDate"" desc";

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

            foreach (var item in response)
            {
                var query = $@"SELECT vd.""VaccinePharmacyLinkDetailId"", vd.""VaccinePharmacyLinkHeaderId"", vd.""VaccineMasterId"", vm.""VaccineName"" , vm.""DisplayName"" ,
		                                vm.""AllowedDays"" , vm.""AllowedType"" ,vag.""AgeGroupName"" 
		                                FROM public.""VaccinePharmacyLinkDetail"" vd
		                                join ""VaccineMaster"" vm on vm.""VaccineMasterId"" = vd.""VaccineMasterId"" 
		                                join ""VaccineAgeGroup"" vag on vag.""VaccineAgeGroupId"" = vm.""VaccineAgeGroupId"" 
		                                where vd.""VaccinePharmacyLinkHeaderId"" = {item.VaccinePharmacyLinkHeaderId}";

                var det = await this.unitOfWork.Current.QueryAsync<VaccinePharmacyLinkDetailModel>(query);
                item.Vaccines = new List<VaccinePharmacyLinkDetailModel>();
                item.Vaccines.AddRange(det);
            }
            return response;
        }

        /// <inheritdoc />
        public async Task<int> DeletePharmacyLinkAsync(int headerId)
        {
            var deleteQuery = $@"delete from ""VaccinePharmacyLinkDetail"" where ""VaccinePharmacyLinkHeaderId"" = {headerId}";
            await this.unitOfWork.Current.ExecuteAsync(deleteQuery);
            var delQ = $@"delete from ""VaccinePharmacyLinkHeader"" where ""VaccinePharmacyLinkHeaderId"" = {headerId}";
            return await this.unitOfWork.Current.ExecuteAsync(delQ);
        }

        /// <inheritdoc />
        public async Task<IEnumerable<VaccinePharmacyLinkDetailModel>> FetchVaccinesWithPharmacyProductInformationAsync(int vaccineMasterId)
        {
            var query = $@"select vd.""VaccinePharmacyLinkHeaderId"" ,
	                        STRING_AGG (vm.""VaccineName"",',') as ""GroupedVaccines"", 
	                        pp.""ProductName"" ,pp.""GenericName"" , c.""Name"" as ""CompanyName"", pp.""PharmacyProductId"" 
	                        from ""VaccinePharmacyLinkDetail"" vd
	                        join ""VaccinePharmacyLinkHeader"" vh on vh.""VaccinePharmacyLinkHeaderId"" = vd.""VaccinePharmacyLinkHeaderId"" 
	                        join ""VaccineMaster"" vm on vm.""VaccineMasterId"" = vd.""VaccineMasterId"" 
	                        join ""PharmacyProduct"" pp on pp.""PharmacyProductId"" = vh.""PharmacyProductId"" 
	                        join ""Company"" c on c.""CompanyId"" = pp.""CompanyId"" 	
	                        where vd.""VaccinePharmacyLinkHeaderId"" in (select ""VaccinePharmacyLinkHeaderId"" from ""VaccinePharmacyLinkDetail"" where ""VaccineMasterId"" = {vaccineMasterId})
	                        group by  vd.""VaccinePharmacyLinkHeaderId"" , pp.""ProductName"" ,pp.""GenericName"" , c.""Name"", pp.""PharmacyProductId""";
            return await this.unitOfWork.Current.QueryAsync<VaccinePharmacyLinkDetailModel>(query);
        }

        /// <inheritdoc />
        public async Task<int> AddImmunizationOrderAsync(ImmunizationHistoryModel model)
        {
            var getPharmaDet = $@"select vd.* from ""VaccinePharmacyLinkHeader"" vh 
                                join ""VaccinePharmacyLinkDetail"" vd on vd.""VaccinePharmacyLinkHeaderId"" = vh.""VaccinePharmacyLinkHeaderId"" 
                                where vh.""PharmacyProductId"" = {model.PharmacyProductId}";

            var getDetail = await this.unitOfWork.Current.QueryAsync<VaccinePharmacyLinkDetailModel>(getPharmaDet);
            if (getDetail == null)
            {
                return 0;
            }
            var insert = new List<ImmunizationHistory>();
            foreach (var item in getDetail)
            {
                var immun = new ImmunizationHistory
                {
                    BatchNumber = model.BatchNumber,
                    BillNumber = model.BillNumber,
                    CreatedBy = model.CreatedBy,
                    CreatedDate = DateTime.Now,
                    PharmacyProductId = model.PharmacyProductId,
                    ProductName = model.ProductName,
                    Status = "Pending",
                    VaccineMasterId = item.VaccineMasterId,
                    PatientId = model.PatientId
                };
                insert.Add(immun);
            }

            return await this.unitOfWork.ImmunizationHistory.BulkInsertAsync(insert);
        }

        /// <inheritdoc />
        public async Task<IEnumerable<ImmunizationHistoryModel>> CheckProductFromSalesBillAsync(int patientId, string pharmacyProductId)
        {
            var query = $@"select ih.""ImmunizationHistoryId"" , ih.""PharmacyProductId"" , ih.""ProductName"" ,ih.""BillNumber"" ,ih.""BatchNumber"" ,ih.""ExpiryDate"" ,
 		                ih.""CreatedDate"" ,ihc.""FullName"" as ""OrderBy"",ih.""Status"" ,ih.""PatientId"",ih.""VaccineMasterId"" , vm.""VaccineName""
 		                from ""ImmunizationHistory"" ih
 		                join ""Account"" ihc on ihc.""AccountId"" = ih.""CreatedBy"" 
                        join ""VaccineMaster"" vm on vm.""VaccineMasterId"" = ih.""VaccineMasterId""  
 		                where ih.""PatientId"" = {patientId} and ih.""PharmacyProductId"" in ({pharmacyProductId}) and ih.""Status"" = 'Pending'";
            return await this.unitOfWork.Current.QueryAsync<ImmunizationHistoryModel>(query);
        }

        /// <inheritdoc />
        public async Task<int> GiveVaccineAsync(ImmunizationHistoryModel model)
        {
            var updated = 0;
            if (!string.IsNullOrEmpty(model.ImmunizationIds))
            {
                var ids = model.ImmunizationIds.Split(',');
                foreach (var id in ids)
                {
                    var find = await this.unitOfWork.ImmunizationHistory.FindAsync(x => x.ImmunizationHistoryId == Convert.ToInt32(id));
                    if (find == null)
                    {
                        return 0;
                    }
                    find.ExpiryDate = model.ExpiryDate;
                    find.BatchNumber = model.BatchNumber;
                    find.BillNumber = model.BillNumber;
                    find.Status = "Completed";
                    find.ProductName = model.ProductName;
                    find.VaccineGivenBy = model.VaccineGivenBy;
                    find.VaccineGivenDate = DateTime.Now;

                    var update = await this.unitOfWork.ImmunizationHistory.UpdateAsync(find);
                    if (update == 0)
                    {
                        return 0;
                    }
                    updated++;
                }
            }
            else
            {
                return 0;
            }

            return updated;
        }

        /// <inheritdoc />
        public async Task<IEnumerable<ImmunizationHistoryModel>> FetchImmunizationHistoryAsync(int patientId)
        {
            var query = $@"select ih.""ImmunizationHistoryId"" , ih.""PharmacyProductId"" , ih.""ProductName"" ,ih.""BillNumber"" ,ih.""BatchNumber"" ,ih.""ExpiryDate"" ,
 		                            ih.""CreatedDate"" ,ihc.""FullName"" as ""OrderBy"",ih.""Status"" ,ih.""PatientId"", ih.""VaccineGivenBy"" , ih.""VaccineGivenDate"",
 		                            g.""FullName"" as ""VaccineGivenByName"", ih.""VaccineMasterId"" , vm.""VaccineName"" ,vm.""AllowedDays"" ,vm.""AllowedType"" 
 		                            from ""ImmunizationHistory"" ih
 		                            join ""VaccineMaster"" vm on vm.""VaccineMasterId"" = ih.""VaccineMasterId"" 
 		                            join ""Account"" ihc on ihc.""AccountId"" = ih.""CreatedBy"" 
 		                            left join ""Account"" g on g.""AccountId"" = ih.""VaccineGivenBy"" 
 		                            where ih.""PatientId"" =  {patientId}";
            return await this.unitOfWork.Current.QueryAsync<ImmunizationHistoryModel>(query);
        }
    }
}
