using System.Collections.Generic;
using Dapper;

namespace Hims.Infrastructure.Services
{
    using System;
    using System.Threading.Tasks;
    using Hims.Domain.Repositories.UnitOfWork;
    using Hims.Domain.Services;
    using Ward = Shared.UserModels.BedManagement.Ward;
    using Room = Shared.UserModels.BedManagement.Room;
    using Bed = Shared.UserModels.BedManagement.Bed;
    using Hims.Shared.UserModels;
    using Hims.Shared.UserModels.BedManagement.Floor;
    using Status = Shared.UserModels.BedManagement.Status;

    /// <summary> The chat service.</summary>
    public class BedManagementService : IBedManagementService
    {
        /// <summary>
        /// The unit of work.
        /// </summary>
        private readonly IUnitOfWork unitOfWork;

        /// <inheritdoc cref="IChatService" />
        public BedManagementService(IUnitOfWork unitOfWork) => this.unitOfWork = unitOfWork;

        public async Task<IEnumerable<Ward.ViewModel>> FetchWardsAsync(int locationId)
        {
            var query = $@"SELECT
	                        w.""WardId"",
	                        w.""WardName"",
	                        room.*
                        FROM
	                        ""Ward"" w
                        JOIN  ""Floor"" f on f.""FloorId""=w.""FloorId"" and f.""LocationId""={locationId}

                            LEFT JOIN LATERAL (
		                        SELECT COUNT(r.""RoomId"") OVER() AS ""TotalRooms"", bed.""TotalBeds"", bedAvailable.""TotalBedsAvailable"", bedCleaning.""TotalBedsCleaning"", bedOccupied.""TotalBedsOccupied"" FROM ""Room"" r
			                        LEFT JOIN LATERAL (
				                        SELECT COUNT(b.""BedId"") OVER() AS ""TotalBeds"" FROM ""Bed"" b
				                        WHERE b.""RoomId"" = r.""RoomId""
				                        LIMIT 1
			                        ) bed ON true
			                        LEFT JOIN LATERAL (
				                        SELECT COUNT(b.""BedId"") OVER() AS ""TotalBedsAvailable"" FROM ""Bed"" b
				                        WHERE b.""RoomId"" = r.""RoomId"" AND b.""BedStatusId"" = 1
				                        LIMIT 1
			                        ) bedAvailable ON true
                                    LEFT JOIN LATERAL (
				                        SELECT COUNT(b.""BedId"") OVER() AS ""TotalBedsCleaning"" FROM ""Bed"" b
				                        WHERE b.""RoomId"" = r.""RoomId"" AND b.""BedStatusId"" = 4
				                        LIMIT 1
			                        ) bedCleaning ON true
                                    LEFT JOIN LATERAL (
				                        SELECT COUNT(b.""BedId"") OVER() AS ""TotalBedsOccupied"" FROM ""Bed"" b
				                        WHERE b.""RoomId"" = r.""RoomId"" AND b.""BedStatusId"" = 2
				                        LIMIT 1
			                        ) bedOccupied ON true
		                        WHERE r.""WardId"" = w.""WardId""
		                        LIMIT 1
                        ) room ON true ORDER BY w. ""WardId"" ";
            var records = await this.unitOfWork.Current.QueryAsync<Ward.ViewModel>(query);
            return records;
        }

