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

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

        /// <inheritdoc cref="IRoomService" />
        public RoomService(IUnitOfWork unitOfWork) => this.unitOfWork = unitOfWork;

        /// <inheritdoc />
        public Task<IEnumerable<RoomModel>> FetchAsync(RoomFilterModel model)
        {
            var where = " WHERE 1 = 1 ";
            if (model.WardId > 0)
            {
                where += $@" AND w.""WardId"" = {model.WardId}";
            }
            if (model.LocationId > 0)
            {
                where += $@" AND F.""LocationId"" = {model.LocationId}";
            }
            if (model.RoomId > 0)
            {
                where += $@" AND r.""RoomId"" = {model.RoomId}";
            }
            if (model.FloorId > 0)
            {
                where += $@" AND F.""FloorId"" = {model.FloorId}";
            }
            if (!string.IsNullOrEmpty(model.FloorName))
            {
                where += $@" and F.""FloorName"" ilike '%{model.FloorName}%' ";
            }
            if (!string.IsNullOrEmpty(model.WardName))
            {
                where += $@" and w.""WardName"" ilike '%{model.WardName}%' ";
            }
            if (!string.IsNullOrEmpty(model.RoomName))
            {
                where += $@" and r.""RoomName"" ilike '%{model.RoomName}%' ";
            }
            if (!string.IsNullOrEmpty(model.Term))
            {
                where += $@" and (lower(r.""RoomName"") ilike '%{model.Term.ToLower()}%' or lower(r.""RoomName"") ilike '%{model.Term.ToLower()}%')";
            }


            var query = $@"SELECT COUNT(*) OVER () AS ""TotalItems"",  r.""RoomId"" ,r.""Active"", r.""RoomName"",r.""RoomRent"",r.""BedsCount"",r.""ChargeCategoryId"",
                                        CA.""FullName"" AS ""CreatedByName"" , MA.""FullName"" AS ""ModifiedByName"" , r.""CreatedDate"",r.""ModifiedDate"",
                                        w.""WardId"",W.""WardName"" ,F.""FloorName"", F.""FloorId"",L.""Name"" as ""LocationName"",cc.""ChargeCategoryName""
                                        
                                        FROM ""Room"" r
                                         join ""ChargeCategory"" cc on cc.""ChargeCategoryId"" = r.""ChargeCategoryId""
                                         join ""Ward"" w on w.""WardId"" = r.""WardId""
                                         join ""Floor"" F on F.""FloorId"" =w.""FloorId"" 
                                            Join ""Location"" L on L.""LocationId"" =F.""LocationId""
                                         left Join ""Account"" CA ON CA.""AccountId"" = r.""CreatedBy""
                                        Left Join ""Account"" MA ON MA.""AccountId"" = r.""ModifiedBy""
                                        
                                        {where} Order by ""RoomId"" DESC";

            if (model.PageIndex <= 0)
            {
                return this.unitOfWork.Current.QueryAsync<RoomModel>(query);
            }

            model.PageIndex = model.PageIndex > 0 ? model.PageIndex - 1 : model.PageIndex;
            query += $@" limit {model.PageSize} offset {model.PageSize * model.PageIndex}";
            return this.unitOfWork.Current.QueryAsync<RoomModel>(query);
        }
        public async Task<string> FetchRoomNameAsync(int roomId)
        {
            var qry = $@"select ""RoomName"" from ""OTRoom"" where ""OTRoomId""={roomId}";
            var response = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<string>(qry);
            return response;
        }
       
            /// <inheritdoc />
            public Task<IEnumerable<Resource>> FetchRoomAsync(RoomFilterModel model)
        {
            var where = " WHERE 1 = 1 ";
            if (!string.IsNullOrEmpty(model.RoomName))
            {
                where += $@" AND TRIM(UPPER(""RoomName"")) = '{model.RoomName.Trim().ToUpper()}'";
            }

            if (model.WardId > 0)
            {
                where += $@" AND w.""WardId"" = {model.WardId}";
            }
            if (model.ChargeCategoryId > 0)
            {
                where += $@" AND R.""ChargeCategoryId"" = {model.ChargeCategoryId}";
            }
            var query = $@" 
                       SELECT COUNT(*) OVER () AS ""TotalItems"" ,
                        R.""RoomId"" AS ""Id"",R.""RoomName"" AS ""Value"" --,F.""FloorName"" AS ""OptionalText1"", L.""Name"" AS ""OptionalText""

                        FROM ""Room"" R 
                        join ""Ward"" W on W.""WardId"" = R.""WardId""
                       
                       {where} Order by R.""RoomId"" DESC";

            if (model.PageIndex <= 0)
            {
                return this.unitOfWork.Current.QueryAsync<Resource>(query);
            }

            model.PageIndex -= 1;
            query += " LIMIT " + model.PageSize + " offset " + (model.PageIndex * model.PageSize);
            return this.unitOfWork.Current.QueryAsync<Resource>(query);
        }

        /// <inheritdoc />
        public async Task<int> AddAsync(RoomModel model)
        {
            var roomModuleMaster = await this.unitOfWork.ModulesMasters.FindAsync(m => m.ModuleName == "Room");
            if (roomModuleMaster == null)
            {
                return -2;
            }
            var checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(""RoomId"") FROM ""Room"" WHERE ""WardId"" = {model.WardId} AND TRIM(UPPER(""RoomName"")) = '{model.RoomName.ToUpper().Trim()}'");
            if (checkIf > 0)
            {
                return -1;
            }


            var room = new Room
            {
                Active = true,
                RoomName = model.RoomName,
                RoomRent = model.RoomRent,
                WardId = model.WardId,
                BedsCount = model.BedsCount,
                CreatedBy = model.CreatedBy,
                CreatedDate = DateTime.Now,
                ChargeCategoryId = model.ChargeCategoryId,
                ModulesMasterId = roomModuleMaster.ModulesMasterId
            };

            return await this.unitOfWork.Rooms.InsertAsync(room);
        }

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



            try
            {
                var room = await this.unitOfWork.Rooms.FindAsync(m => m.RoomId == model.RoomId);
                room.RoomName = model.RoomName;
                room.Active = model.Active;       //true;
                room.RoomRent = model.RoomRent;
                room.BedsCount = model.BedsCount;
                room.WardId = model.WardId;
                room.ChargeCategoryId = model.ChargeCategoryId;
                room.ModifiedBy = model.ModifiedBy;
                room.ModifiedDate = DateTime.Now;
                return await this.unitOfWork.Rooms.UpdateAsync(room);
            }
            catch (Exception ex)
            {
                return -1;
            }

        }

        /// <inheritdoc />
        public async Task<int> DeleteAsync(int roomId)
        {
            var query1 = $@"select count(""BedId"") from ""Admission"" where ""IsDischarged"" is false and ""Active"" is true and ""BedId"" in (select ""BedId"" from ""Bed"" where ""RoomId"" = {roomId}) ";
            var response1 = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<int>(query1);
            if (response1 > 0)
            {
                return -2;
            }
            var query = $@"DELETE FROM ""Room"" WHERE ""RoomId""= {roomId}";
            return await this.unitOfWork.Current.ExecuteAsync(query);
        }

        /// <inheritdoc />
        public async Task<string> FindNameByRoomId(int roomId)
        {
            var query = $@"SELECT ""RoomName"" FROM ""Room"" WHERE ""RoomId"" = {roomId}";
            var response = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<string>(query);
            return response;
        }

        /// <inheritdoc />
        public async Task<int> InsertOTRoom(RoomModel model)
        {
            var checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(""OTRoomId"")  FROM ""OTRoom"" WHERE TRIM(UPPER(""RoomName"")) = '{model.RoomName.ToUpper().Trim()}'");

            if (checkIf > 0)
            {
                return -1;
            }
            var transaction = this.unitOfWork.BeginTransaction();
            var otRoom = new OTRoom
            {
                Active = true,
                CreatedBy = model.CreatedBy,
                CreatedDate = DateTime.Now,
                RoomName = model.RoomName,
                WardId = model.WardId,
                BedStatusId = 1
            };
            otRoom.OTRoomId = await this.unitOfWork.OTRooms.InsertAsync(otRoom, transaction);
            if (otRoom.OTRoomId == 0)
            {
                return -2;
            }
            if (!string.IsNullOrEmpty(model.PerformedTestId))
            {
                var testId = model.PerformedTestId.Split(',');
                var models = testId.Select(m => new OTRoomSurgeryMap { OTRoomId = otRoom.OTRoomId, LocationId = model.LocationId, SurgeryId = int.Parse(m) });
                var testResponse = await this.unitOfWork.OTRoomSurgeryMaps.BulkInsertAsync(models, transaction);
                if (testResponse == 0)
                {
                    transaction.Rollback();
                    return -2;
                }

            }
            transaction.Commit();
            return 1;

        }

        /// <inheritdoc />
        public async Task<int> UpdateOTRoom(RoomModel model)
        {

            var checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(""OTRoomId"")  FROM ""OTRoom"" WHERE TRIM(UPPER(""RoomName"")) = '{model.RoomName.ToUpper().Trim()}'  and ""OTRoomId"" <> {model.OTRoomId} ");
            var transaction = this.unitOfWork.BeginTransaction();
            if (checkIf > 0)
            {
                return -1;
            }
            var previous = await this.unitOfWork.OTRooms.FindAsync(m => m.OTRoomId == model.OTRoomId);
            previous.RoomName = model.RoomName;
            previous.WardId = model.WardId;
            previous.ModifiedBy = model.CreatedBy;
            previous.ModifiedDate = DateTime.Now;
            var updateResponse = await this.unitOfWork.OTRooms.UpdateAsync(previous, transaction);
            if (updateResponse == 0)
            {
                transaction.Rollback();
                return -1;
            }
            if (!string.IsNullOrEmpty(model.PerformedTestId))
            {
                var query = $@"DELETE FROM ""OTRoomSurgeryMap"" where ""OTRoomId"" = {model.OTRoomId}";
                await this.unitOfWork.Current.QueryAsync(query, transaction);
                var performedTests = model.PerformedTestId.Split(',');
                var models = performedTests.Select(m => new OTRoomSurgeryMap
                {
                    LocationId = model.LocationId,
                    OTRoomId = previous.OTRoomId,
                    SurgeryId = int.Parse(m)
                });
                var testResponse = await this.unitOfWork.OTRoomSurgeryMaps.BulkInsertAsync(models, transaction);
                if (testResponse == 0)
                {
                    transaction.Rollback();
                    return -4;
                }
            }
            transaction.Commit();
            return 1;
        }
        public async Task<IEnumerable<RoomModel>> FetchSurgeryOTRoom( RoomFilterModel model)
        {
            //var qry = $@"select  Distinct otr.""SurgeryId"" as ""Id"",S.""Name"" as ""SurgeryName""
            //from ""OTRoomSurgeryMap"" otr
            //left join ""Surgery"" S on S.""SurgeryId""=otr.""SurgeryId""  where otr.""LocationId""={LocationId}";
            //return await this.unitOfWork.Current.QueryAsync<RoomModel>(qry);
            var where = " WHERE 1 = 1 ";
            if (model.OTRoomId > 0)
            {
                where += $@" AND otr.""OTRoomId"" = {model.OTRoomId}";
            }

            if (model.LocationId > 0)
            {
                where += $@" AND otr.""LocationId"" = {model.LocationId}";
            }
            if (model.Active != null)
            {
                if ((bool)model.Active)
                {
                    where += $@" and ot.""Active"" is true";
                }
                else
                {
                    where += $@" and ot.""Active"" is false";
                }
            }
            if (model.SurgeryId > 0)
            {
                where += $@" AND otr.""SurgeryId"" = {model.SurgeryId}";
            }

            var qry = $@"select  Distinct otr.""SurgeryId"" as ""Id"",S.""Name"" as ""SurgeryName""
            from ""OTRoomSurgeryMap"" otr
            left join ""Surgery"" S on S.""SurgeryId""=otr.""SurgeryId""  
 left join ""OTRoom"" ot on ot.""OTRoomId""=otr.""OTRoomId""  
{where}";
            return await this.unitOfWork.Current.QueryAsync<RoomModel>(qry);

        }

        /// <inheritdoc />
        public async Task<IEnumerable<RoomModel>> FetchOTRoomAsync(RoomFilterModel model)
        {
            var where = " WHERE 1 = 1 ";
            if (model.OTRoomId > 0)
            {
                where += $@" AND OTR.""OTRoomId"" = {model.OTRoomId}";
            }
            if (model.WardId > 0)
            {
                where += $@" AND OTR.""WardId"" = {model.WardId}";
            }
            if (model.LocationId > 0)
            {
                where += $@" AND F.""LocationId"" = {model.LocationId}";
            }
            if (model.Active != null)
            {
                if ((bool)model.Active)
                {
                    where += $@" and OTR.""Active"" is true";
                }
                else
                {
                    where += $@" and OTR.""Active"" is false";
                }
            }
            if (model.SurgeryId > 0)
            {
                where += $@" AND osm.""SurgeryId"" = {model.SurgeryId}";
            }

            var query = $@" SELECT  Distinct W.""WardName"", OTR.""OTRoomId"", OTR.""WardId"", OTR.""RoomName"", OTR.""Active"", OTR.""BedStatusId"", OTR.""CreatedBy"", OTR.""CreatedDate"",
string_agg(osm.""SurgeryId""::text, ', ') as ""SurgeryId"",
                                string_agg(S.""Name"", ',') as ""SurgeryName"",
		                            OTR.""ModifiedBy"", OTR.""ModifiedDate"",W.""FloorId"",F.""FloorName"",BD.""BedStatusName"",
		                            CS.""FullName"" as ""CreatedByName"",CSR.""RoleName"" as ""CreatedByRole"",MS.""FullName"" as ""ModifiedByName"",
		                            MSR.""RoleName"" as ""ModifiedByRole""
                                FROM ""OTRoom"" OTR
                                join ""Ward"" W on W.""WardId"" = OTR.""WardId""

                                join ""Floor"" F on F.""FloorId"" = W.""FloorId""

                                join ""BedStatus"" BD on BD.""BedStatusId"" = OTR.""BedStatusId""

                                join ""Account"" CS on CS.""AccountId"" = OTR.""CreatedBy""

                                join ""Role"" CSR on CSR.""RoleId"" = CS.""RoleId""

                                left join ""Account"" MS on MS.""AccountId"" = OTR.""ModifiedBy""

                                left join ""Role"" MSR on MSR.""RoleId"" = MS.""RoleId""
                                 left join  ""OTRoomSurgeryMap"" osm on osm.""OTRoomId"" = OTR.""OTRoomId""
                                left join  ""Surgery"" S on S.""SurgeryId"" = osm.""SurgeryId""
                                   {where}
            group by  OTR.""OTRoomId"", OTR.""WardId"", OTR.""RoomName"", OTR.""Active"", OTR.""BedStatusId"", OTR.""CreatedBy"", 
								OTR.""CreatedDate"",
		                            OTR.""ModifiedBy"", OTR.""ModifiedDate"",W.""WardName"",W.""FloorId"",F.""FloorName"",BD.""BedStatusName"",
		                            CS.""FullName"" ,CSR.""RoleName"",MS.""FullName"",
		                            MSR.""RoleName"" 
                             order by OTR.""CreatedDate"" desc";
            return await this.unitOfWork.Current.QueryAsync<RoomModel>(query);
        }


        public async Task<int> ActivateOrDeactivateTest(RoomModel model)
        {
            var checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(""OTRoomId"")  FROM ""OTRegister"" WHERE  ""OTRoomId"" = {model.OTRoomId} ");
            if (checkIf > 0)
            {
                return -1;
            }
            var query = $@"UPDATE ""OTRoom""
	                           SET ""ModifiedBy""={model.CreatedBy}, ""ModifiedDate""=now(), ""Active""= {model.Active}
	                           WHERE ""OTRoomId""= {model.OTRoomId}";
            return await this.unitOfWork.Current.ExecuteAsync(query);
        }

        public async Task<int> ChangeRoomStatusAsync(RoomModel model)
        {
            var query = $@"select count(""BedId"") from ""Admission"" where ""IsDischarged"" is false and ""Active"" is true and ""BedId"" in (select ""BedId"" from ""Bed"" where ""RoomId"" = {model.RoomId}) ";
            var response = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<int>(query);
            if (response > 0)
            {
                return -2;
            }
            var room = await this.unitOfWork.Rooms.FindAsync(m => m.RoomId == model.RoomId);
            if (room == null)
            {
                return -1;
            }
            room.Active = model.Active;
            room.ModifiedBy = model.ModifiedBy;
            room.ModifiedDate = DateTime.Now;
            return await this.unitOfWork.Rooms.UpdateAsync(room);
        }
    }
}