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 Shared.EntityModels;
    using Shared.UserModels.Filters;

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

        /// <inheritdoc cref="IBedService" />
        public BedService(IUnitOfWork unitOfWork) => this.unitOfWork = unitOfWork;

        /// <inheritdoc />
        public Task<IEnumerable<BedModel>> FetchAsync(BedFilterModel model)
        {
            var where = " WHERE 1 = 1 ";
            if (!string.IsNullOrEmpty(model.BedNumber))
            {
                where += $@" AND TRIM(UPPER(""BedNumber"")) = '{model.BedNumber.Trim().ToUpper()}'";
            }
            if (model.RoomId > 0)
            {
                where += $@" AND r.""RoomId"" = {model.RoomId}";
            }
            if (model.FloorId > 0)
            {
                where += $@" AND f.""FloorId"" = {model.FloorId}";
            }
            if (model.WardId > 0)
            {
                where += $@" AND w.""WardId"" = {model.WardId}";
            }
            if (model.BedId > 0)
            {
                where += $@" AND b.""BedId"" = {model.BedId}";
            }
            if (model.BedStatusId > 0)
            {
                where += $@" AND bs.""BedStatusId"" = {model.BedStatusId}";
            }
            if (model.LocationId > 0)
            {
                where += $@" AND F.""LocationId"" = {model.LocationId}";
            }


            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.BedNumber))
            {
                where += $@" and b.""BedNumber"" ilike '%{model.BedNumber}%' ";
            }

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


            var query = $@"SELECT COUNT(*) OVER() AS ""TotalItems"" ,b.""BedId"", bs.""BedStatusName"",b.""BedType"",
                                b.""BedNumber"",b.""Active"",CA.""FullName"" AS ""CreatedByName"" , MA.""FullName"" AS ""ModifiedByName"" , b.""CreatedDate"",b.""ModifiedDate"",
                            w.""WardName"",  w.""WardId"",
                            r.""RoomName"",r.""RoomId"" ,f.""FloorName"" , f.""FloorId"",L.""Name"" as ""LocationName""
                        from ""Ward"" w             
                        join ""Room"" r on r.""WardId"" = w.""WardId""
                        join ""Bed"" b on b.""RoomId"" = r.""RoomId"" 
                        join ""Floor"" f on f.""FloorId"" = w.""FloorId""
                        Join ""Location"" L on L.""LocationId""=f.""LocationId""
                        left Join ""Account"" CA ON CA.""AccountId"" = b.""CreatedBy""
                       Left Join ""Account"" MA ON MA.""AccountId"" = b.""ModifiedBy""
                        join ""BedStatus"" bs on bs.""BedStatusId"" = b.""BedStatusId""{where} Order by ""BedId"" DESC";


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


        /// <inheritdoc />
        public async Task<int> AddAsync(BedModel model)
        {
            var checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(""BedId"") FROM ""Bed"" WHERE ""RoomId"" = {model.RoomId} AND TRIM(UPPER(""BedNumber"")) = '{model.BedNumber.ToUpper().Trim()}'");
            if (checkIf > 0)
            {
                return -1;
            }
            var Query1 = $@"with MaxAllow as(select ""BedsCount"", ""RoomId"" from ""Room""  where ""RoomId"" = {model.RoomId} )
                          select  ma.""BedsCount"" - count(b.""BedId"") as ""SUbb""   from ""Bed"" b
                          join MaxAllow ma on ma.""RoomId"" = b.""RoomId""
                         where b.""RoomId"" = {model.RoomId} group by ma.""BedsCount""";

            var query = $@"select ""BedsCount"" - (select count(""BedId"") as ""Total"" from ""Bed"" where ""RoomId"" = {model.RoomId}) from ""Room""  where ""RoomId"" = {model.RoomId}";
            var CheckRoomCount = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>(query);
            if (CheckRoomCount <= 0)
            {
                return -2;
            }

            var bed = new Bed
            {
                Active = true,
                BedNumber = model.BedNumber,
                BedStatusId = model.BedStatusId,
                RoomId = model.RoomId,
                BedType = model.BedType,
                CreatedBy = model.CreatedBy,
                CreatedDate = DateTime.Now,
            };

            return await this.unitOfWork.Beds.InsertAsync(bed);
        }


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

            var bed = await this.unitOfWork.Beds.FindAsync(m => m.BedId == model.BedId);
            bed.BedNumber = model.BedNumber;
            bed.Active = model.Active;
            bed.BedStatusId = bed.BedStatusId;
            bed.RoomId = model.RoomId;
            bed.BedType = model.BedType;
            bed.ModifiedBy = model.ModifiedBy;
            bed.ModifiedDate = DateTime.Now;
            return await this.unitOfWork.Beds.UpdateAsync(bed);
        }

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

        /// <inheritdoc />
        public Task<IEnumerable<BedModel>> FetchBedAsync(BedFilterModel model)
        {
            var where = " WHERE 1 = 1 ";
            if (!string.IsNullOrEmpty(model.RoomName))
            {
                where += $@" AND TRIM(UPPER(""RoomName"")) = '{model.RoomName.Trim().ToUpper()}'";
            }


            if (model.BedId > 0)
            {
                where += $@" AND ""BedId"" = {model.BedId}";
            }

            var query = $@" 
                       SELECT COUNT(*) OVER () AS ""TotalItems"" ,* FROM ""Room"" r join ""Ward"" w on w.""WardId"" = r.""WardId"" join ""Bed"" b on b.""RoomId"" = r.""RoomId""
                       {where} Order by b.""BedId"" DESC";

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

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

        public async Task<string> FindNumberByBedId(int bedId)
        {
            var query = $@"SELECT ""BedNumber"" FROM ""Bed"" WHERE ""BedId"" = {bedId}";
            var response = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<string>(query);
            return response;
        }

        /// <summary>
        /// Changes the status asynchronous.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <returns></returns>
        public async Task<int> ChangeStatusAsync(BedModel model)
        {
            var query = $@"select Count(""BedId"") from ""Admission"" where ""IsDischarged"" is false and ""Active"" is true and ""BedId"" = {model.BedId} ";
            var response = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<int>(query);
            if (response > 0)
            {
                return -2;
            }
            var bed = await this.unitOfWork.Beds.FindAsync(m => m.BedId == model.BedId);
            if (bed == null)
            {
                return -1;
            }
            bed.Active = model.Active;
            bed.ModifiedBy = model.ModifiedBy;
            bed.ModifiedDate = DateTime.Now;
            return await this.unitOfWork.Beds.UpdateAsync(bed);
        }
    }
}