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;

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

        /// <inheritdoc cref="IWardService" />
        public FloorService(IUnitOfWork unitOfWork) => this.unitOfWork = unitOfWork;

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

            if (model.LocationId > 0)
            {
                where += $@" AND F.""LocationId""  = {model.LocationId}";
            }
            if (model.FloorId > 0)
            {
                where += $@" AND F.""FloorId""  = {model.FloorId}";
            }
            var query = $@"SELECT COUNT(*) OVER () AS ""TotalItems"",F.""FloorId"", F.""Active"",F.""FloorName"",CA.""FullName"" AS ""CreatedByName"" , MA.""FullName"" AS ""ModifiedByName"" , F.""CreatedDate"",F.""ModifiedDate""
                         , string_agg(L.""Name"", ', ') as ""LocationNames"",string_agg(L.""LocationId""::text, ', ') as ""LocationIds"",L.""LocationId""
                        FROM ""Floor"" F
                        left Join ""Account"" CA ON CA.""AccountId"" = F.""CreatedBy""
                       Left Join ""Account"" MA ON MA.""AccountId"" = F.""ModifiedBy""     
                        join ""Location"" L ON L.""LocationId"" = F.""LocationId""
                        {where}
                        group by F.""FloorId"",CA.""FullName"", MA.""FullName"",L.""LocationId""
                        Order by ""FloorId"" DESC";

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

            model.PageIndex -= 1;
            //query += " LIMIT " + model.PageSize + " offset " + (model.PageIndex * model.PageSize);
            query += $@" limit {model.PageSize} offset {model.PageSize * model.PageIndex}";

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

        /// <inheritdoc />
        public Task<IEnumerable<Resource>> FetchFloorAsync()
        {
            var query = $@"SELECT  COUNT(*) OVER () AS ""TotalItems"",
                       F.""FloorId"" AS ""Id"",F.""FloorName"" AS ""Value"" , L.""Name"" AS ""OptionalText""
                            FROM ""Floor"" F
                            join ""Location"" L on F.""LocationId""=L.""LocationId""
                           where F.""Active"" = true
                        Order by ""FloorId"" DESC";
            return this.unitOfWork.Current.QueryAsync<Resource>(query);

        }

        /// <inheritdoc />
        public async Task<int> AddAsync(FloorModel model)
        {
            var checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(""FloorId"") FROM ""Floor"" WHERE TRIM(UPPER(""FloorName"")) = '{model.FloorName.ToUpper().Trim()}' and ""LocationId"" ={model.LocationId}");
            if (checkIf > 0)
            {
                return -1;
            }
            var floor = new Floor
            {
                Active = true,
                FloorName = model.FloorName,
                CreatedBy = model.CreatedBy,
                CreatedDate = DateTime.Now,
                LocationId = model.LocationId
            };
            var floorId = await this.unitOfWork.Floors.InsertAsync(floor);
            return floorId;
        }

        /// <inheritdoc />
        public async Task<int> UpdateAsync(FloorModel model)
        {
            var checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(""FloorId"") FROM ""Floor"" WHERE TRIM(UPPER(""FloorName"")) = '{model.FloorName.ToUpper().Trim()}' AND ""FloorId"" <> {model.FloorId} and ""LocationId"" ={model.LocationId}");
            
            
            if (checkIf > 0)
            {
                return -1;
            }
            var floor = await this.unitOfWork.Floors.FindAsync(m => m.FloorId == model.FloorId);
            floor.FloorName = model.FloorName;
            floor.Active = model.Active;
            floor.ModifiedBy = model.ModifiedBy;
            floor.ModifiedDate = DateTime.Now;
            floor.LocationId = Convert.ToInt32(model.LocationId);
            return await this.unitOfWork.Floors.UpdateAsync(floor);
        }

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

        public async Task<string> FindNameByFloorId(int floorId)
        {
            var query = $@"SELECT ""FloorName"" FROM ""Floor"" WHERE ""FloorId"" = {floorId}";
            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(FloorModel model)
        {
            var query = $@"select count(""BedId"") from ""Admission"" where ""IsDischarged"" is false and ""Active"" is true and ""BedId"" in (select b.""BedId"" from ""Bed"" b join ""Room"" r on r.""RoomId"" = b.""RoomId"" join ""Ward"" w on w.""WardId"" = r.""WardId"" where w.""FloorId"" = {model.FloorId}) ";
            var response = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<int>(query);
            if (response > 0)
            {
                return -2;
            }
            var floor = await this.unitOfWork.Floors.FindAsync(m => m.FloorId == model.FloorId);
            if (floor == null)
            {
                return -1;
            }
            floor.Active = model.Active;
            floor.ModifiedBy = model.ModifiedBy;
            floor.ModifiedDate = DateTime.Now;
            return await this.unitOfWork.Floors.UpdateAsync(floor);
        }
    }
}