        public async Task<IEnumerable<Room.ViewModel>> FetchRoomsAsync(int wardId)
        {
            var query = $@"SELECT
                            r.""RoomId"",
                            r.""RoomName"",
                            r.""RoomRent"",
	                        room.*,
	                        bedAvailable.*,
	                        bedCleaning.*,
	                        bedOccupied.*
                            FROM
	                            ""Room"" r
	                            LEFT JOIN LATERAL (
		                            SELECT COUNT(b.""BedId"") OVER() AS ""TotalBeds""
		                            FROM ""Bed"" b
		                            WHERE b.""RoomId"" = r.""RoomId""
		                            LIMIT 1
	                            ) room ON true
	                            LEFT JOIN LATERAL (
		                            SELECT COUNT(b.""BedId"") OVER() AS ""TotalBedsAvailable"" FROM ""Bed"" b
		                            WHERE b.""RoomId"" = r.""RoomId"" AND b.""BedStatusId"" = 1
		                            LIMIT 1
	                            ) bedAvailable ON true
	                            LEFT JOIN LATERAL (
		                            SELECT COUNT(b.""BedId"") OVER() AS ""TotalBedsCleaning"" FROM ""Bed"" b
		                            WHERE b.""RoomId"" = r.""RoomId"" AND b.""BedStatusId"" = 4
		                            LIMIT 1
	                            ) bedCleaning ON true
	                            LEFT JOIN LATERAL (
		                            SELECT COUNT(b.""BedId"") OVER() AS ""TotalBedsOccupied"" FROM ""Bed"" b
		                            WHERE b.""RoomId"" = r.""RoomId"" AND b.""BedStatusId"" = 2
		                            LIMIT 1
	                            ) bedOccupied ON true
                            WHERE r.""WardId"" = {wardId} ORDER BY r. ""RoomId""";
            var records = await this.unitOfWork.Current.QueryAsync<Room.ViewModel>(query);
            return records;
        }

        public async Task<IEnumerable<Bed.ViewModel>> FetchBedsAsync(int roomId)
        {
            var query = $@"SELECT DISTINCT ON(b.""BedId"")
	                        b.""BedId"",
	                        b.""BedNumber"",
	                        b.""BedStatusId"",
	                        s.""BedStatusName"",
	                        A.""AdmissionNo"",
	                        A.""AdmissionDate"",
	                        p.""FullName"",
	                        P.""Gender"",
	                        P.""ThumbnailUrl"",
	                        P.""Guid"" :: TEXT,
	                        P.""Age"",
	                        pp.""FullName"" AS ""ProviderName"",
	                        pp.""Gender"" AS ""ProviderGender"",
	                        de.""DepartmentName""
                        FROM
	                        ""Bed"" b
	                        JOIN ""BedStatus"" s ON s.""BedStatusId"" = b.""BedStatusId""
	                        LEFT JOIN ""Admission"" A ON A.""BedId"" = b.""BedId""
	                        LEFT JOIN ""Department"" de on de.""DepartmentId"" = a.""DepartmentId""
	                        LEFT JOIN ""Discharge"" d ON d.""AdmissionId"" = A.""AdmissionId""
	                        LEFT JOIN ""Patient"" P on P.""PatientId"" = A.""PatientId""
	                        LEFT JOIN ""Provider"" pp ON pp.""ProviderId"" = A.""ProviderId"" 
                        WHERE
	                        (d.""DischargeId"" IS NULL OR b.""BedStatusId"" = 1) AND b.""RoomId"" = {roomId} ORDER BY b. ""BedId"" ";
            var records = await this.unitOfWork.Current.QueryAsync<Bed.ViewModel>(query);
            return records;
        }

        public async Task<string> FetchBedsAvailabilityAsync(BedManagementFilterModel model)
        {
            var where = $@"where 1=1";
            if (model.FloorId != null)
            {
                where += $@"and F.""FloorId""={model.FloorId}";
            }
            if (model.LocationId > 0)
            {
                where += $@"and F.""LocationId""={model.LocationId}";
            }
            if (model.WardId != null)
            {
                where += $@"and w.""WardId""={model.WardId}";
            }
            if (model.RoomId != null)
            {
                where += $@"and R.""RoomId""={model.RoomId}";
            }
            if (model.BedId != null)
            {
                where += $@"and B.""BedId""={model.BedId}";
            }
            if (model.BedStatusId != null)
            {
                where += $@"and Bs.""BedStatusId""={model.BedStatusId}";
            }
            //var query = $@" select jsonb_agg(jsonb_build_object('floorId ',a.""FloorId"",'floorName',a.""FloorName"",'wards',a.""Ward"")) ::text
            //                from(
            //                select a.""FloorId"",a.""FloorName"",
            //                jsonb_agg(jsonb_build_object('wardId ',a.""WardId"",'wardName',a.""WardName"",'rooms',a.""Room""))""Ward""
            //                from(
            //                select a.""FloorId"",a.""FloorName"",a.""WardId"",a.""WardName"",
            //                jsonb_agg(jsonb_build_object('roomId ',a.""RoomId"",'roomName',a.""RoomName"",'roomRent',a.""RoomRent"",'beds',a.""Beds""))""Room""
            //                from (
            //                Select F.""FloorId"",F.""FloorName"",w.""WardId"",w.""WardName"",
            //                R.""RoomId"",R.""RoomName"",R.""RoomRent"",
            //                jsonb_agg(jsonb_build_object('bedId', B.""BedId"", 'bedNumber', B.""BedNumber"", 'bedStatusName', Bs.""BedStatusName"")) ""Beds""
            //                from ""Floor"" F
            //                left join ""Ward"" w on w.""FloorId""=F.""FloorId""
            //                left join ""Room"" R on R.""WardId""=w.""WardId""
            //                left join ""Bed"" B on R.""RoomId""=B.""RoomId""
            //                left join ""BedStatus"" Bs on Bs.""BedStatusId""=B.""BedStatusId""
            //                {where}
            //                group by F.""FloorId"",F.""FloorName"",w.""WardId"",w.""WardName"",R.""RoomId"",R.""RoomName"",R.""RoomRent"") a 
            //                group by a.""FloorId"",a.""FloorName"",a.""WardId"",a.""WardName"")a 
            //                group by a.""FloorId"",a.""FloorName"") a";

            var query = $@"with cts as (									  
                            select  A.""PatientId"",P.""Gender"",A.""PatientPriorityId"",A.""AdmissionId"",A.""AdmissionNo"",A.""ExpectedDischargeDate"",A.""BedId"",paacc.""FullName"" as ""PatientName"",
Pr.""FullName"" as ""ProviderName"",A.""AdmissionDate"",P.""Age"" ,D.""DepartmentName"" from ""Admission"" A
                            join ""Patient"" P on P.""PatientId""=A.""PatientId""	
 join ""Provider"" Pr on A.""ProviderId""=Pr.""ProviderId""

    join ""Department"" D on D.""DepartmentId"" = A.""DepartmentId""

                             join ""Account"" paacc on paacc.""ReferenceId"" = A.""PatientId"" and paacc.""RoleId"" = 4
	 						join ""Account"" pracc on pracc.""ReferenceId"" = A.""ProviderId"" and pracc.""RoleId"" = 3
                            join ""Bed"" B on A.""BedId"" = B.""BedId""								  
                            where  A.""AdmissionId"" not in (select ""AdmissionId"" from ""Discharge"") and A.""Active"" = TRUE
                            )
                            select jsonb_agg( jsonb_build_object('floorId',a.""FloorId"",'floorName',a.""FloorName"",'wards',
                            a.""Ward"" )) ::text
            	            from(
                                   select a.""FloorId"",a.""FloorName"",
                                   jsonb_agg(
            			case when a.""WardId"" is not null then 
            			jsonb_build_object('wardId',a.""WardId"",'wardName',a.""WardName"",'rooms',a.""Room"")
            			else jsonb_build_object('value','Empty') end )""Ward""
                                   from(select a.""FloorId"",a.""FloorName"",a.""WardId"",a.""WardName"",
                                   jsonb_agg(
            			case when a.""RoomId"" is not null then 
            			jsonb_build_object('roomId',a.""RoomId"",'roomName',a.""RoomName"",'roomRent',a.""RoomRent"",'beds',
            		    a.""Beds"" ) else jsonb_build_object('value','Empty') end )""Room""
                                   from (
                                   Select F.""FloorId"",F.""FloorName"",w.""WardId"",w.""WardName"",
                                   R.""RoomId"",R.""RoomName"",R.""RoomRent"",
            			jsonb_agg(
            				case when B.""BedId"" is not null then 
            				jsonb_build_object('bedId', B.""BedId"", 'bedNumber', B.""BedNumber"", 'bedStatusName', Bs.""BedStatusName"",'age',C.""Age"",'admissionDate',c.""AdmissionDate"",
											  'departmentName',""DepartmentName"",'patientId ', c.""PatientId"",'gender',c.""Gender"",'admissionId',c.""AdmissionId"",'PatientPriorityId',c.""PatientPriorityId"",
											  'admissionNo',c.""AdmissionNo"",'patientName',c.""PatientName"",'providerName',""ProviderName"",'expectedDischargeDate',c.""ExpectedDischargeDate"")
            				else jsonb_build_object('value','Empty') end 
            			)	
            			""Beds""
                                   from ""Floor"" F
                                   left join ""Ward"" w on w.""FloorId"" = F.""FloorId""
                                   left join ""Room"" R on R.""WardId"" = w.""WardId""
                                   left join ""Bed"" B on R.""RoomId"" = B.""RoomId""
                                   left join ""BedStatus"" Bs on Bs.""BedStatusId"" = B.""BedStatusId""
									left join cts c on c.""BedId"" = B.""BedId""
                                   {where}
                                   group by F.""FloorId"",F.""FloorName"",w.""WardId"",w.""WardName"",R.""RoomId"",R.""RoomName"",R.""RoomRent"") a 
                                   group by a.""FloorId"",a.""FloorName"",a.""WardId"",a.""WardName"")a 
                                   group by a.""FloorId"",a.""FloorName"") a";
            return await this.unitOfWork.Current.ExecuteScalarAsync<string>(query);
        }
        public async Task<BedPatientDetailsModel> FindPatientDetailsByBedIdAsync(int bedId)
        {
            var query = $@"Select dc.""DischargeId"",pacc.""FullName"" as ""PatientName"",pracc.""FullName"" as ""ProviderName"",p.""Age"",p.""Gender"",ad.""AdmissionNo"",ad.""AdmissionDate""::text,ad.""ExpectedDischargeDate""::text,d.""DepartmentName"" from ""Admission"" ad 
                            join ""Account"" pacc on pacc.""ReferenceId"" = ad.""PatientId"" and pacc.""RoleId"" = 4 and  pacc.""Active"" = true
                            join ""Patient"" p on p.""PatientId"" = ad.""PatientId"" and p.""Active"" = true
                            join ""Account"" pracc on pracc.""ReferenceId"" = ad.""ProviderId"" and pracc.""RoleId"" = 3 and  pracc.""Active"" = true
                            left join ""Department"" d on d.""DepartmentId"" = ad.""DepartmentId"" and ad.""Active"" = true
                            left join ""Discharge"" dc on dc.""AdmissionId"" = ad.""AdmissionId""
                            where ad.""BedId"" = {bedId} and ad.""AdmissionId"" not in (select ""AdmissionId"" from ""Discharge"")";
            var records = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<BedPatientDetailsModel>(query);
            return records;
        }

        public async Task<int> FetchAvailableBedsCountAsync(int locationId)
        {
            var query = $@"Select Count(b.""BedId"") from ""Bed"" b 
                            join ""BedStatus"" bs on bs.""BedStatusId"" = b.""BedStatusId"" 
                            JOIN ""Room"" r on r.""RoomId""=b.""RoomId""
						    JOIN ""Ward"" w on w.""WardId""=r.""WardId""
						    JOIN ""Floor"" f on f.""FloorId""=w.""FloorId""
						    where bs.""BedStatusId"" = 1 and f.""LocationId""={locationId}";
            var records = await this.unitOfWork.Current.ExecuteScalarAsync<int>(query);
            return records;
        }

        public async Task<int> FetchBookedBedsCountAsync(int locationId)
        {
            var query = $@"Select Count(b.""BedId"") from ""Bed"" b 
                            join ""BedStatus"" bs on bs.""BedStatusId"" = b.""BedStatusId""
                            JOIN ""Room"" r on r.""RoomId""=b.""RoomId""
						    JOIN ""Ward"" w on w.""WardId""=r.""WardId""
						    JOIN ""Floor"" f on f.""FloorId""=w.""FloorId""
                            where bs.""BedStatusId"" = 2  and f.""LocationId"" ={locationId} ";
            var records = await this.unitOfWork.Current.ExecuteScalarAsync<int>(query);
            return records;
        }

        public async Task<int> FetchUnderCleaningBedsCountAsync(int locationId)
        {
            var query = $@"Select Count(b.""BedId"") from ""Bed"" b 
                            join ""BedStatus"" bs on bs.""BedStatusId"" = b.""BedStatusId"" 
                            JOIN ""Room"" r on r.""RoomId""=b.""RoomId""
						    JOIN ""Ward"" w on w.""WardId""=r.""WardId""
						    JOIN ""Floor"" f on f.""FloorId""=w.""FloorId""
                            where bs.""BedStatusId"" = 4 and f.""LocationId"" ={locationId} ";
            var records = await this.unitOfWork.Current.ExecuteScalarAsync<int>(query);
            return records;
        }

        public async Task<int> FetchTotalBedsCountAsync(int locationId)
        {
            var query = $@"Select Count(b.""BedId"") from ""Bed"" b
                            JOIN ""Room"" r on r.""RoomId""=b.""RoomId""
						    JOIN ""Ward"" w on w.""WardId""=r.""WardId""
						    JOIN ""Floor"" f on f.""FloorId""=w.""FloorId""
						    where b .""Active"" = true and f.""LocationId""={locationId}";

            var records = await this.unitOfWork.Current.ExecuteScalarAsync<int>(query);
            return records;
        }
        public async Task<IEnumerable<Status.ViewModel>> FetchBedStatusItemsAsync(int locationId)
        {
            var query = $@"Select * from ""BedStatus""";
            var records = await this.unitOfWork.Current.QueryAsync<Status.ViewModel>(query);
            return records;
        }
    }
}
