namespace Hims.Infrastructure.Services
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Threading.Tasks;

    using Dapper;

    using Domain.Configurations;
    using Domain.Entities;
    using Domain.Repositories.UnitOfWork;
    using Domain.Services;
    using Hims.Domain.Helpers;
    using Hims.Shared.UserModels;
    using Hims.Shared.UserModels.Admission;
    using Hims.Shared.UserModels.Common;
    using Hims.Shared.UserModels.Slots;
    using Shared.DataFilters;
    using Shared.EntityModels;
    using Shared.Library.Enums;
    using Shared.UserModels.Filters;

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

        /// <summary>
        /// The helper.
        /// </summary>
        private readonly IDocumentHelper documentHelper;

        /// <summary>
        /// the wallet service
        /// </summary>
        private readonly IWalletService walletServices;

        /// <summary>
        /// The patient document services.
        /// </summary>
        private readonly IPatientDocumentService patientDocumentServices;
        /// <summary>
        /// The running environment.
        /// </summary>
        private readonly IRunningEnvironment runningEnvironment;
        /// <summary>
        /// The FTP upload helper.
        /// </summary>
        private readonly IFtpUploadHelper ftpUploadHelper;
        /// <summary>
        /// the patient service
        /// </summary>
        private readonly IPatientService patientServices;
        /// <inheritdoc cref="IAppointmentService" />
        public AdmissionServices(IUnitOfWork unitOfWork, IDocumentHelper documentHelper, IWalletService walletServices, IPatientDocumentService patientDocumentService,
            IRunningEnvironment runningEnvironment, IFtpUploadHelper ftpUploadHelper, IPatientService patientServices)
        {
            this.unitOfWork = unitOfWork;
            this.documentHelper = documentHelper;
            this.walletServices = walletServices;
            this.patientDocumentServices = patientDocumentService;
            this.ftpUploadHelper = ftpUploadHelper;
            this.runningEnvironment = runningEnvironment;
            this.patientServices = patientServices;
        }

        /// <inheritdoc />
        public async Task<IEnumerable<AdmissionModel>> FetchAsync(AdmissionFilterModel model)
        {
            try
            {
                var paramsString = "";

                if (model.AdmissionNo != null)
                {
                    paramsString += $@" '{model.AdmissionNo}'";
                }
                else
                {
                    paramsString += $@" null";
                }

                if (model.ProviderId != null)
                {
                    paramsString += $@", {model.ProviderId}";
                }
                else
                {
                    paramsString += $@", null";
                }

                if (model.PatientId != null)
                {
                    paramsString += $@", {model.PatientId}";
                }
                else
                {
                    paramsString += $@", null";
                }

                if (model.IsDischarged != null)
                {
                    paramsString += $@", {model.IsDischarged}";
                }
                else
                {
                    paramsString += $@", null";
                }

                if (model.PatientMobile != null)
                {
                    paramsString += $@", '{model.PatientMobile}'";
                }
                else
                {
                    paramsString += $@", null";
                }

                if (model.FromDate != null)
                {
                    paramsString += $@", '{model.FromDate}'";
                }
                else
                {
                    paramsString += $@", null";
                }

                if (model.ToDate != null)
                {
                    paramsString += $@", '{model.ToDate}'";
                }
                else
                {
                    paramsString += $@", null";
                }

                if (model.DischargeDate != null)
                {
                    paramsString += $@", '{model.DischargeDate}'";
                }
                else
                {
                    paramsString += $@", null";
                }

                if (model.UMRNo != null)
                {
                    paramsString += $@", '{model.UMRNo}'";
                }
                else
                {
                    paramsString += $@", null";
                }
                if (model.Active != null)
                {
                    paramsString += $@", '{model.Active}'";
                }
                else
                {
                    paramsString += $@", null";
                }

                 paramsString += $@", {model.LocationId}";
                if (model.PageIndex != null)
                {
                    model.PageIndex -= 1;
                    paramsString += $@", '{model.PageIndex}'";
                }
                else
                {
                    paramsString += $@", null";
                }
                if (model.PageSize != null)
                {
                    paramsString += $@", '{model.PageSize}'";
                }
                else
                {
                    paramsString += $@", null";
                }

               // model.PageIndex -= 1;
                // var query = $@"select * from ""udf_fetch_Admission1""({paramsString},{model.PageIndex},{model.PageSize})";
                var query = $@"select * from ""udf_fetch_Admission_Location""({paramsString})";

                var admissionModels = await this.unitOfWork.Current.QueryAsync<AdmissionModel>(query);
                if (admissionModels.Any())
                {
                    foreach (var item in admissionModels)
                    {
                        var admissionPackage = await this.unitOfWork.AdmissionPackage.FindAsync(m => m.AdmissionId == item.AdmissionId && m.Active);
                        if (admissionPackage != null && admissionPackage.AdmissionPackageId > 0)
                        {
                            item.AdmissionPackageId = admissionPackage.AdmissionPackageId;
                            item.PackageModuleId = admissionPackage.PackageId;
                        }
                    }
                }
                //var admissionModels = admissions.ToList();
                //try
                //{
                //    foreach (var admission in admissionModels)
                //    {
                //        string url1 = admission.PatientThumbnailUrl;
                //        string url2 = admission.ProviderThumbnailUrl;

                //        try
                //        {
                //            if (url1 != null && url1.Contains("ftp") && (!url1.StartsWith("ftp")))
                //            {
                //                // Get a substring between two strings
                //                int firstStringPosition = url1.IndexOf("ftp");
                //                int secondStringPosition = url1.IndexOf("jpg");
                //                var finalUrl1 = url1.Substring(firstStringPosition, secondStringPosition - firstStringPosition + 3);
                //                if (!string.IsNullOrEmpty(finalUrl1) && finalUrl1.StartsWith("ftp"))
                //                {
                //                    admission.PatientThumbnailUrl = await this.documentHelper.FetchImageBase64(admission.AdmissionId, finalUrl1);
                //                }
                //            }
                //            else
                //            {
                //                admission.PatientThumbnailUrl = null;
                //            }
                //            if (url2 != null && url2.Contains("ftp") && (!url2.StartsWith("ftp")))
                //            {
                //                // Get a substring between two strings
                //                int firstStringPosition = url2.IndexOf("ftp");
                //                int secondStringPosition = url2.IndexOf("jpg");
                //                var finalUrl2 = url2.Substring(firstStringPosition, secondStringPosition - firstStringPosition + 3);
                //                if (!string.IsNullOrEmpty(finalUrl2) && finalUrl2.StartsWith("ftp"))
                //                {
                //                    admission.ProviderThumbnailUrl = await this.documentHelper.FetchImageBase64(admission.AdmissionId, finalUrl2);
                //                }
                //            }
                //            else
                //            {
                //                admission.ProviderThumbnailUrl = null;
                //            }
                //        }
                //        catch (Exception)
                //        {
                //            // ignore
                //        }
                //    }
                //}
                //catch (Exception)
                //{
                //    // ignore
                //}
                return admissionModels;
            }
            catch (Exception ex)
            {
                return null;
            }
        }

        /// <inheritdoc />
        public Task<AdmissionModel> FindAsync(int admissionId)
        {
            try
            {
                var query = $@"select * from ""Admission"" where ""AdmissionId""= {admissionId} ";

                var result = this.unitOfWork.Current.QueryFirstOrDefaultAsync<AdmissionModel>(query);

                return result;
            }
            catch (Exception ex)
            {
                return null;
            }
        }

        /// <inheritdoc />
        public async Task<int> AddAdmissionAsync(ModifyAdmissionModel model)
        {
            try
            {
                var admissionNo = await this.GetAppointmentNo("IP");
                var transaction = this.unitOfWork.BeginTransaction();
                //var query1 = $@"SELECT ""IsDischarged"" FROM ""Admission"" WHERE ""PatientId"" = {model.PatientId} order by 1 desc ";
                var query1 = $@"select ds.""DischargeId"", A.* from ""Admission"" A left join ""Discharge"" ds on ds.""AdmissionId"" = A.""AdmissionId"" and ds.""Active"" = true  WHERE ""PatientId"" = {model.PatientId} AND A.""Active"" IS TRUE order by 1 desc ";
                var existingAdmission = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<AdmissionModel>(query1, transaction);
                if (model.AdmissionId <= 0 && existingAdmission != null && existingAdmission.DischargeId == null)
                {
                    transaction.Commit();
                    return -1;
                }
                var bed = await this.unitOfWork.Beds.FindAsync(x => x.BedId == model.BedId, transaction);
                var admission = new Admission
                {
                    Active = true,
                    AdmissionNo = model.AdmissionId == 0 ? admissionNo : model.AdmissionNo,
                    AdmissionDate = model.AdmissionDate,
                    AdmissionTime = model.AdmissionTime,
                    ProviderId = model.ProviderId,
                    PatientId = model.PatientId,
                    DepartmentId = model.DepartmentId,
                    PatientType = model.PatientType,
                    AdmissionNotes = model.AdmissionNotes,
                    BabysBirthDate = model.BabysBirthDate,
                    BabysBirthTime = model.BabysBirthTime,
                    BabysFathersName = model.BabysFathersName,
                    BabysGender = model.BabysGender,
                    BabysMothersAdmissionNo = model.BabysMothersAdmissionNo,
                    BabysSurgeryType = model.BabysSurgeryType,
                    BedId = model.BedId,
                    EncounterId = model.EncounterId,
                    IsDischarged = model.IsDischarged,
                    IsMaternity = model.IsMaternity,
                    PatientFamilyId = model.PatientFamilyId,
                    ReadyforDischarge = model.ReadyforDischarge,
                    SurgeryTypeId = model.SurgeryTypeId,
                    AttendantName = model.AttendantName,
                    AttendantContactNo = model.AttendantContactNo,
                    AttendantRelationWithPatient = model.AttendantRelationWithPatient,
                    VisitTypeId = model.VisitTypeId,
                    IsConvertedFromOPtoIp = model.IsConvertedFromOPtoIp ?? false,
                    PatientPriorityId = model.PatientPriorityId,
                    LocationId = model.LocationId,
                    CaseTypeId = model.CaseTypeId,
                    AdmissionPayTypeId = model.AdmissionPayTypeId,
                    InsuranceCompanyId = model.InsuranceCompanyId,
                    ReferralDoctorId = model.ReferralDoctorId,
                    TpaId = model.TpaId,
                    PatientOrganization = model.PatientOrganization,
                    BedAssociatedData = model.BedAssociatedData,
                    NRIData = model.NRIData,
                    SurrogacyData = model.SurrogacyData,
                    EmergencyInfo = model.EmergencyInfo,
                    DoctorUnitMasterId = model.DoctorUnitMasterId,
                    CurrentRoomId = bed.RoomId,
                    CounsellingId = model.CounsellingId
                };

                if (model.AdmissionId == 0)
                {
                    admission.CreatedBy = model.CreatedBy;
                    admission.CreatedDate = DateTime.UtcNow;
                    admission.AdmissionId = await this.unitOfWork.Admission.InsertAsync(admission, transaction);

                    if (admission.AdmissionId > 0 && model.CounsellingId != null)
                    {
                        var counselling = await this.unitOfWork.Counsellings.FindAsync(x => x.CounsellingId == model.CounsellingId);
                        counselling.ModifiedBy = model.CreatedBy;
                        counselling.ModifiedDate = DateTime.UtcNow;
                        counselling.IsInUse = true;
                        var counsellingResponse = await this.unitOfWork.Counsellings.UpdateAsync(counselling);
                        if (counsellingResponse <= 0)
                        {
                            transaction.Rollback();
                            return 0;
                        }

                        if (counselling.PackageModuleId != 0)
                        {
                            // update package
                            var admissionPackage = new AdmissionPackage
                            {
                                Active = true,
                                CreatedDate = DateTime.Now,
                                CreatedBy = model.CreatedBy,
                                AdmissionId = admission.AdmissionId,
                                AppointmentId = null,
                                PackageId = counselling.PackageModuleId
                            };

                            admissionPackage.AdmissionPackageId = await this.unitOfWork.AdmissionPackage.InsertAsync(admissionPackage, transaction);
                            model.AdmissionPackageId = admissionPackage.AdmissionPackageId;
                            if (admissionPackage.AdmissionPackageId == 0)
                            {
                                transaction.Rollback();
                                return -1;
                            }
                        }
                    }

                    if (model.BedId != null)
                    {
                        var query = $@"update ""Bed"" set ""BedStatusId"" = 2 where ""BedId""= {model.BedId} ";
                        var bedResponse = await this.unitOfWork.Current.ExecuteAsync(query, transaction);
                        if (bedResponse < 0)
                        {
                            transaction.Rollback();
                            return 0;
                        }
                    }
                }
                else
                {
                    if (model.BedId != null)
                    {
                        if (model.swap == false || model.swap == null)
                        {
                            // check if bed available
                            var existingAdmissionSub = await this.unitOfWork.Admission.FindAsync(x => x.AdmissionId == model.AdmissionId, transaction);
                            if (existingAdmissionSub.BedId == null || existingAdmissionSub.BedId != model.BedId)
                            {
                                //var bed = await this.unitOfWork.Beds.FindAsync(x => x.BedId == model.BedId, transaction);
                                if (bed.BedStatusId == 1)
                                {
                                    var query = $@"update ""Bed"" set ""BedStatusId"" = 2 where ""BedId""= {model.BedId} ";
                                    var bedResponse = await this.unitOfWork.Current.ExecuteAsync(query, transaction);
                                    if (bedResponse < 0)
                                    {
                                        transaction.Rollback();
                                        return 0;
                                    }

                                    if (existingAdmissionSub.BedId > 0)
                                    {
                                        query = $@"update ""Bed"" set ""BedStatusId"" = 1 where ""BedId""= {existingAdmissionSub.BedId} ";
                                        bedResponse = await this.unitOfWork.Current.ExecuteAsync(query, transaction);
                                        if (bedResponse < 0)
                                        {
                                            transaction.Rollback();
                                            return 0;
                                        }
                                    }
                                }
                                else
                                {
                                    transaction.Rollback();
                                    return 2;
                                }
                            }
                        }
                        else
                        {
                            int count = 0;
                            var existingAdmissionSub = await this.unitOfWork.Admission.FindAsync(x => x.BedId == model.BedId, transaction);
                            int secAdmmissuioin = existingAdmissionSub.AdmissionId;
                            var existingAdmissionSub2 = await this.unitOfWork.Admission.FindAsync(x => x.AdmissionId == model.AdmissionId, transaction);
                            int firstAdmmision = existingAdmissionSub2.AdmissionId;
                            int firstbed = (int)existingAdmissionSub2.BedId;
                            int secbed = (int)existingAdmissionSub.BedId;
                            existingAdmissionSub.ModifiedBy = model.ModifiedBy;
                            existingAdmissionSub.ModifiedDate = DateTime.UtcNow;

                            existingAdmissionSub.BedId = firstbed;
                            existingAdmissionSub2.BedId = secbed;
                            var result1 = await this.unitOfWork.Admission.UpdateAsync(existingAdmissionSub, transaction);
                            var result2 = await this.unitOfWork.Admission.UpdateAsync(existingAdmissionSub2, transaction);
                            count = result1 + result2;
                            if (count < 2)
                            {
                                transaction.Rollback();
                                return -1;
                            }
                        }
                    }

                    admission.AdmissionId = model.AdmissionId;
                    admission.ModifiedBy = model.ModifiedBy;
                    admission.ModifiedDate = DateTime.UtcNow;
                    var updateResponse = await this.unitOfWork.Admission.UpdateAsync(admission, transaction);
                    if (updateResponse < 0)
                    {
                        transaction.Rollback();
                        return 0;
                    }
                }

                if (model.Breakfast != null || model.Lunch != null || model.Dinner != null)
                {
                    var patientTiming = await this.unitOfWork.PatientTiming.FindAsync(x => x.PatientId == model.PatientId, transaction);
                    if (patientTiming != null)
                    {
                        patientTiming.Breakfast = model.Breakfast != null ? model.Breakfast : patientTiming.Breakfast;
                        patientTiming.Lunch = model.Lunch != null ? model.Lunch : patientTiming.Lunch;
                        patientTiming.Dinner = model.Dinner != null ? model.Dinner : patientTiming.Dinner;
                        await this.unitOfWork.PatientTiming.UpdateAsync(patientTiming, transaction);
                    }
                    else
                    {
                        await this.unitOfWork.PatientTiming.InsertAsync(new PatientTiming
                        {
                            PatientId = model.PatientId,
                            Breakfast = model.Breakfast,
                            Lunch = model.Lunch,
                            Dinner = model.Dinner
                        }, transaction);
                    }
                }

                if (model.PackageModuleId != null && model.PackageModuleId > 0)
                {
                    if (model.AdmissionPackageId == null || model.AdmissionPackageId == 0)
                    {
                        var admissionPackage = new AdmissionPackage
                        {
                            AdmissionId = admission.AdmissionId,
                            Active = true,
                            AppointmentId = null,
                            CreatedBy = (int)(model.AdmissionId == 0 ? model.CreatedBy : model.ModifiedBy),
                            CreatedDate = DateTime.UtcNow,
                            ModifiedBy = null,
                            ModifiedDate = null,
                            PackageId = (int)model.PackageModuleId
                        };

                        admissionPackage.AdmissionPackageId = await this.unitOfWork.AdmissionPackage.InsertAsync(admissionPackage, transaction);
                        if (admissionPackage.AdmissionPackageId < 0)
                        {
                            transaction.Rollback();
                            return 0;
                        }
                    }
                    else
                    {
                        var admissionPackage = await this.unitOfWork.AdmissionPackage.FindByIdAsync(model.AdmissionPackageId, transaction);
                        admissionPackage.AdmissionId = admission.AdmissionId;
                        admissionPackage.PackageId = (int)model.PackageModuleId;
                        admissionPackage.ModifiedBy = (int)(model.AdmissionId == 0 ? model.CreatedBy : model.ModifiedBy);
                        admissionPackage.ModifiedDate = DateTime.UtcNow;
                        var updated = await this.unitOfWork.AdmissionPackage.UpdateAsync(admissionPackage, transaction);
                        if (updated < 0)
                        {
                            transaction.Rollback();
                            return 0;
                        }
                    }
                }
                var patientDetails = await this.patientServices.FindAsync(model.PatientId);


                if (model.Files != null && model.Files.Count > 0)
                {
                    var patientDocuments = new List<PatientDocumentModel>();
                    var index = 0;
                    foreach (var file in model.Files)
                    {
                        var docModel = new PatientDocumentModel
                        {
                            DocumentType = model.DocumentType,
                            Description = model.Description,
                            DocumentName = index == 0 ? model.DocumentName : $"{model.DocumentName}_{index}",
                            UploadedBy = model.CreatedBy,
                            PatientId = model.PatientId,
                            ContentType = file.ContentType,
                            Size = file.Length
                        };

                        var filePath = $@"{this.runningEnvironment.CurrentEnvironment}/{patientDetails.Guid}/{model.DocumentType}";

                        try
                        {
                            await this.ftpUploadHelper.CreateDirectory(filePath);
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine(e.Message);
                        }
                        model.DocumentName = index == 0 ? model.DocumentName : $"{model.DocumentName}_{index}";
                        var dbPath = $@"{model.DocumentName}_{DateTime.UtcNow.Ticks}{Path.GetExtension(file.FileName)}";
                        filePath += $@"/{dbPath}";

                        var uploadResponse = await this.ftpUploadHelper.UploadFromFileAsync(filePath, file);
                        if (uploadResponse <= 0)
                        {
                            return 0;
                        }
                        docModel.ThumbnailUrl = this.documentHelper.GetThumbnail(file.ContentType);
                        docModel.DocumentUrl = dbPath;
                        patientDocuments.Add(docModel);
                        index++;
                    }
                    await this.patientDocumentServices.AddAsync(patientDocuments);
                }
                transaction.Commit();
                return admission.AdmissionId;
            }
            catch (Exception ex)
            {
                return 0;
            }
        }

        /// <inheritdoc />
        public Task<IEnumerable<BedManagementModel>> FetchWardsAsync(int locationId)
        {
            var query = $@"select distinct ""WardId"",""WardName"",count(""RoomId"")over(partition by ""WardId"")""NoOfRooms"",sum(""NoOfBeds"") over(partition by ""WardId"")""NoOfBeds"",sum(""AvailableBeds"") over(partition by ""WardId"")""AvailableBeds"",
                sum(""BookedBed"") over(partition by ""WardId"")""BookedBed""  from (
                select w.""WardId"",w.""WardName"",r.""RoomId"" ""RoomId"" ,(select count(""BedId"") from ""Bed"" B where r.""RoomId"" = B.""RoomId"" )""NoOfBeds""
                ,(select count(""BedId"") from ""Bed"" B where r.""RoomId"" = B.""RoomId"" and B.""BedStatusId""=1) ""AvailableBeds"",
                (select count(""BedId"") from ""Bed"" B where r.""RoomId"" = B.""RoomId"" and B.""BedStatusId""=2) ""BookedBed"" from ""Ward"" w
                left join ""Room"" r on r.""WardId"" = w.""WardId""
                JOIN ""Floor"" f on f.""FloorId""=w.""FloorId""
                WHERE f.""LocationId"" = {locationId}) a
                order by a.""WardId""  ";

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

        /// <inheritdoc />
        public Task<AdmissionModel> FetchAdmissionInfo(int admissionId)
        {
            var query = $@"select p.""FullName"" ""PatientName"",pf.""FullName"" ""FamilyMemberName"",pf.""Relation"",ac.""FullName"" ""CreatedByName"",rc.""RoleName"" ""CreatedByRoleName"",
                            am.""FullName"" ""ModifiedByName"",rm.""RoleName"" ""ModifiedByRoleName"" ,ad.""AdmissionNo"", pr.""FullName"" as ""ProviderName""
                            from ""Admission"" ad
                            join ""Patient"" p on p.""PatientId"" = ad.""PatientId""
                            join ""Account"" ac on ac.""AccountId"" = ad.""CreatedBy""
			                join ""Provider"" pr on pr.""ProviderId""=ad.""ProviderId""
							left join ""PatientFamily"" pf on pf.""PatientFamilyId"" = ad.""PatientFamilyId""
                            left join ""Account"" am on am.""AccountId"" = ad.""ModifiedBy""
                            join ""Role"" rc on rc.""RoleId"" = ac.""RoleId""
                            left join ""Role"" rm on rm.""RoleId"" = am.""RoleId"" where ""AdmissionId""={admissionId} ";

            return this.unitOfWork.Current.QueryFirstOrDefaultAsync<AdmissionModel>(query);
        }

        /// <inheritdoc />
        public async Task<int> CancelAsync(int admissionId)
        {
            var result = 1;
            var admission = await this.unitOfWork.Admission.FindAsync(x => x.AdmissionId == admissionId);
            admission.Active = false;
            result = await this.unitOfWork.Admission.UpdateAsync(admission);

            var bed = await this.unitOfWork.Beds.FindAsync(x => x.BedId == admission.BedId);
            if (bed != null)
            {
                bed.BedStatusId = 1;
                result = await this.unitOfWork.Beds.UpdateAsync(bed);
            }
            return result;
        }

        /// <inheritdoc />
        public Task<IEnumerable<BedManagementModel>> FetchRoomsAsync(int? wardId, int? chargeCategoryId, int? locationId)
        {
            var where = $@"where 1=1";
            if (wardId != null)
            {
                where += $@" and w.""WardId"" = {wardId}";

            }
            if (chargeCategoryId != null)
            {
                where += $@" AND r.""ChargeCategoryId"" = {chargeCategoryId}";
            }
            if (locationId != null)
            {
                where += $@" AND f.""LocationId"" = {locationId}";
            }

            var query = $@"select distinct ""WardId"",""RoomId"",""RoomName"",""RoomRent"",sum(""NoOfBeds"") over(partition by ""RoomId"")""NoOfBeds"",
                            sum(""AvailableBeds"") over(partition by ""RoomId"")""AvailableBeds"",
							sum(""BookedBed"") over(partition by ""RoomId"")""BookedBed""
							from (
                            select w.""WardId"",w.""WardName"",r.""RoomId"" ""RoomId"",r.""RoomName"",r.""RoomRent"" ,
                            (select count(""BedId"") from ""Bed"" B where r.""RoomId"" = B.""RoomId"" )""NoOfBeds"",
	                        (select count(""BedId"") from ""Bed"" B where r.""RoomId"" = B.""RoomId"" and B.""BedStatusId""=1) ""AvailableBeds"",
							(select count(""BedId"") from ""Bed"" B where r.""RoomId"" = B.""RoomId"" and B.""BedStatusId""=2) ""BookedBed""
                            from ""Ward"" w
                            join ""Room"" r on r.""WardId"" = w.""WardId""
                            {where} --where w.""WardId"" = {wardId}
) a
                            order by a.""WardId"" ";

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

        /// <inheritdoc />
        public Task<IEnumerable<PriorityViewModel>> FetchPrioritiesAsync()
        {
            var query = @"SELECT
	                        ""PatientPriorityId"" ""Id"",
	                        ""Name"",
	                        ""Icon"",
	                        ""Color""
                        FROM
	                        ""PatientPriority"" ORDER BY ""Id"" ";

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

        /// <inheritdoc />
        public Task<IEnumerable<BedManagementModel>> FetchBedsAsync(BedManagementModel model)
        {
            //no need of Location Because we are Already Charges Based Location wise For rooms.
            var where = $@"";
            if (model.ModulesMasterId != null)
            {
                where += $@" and cmc.""ModulesMasterId""  = {model.ModulesMasterId}";
            }
            if (model.ChargeModuleTemplateId != null)
            {
                where += $@" and cmc.""ChargeModuleTemplateId"" = {model.ChargeModuleTemplateId}";
            }
            if (model.ChargeCategoryId != null)
            {
                where += $@" and cmc.""ChargeCategoryId"" = {model.ChargeCategoryId}";
            }
            var query = $@"SELECT  cmd.""Amount"" as ""RoomRent"",mm.""ModuleName"" ,r.""RoomId"",r.""RoomName"",b.""BedId"",b.""BedNumber"",w.""WardId"",w.""WardName"",bs.""BedStatusName"",b.""BedStatusId"",b.""BedType"",b.""Active"",r.""ChargeCategoryId"",cc.""ChargeCategoryName"", f.""FloorName"" 
                                FROM ""ChargeModuleDetails"" cmd
	                            join ""ChargeModuleCategory"" cmc on cmc.""ChargeModuleCategoryId"" = cmd.""ChargeModuleCategoryId""
	                            join ""ChargeCategory"" cc on cc.""ChargeCategoryId"" = cmc.""ChargeCategoryId"" and cc.""Active"" =true
                                join ""ChargeModuleTemplate"" cmt on cmt.""ChargeModuleTemplateId"" = cmc.""ChargeModuleTemplateId"" and cmt.""IsInUse"" is true
                                join ""ModulesMaster"" mm on mm.""ModulesMasterId"" = cmc.""ModulesMasterId""
                                join ""Room"" r on r.""RoomId"" = cmd.""ReferenceId""
                                join ""Ward"" w on w.""WardId"" = r.""WardId""
                                join ""Floor"" f on f.""FloorId"" = w.""FloorId""
                                join ""Bed"" b on b.""RoomId"" = r.""RoomId"" and b.""BedStatusId"" = 1
                                join ""BedStatus"" bs on bs.""BedStatusId""=b.""BedStatusId""
                                Where 1=1 {where}
                                order by cmd.""ChargeModuleDetailsId"" asc ";
            return this.unitOfWork.Current.QueryAsync<BedManagementModel>(query);
        }

        /// <summary>
        /// The get appointment number.
        /// </summary>
        /// <param name="patientType">
        /// The patient Type.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        private async Task<string> GetAppointmentNo(string patientType)
        {
            // Ex: IP20010005/OP20010005
            var admissionNumber = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<string>($@"SELECT ""AdmissionNo"" FROM ""Admission"" where ""AdmissionNo"" ILIKE '%{patientType}%' ORDER BY ""AdmissionId"" DESC");
            return CoreFilter.GetTransactionId(admissionNumber, patientType);
        }

        /// <summary>
        /// Raises the bed change request asynchronous.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <returns></returns>
        public async Task<int> RaiseBedChangeRequestAsync(AdmissionBedChangeRequestModel model)
        {
            var check = await this.unitOfWork.AdmissionBedChangeRequests.FindAsync(x => x.AdmissionId == model.AdmissionId && x.Active == true);
            if (check != null)
            {
                return -2;
            }

            var request = new AdmissionBedChangeRequest
            {
                Active = true,
                AdmissionId = model.AdmissionId,
                RequestComments = model.RequestComments,
                ChargeCategories = model.ChargeCategories.ToArray(),
                CreatedBy = model.CreatedBy,
                CreatedDate = DateTime.Now,

            };
            request.AdmissionBedChangeRequestId = await this.unitOfWork.AdmissionBedChangeRequests.InsertAsync(request);
            if (request.AdmissionBedChangeRequestId > 0)
            {
                var query1 = $@"UPDATE ""AdmissionBedChangeRequest"" SET ""ChargeCategories"" = ARRAY[{model.ChargeCategories}] WHERE ""AdmissionBedChangeRequestId""= '{request.AdmissionBedChangeRequestId}'";
                await this.unitOfWork.Current.ExecuteAsync(query1);
                return request.AdmissionBedChangeRequestId;
            }
            else
            {
                return -1;
            }

        }

        public async Task<int> AddAdmissionTransferRequest(AdmissionTransferRequestModel model)
        {


            var record = new AdmissionTransferRequest
            {
                AdmissionId = (int)model.AdmissionId,
                DoctorUnitMasterId = model.DoctorUnitMasterId != null ? model.DoctorUnitMasterId : null,
                Active = true,
                CreatedBy = (int)model.CreatedBy,
                CreatedDate = DateTime.Now,
                LocationId = model.LocationId,
                AdmissionChangeRequestTypeId = model.AdmissionChangeRequestTypeId,
                RequestComments = model.RequestComments,
                ChargeCategories = model.ChargeCategories != null ? model.ChargeCategories.ToArray() : null
            };
            record.AdmissionTransferRequestId = await this.unitOfWork.AdmissionTransferRequests.InsertAsync(record);
            if (record.AdmissionTransferRequestId > 0 && model.AdmissionChangeRequestTypeId == 1)
            {
                var query1 = $@"UPDATE ""AdmissionTransferRequest"" SET ""ChargeCategories"" = ARRAY[{model.ChargeCategories}] WHERE ""AdmissionTransferRequestId""= '{record.AdmissionTransferRequestId}'";
                await this.unitOfWork.Current.ExecuteAsync(query1);
            }
            return record.AdmissionChangeRequestTypeId;

        }

        public Task<IEnumerable<AdmissionTransferRequestModel>> FetchAdmissionTransferRequest(AdmissionTransferRequestModel model)
        {
            var where = $@" WHERE 1=1";
            if (model.LocationId != 0)
            {
                where += $@" and ATR.""LocationId"" = {model.LocationId}";
            }
            if (model.AdmissionId != 0)
            {
                where += $@" and ATR.""AdmissionId"" = {model.AdmissionId}";
            }
            if (model.AdmissionChangeRequestTypeId != 0)
            {
                where += $@" and ATR.""AdmissionChangeRequestTypeId"" = {model.AdmissionChangeRequestTypeId}";
            }
            if (model.Active != null)
            {
                where += $@" and ATR.""Active"" = {model.Active}";
            }

            var query = $@"select count(*) over () as ""TotalItems"" ,  ATR.""AdmissionTransferRequestId"", ATR.""AdmissionId"",ATR.""DoctorUnitMasterId"",ATR.""Active"", PT.""FullName"", DM.""UnitName"",ACRT.""AdmissionChangeRequestTypeId"",ACRT.""ChangeRequestType"", 
                C.""FullName"" as ""CreatedByName"", M.""FullName"" as ""ApprovedByName"" ,  R.""FullName"" as ""RejectedByName"",ATR.""CreatedDate"", ATR.""ApprovedDate"",ATR.""RejectedDate"",ATR.""ChargeCategories"":: text, string_agg(DISTINCT CC.""ChargeCategoryName"", ',') AS ""ChargeCategoryName""
                from ""AdmissionTransferRequest"" ATR
                join ""Admission"" AD on AD.""AdmissionId""= ATR.""AdmissionId"" and AD.""Active""=true
                join ""Patient"" PT on PT.""PatientId""=AD.""PatientId""
                left Join ""DoctorUnitMaster"" DM ON DM.""DoctorUnitMasterId""=ATR.""DoctorUnitMasterId""
                 join ""Account"" C on C.""AccountId"" = ATR.""CreatedBy""
				 left join ""ChargeCategory"" CC ON CC.""ChargeCategoryId""=ANY(ATR.""ChargeCategories"")
				 left join ""AdmissionChangeRequestType"" ACRT on ACRT.""AdmissionChangeRequestTypeId""= ATR.""AdmissionChangeRequestTypeId""
                left join ""Account"" M on M.""AccountId"" = ATR.""ApprovedBy"" 
                 left join ""Account"" R on R.""AccountId"" = ATR.""RejectedBy""                {where}
				group by ATR.""AdmissionTransferRequestId"",PT.""FullName"",DM.""UnitName"",ACRT.""AdmissionChangeRequestTypeId"",ACRT.""ChangeRequestType"", C.""FullName"",M.""FullName"", R.""FullName"" Order By ATR.""CreatedDate"" desc
				";
            if (model.PageSize != null && model.PageIndex != null)
            {
                model.PageIndex = model.PageIndex > 0 ? model.PageIndex - 1 : model.PageIndex;
                query += $@" limit {model.PageSize} offset {model.PageIndex * model.PageSize}";
            }
            return this.unitOfWork.Current.QueryAsync<AdmissionTransferRequestModel>(query);
        }

        public async Task<int> AcceptTransferRequest(AdmissionTransferRequestModel model)
        {
            int request2 = 0;          
            var query1 = $@"UPDATE ""AdmissionTransferRequest"" SET ""Active"" = false, ""ApprovedBy""='{model.ApprovedBy}', ""ApprovedDate""=now() WHERE ""AdmissionTransferRequestId""= '{model.AdmissionTransferRequestId}'";
            var request = await this.unitOfWork.Current.ExecuteAsync(query1);
            if (request > 0)
            {
                if (model.DoctorUnitMasterId != 0)
                {
                    var query2 = $@"UPDATE ""Admission"" SET ""DoctorUnitMasterId"" = {model.DoctorUnitMasterId} WHERE ""AdmissionId""= '{model.AdmissionId}'";
                    request2 = await this.unitOfWork.Current.ExecuteAsync(query2);

                }
                else if (model.BedId != 0)
                {
                    var bed = await this.unitOfWork.Beds.FindAsync(x => x.BedId == model.BedId);
                    var query2 = $@"UPDATE ""Admission"" SET ""BedId"" = {model.BedId} , ""CurrentRoomId"" = {bed.RoomId} WHERE ""AdmissionId""= '{model.AdmissionId}'";
                    request2 = await this.unitOfWork.Current.ExecuteAsync(query2);
                }

            }
            return request2;

        }

        public async Task<int> RejectAdmissionTransferRequest(AdmissionTransferRequestModel model)
        {

            var query1 = $@"UPDATE ""AdmissionTransferRequest"" SET ""Active"" = false, ""RejectedBy""='{model.RejectedBy}', ""RejectedDate""=now()  WHERE ""AdmissionTransferRequestId""= '{model.AdmissionTransferRequestId}'";
            return await this.unitOfWork.Current.ExecuteAsync(query1);

        }

        public async Task<FinalBillModel> FindFinalBillAsync(int finalBillId)
        {
            var query = $@"select fb.*,fb.""FinalAmount"" as ""Amount"",ad.""PatientId"" from ""FinalBill"" fb
left join ""Admission"" ad on ad.""AdmissionId""=fb.""AdmissionId"" where fb.""AdmissionId""={finalBillId}";
            return await this.unitOfWork.Current.QuerySingleOrDefaultAsync<FinalBillModel>(query);
        }
    }
}