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

    using Shared.UserModels;
    using Hims.Shared.Library.Enums;
    using Hims.Shared.UserModels.Inventory;
    using Hims.Shared.UserModels.Pharmacy;


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

        /// <summary>
        /// Initializes a new instance of the <see cref="WebTelemedicineServices"/> class.
        /// </summary>
        /// <param name="unitOfWork">
        /// The unit of work.
        /// </param>
        public InventoryStoreServices(IUnitOfWork unitOfWork) => this.unitOfWork = unitOfWork;

        /// <inheritdoc />
        public async Task<int> InsertInventoryWareHouse(InventoryWareHouseModel model)
        {
            var where = " and 1=1 ";
            if (model.LocationId != null)
            {
                where += $@" and ""LocationId""={model.LocationId}";
            }
            var checkIf = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<int>($@"Select count(*) from ""InventoryWareHouse"" where lower(""Name"") = lower('{model.Name}') {where}");

            if (checkIf != 0)
            {
                return -2;
            }

            var transaction = this.unitOfWork.BeginTransaction();
            var warehouse = new InventoryWareHouse
            {
                CreatedBy = model.CreatedBy,
                CreatedDate = DateTime.Now,
                Name = model.Name,
                Active = true,
                LocationId = model.LocationId
            };

            warehouse.InventoryWareHouseId = await this.unitOfWork.InventoryWareHouses.InsertAsync(warehouse);
            if (warehouse.InventoryWareHouseId == 0)
            {
                transaction.Rollback();
                return -1;
            }

            if (!string.IsNullOrEmpty(model.AllowedAccountId))
            {
                var allowedIds = model.AllowedAccountId.Split(",");
                var models = allowedIds.Select(m => new InventoryWareHouseUser { AccountId = int.Parse(m), InventoryWareHouseId = warehouse.InventoryWareHouseId });
                var userResponse = await this.unitOfWork.InventoryWareHouseUsers.BulkInsertAsync(models, transaction);
                if (userResponse == 0)
                {
                    transaction.Rollback();
                    return -1;
                }
            }

            transaction.Commit();
            return warehouse.InventoryWareHouseId;
        }

        /// <inheritdoc />
        public async Task<int> UpdateInventoryWareHouse(InventoryWareHouseModel model)
        {
            var where = " and 1=1 ";
            if (model.LocationId != null)
            {
                where += $@" and ""LocationId""={model.LocationId}";
            }

            var checkIf = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<int>($@"Select count(*) from ""InventoryWareHouse"" where lower(""Name"") = lower('{model.Name}') and ""InventoryWareHouseId"" <> {model.InventoryWareHouseId} {where}");

            if (checkIf != 0)
            {
                return -2;
            }

            var warehouse = await this.unitOfWork.InventoryWareHouses.FindAsync(w => w.InventoryWareHouseId == model.InventoryWareHouseId);
            if (warehouse == null)
            {
                return -3;
            }

            var transaction = this.unitOfWork.BeginTransaction();

            warehouse.ModifiedBy = model.CreatedBy;
            warehouse.ModifiedDate = DateTime.Now;
            warehouse.Name = model.Name;
            warehouse.LocationId = model.LocationId != null ? model.LocationId : (int?)null;

            var updateReponse = await this.unitOfWork.InventoryWareHouses.UpdateAsync(warehouse, transaction);

            if (updateReponse == 0)
            {
                transaction.Rollback();
                return -1;
            }

            if (!string.IsNullOrEmpty(model.AllowedAccountId))
            {
                var deleteQuery = $@"DELETE FROM ""InventoryWareHouseUser""	WHERE ""InventoryWareHouseId"" = {model.InventoryWareHouseId}";
                await this.unitOfWork.Current.ExecuteAsync(deleteQuery, transaction);


                var allowedIds = model.AllowedAccountId.Split(",");
                var models = allowedIds.Select(m => new InventoryWareHouseUser { AccountId = int.Parse(m), InventoryWareHouseId = warehouse.InventoryWareHouseId });
                var userResponse = await this.unitOfWork.InventoryWareHouseUsers.BulkInsertAsync(models, transaction);
                if (userResponse == 0)
                {
                    transaction.Rollback();
                    return -1;
                }
            }

            transaction.Commit();
            return warehouse.InventoryWareHouseId;
        }

        /// <inheritdoc />
        public async Task<IEnumerable<InventoryWareHouseModel>> FetchAllWarehousesAsync(InventoryWareHouseModel model)
        {
            var where = "where 1=1";

            if (model.LocationId != null)
            {
                where += $@" and PWH.""LocationId""={model.LocationId} or PWH.""LocationId"" is null"; // Changing for central pharmacy.
            }

            var query = $@"SELECT count(PWH.*) over() as ""TotalItems"", PWH.""InventoryWareHouseId"", PWH.""Name"", PWH.""CreatedBy"", PWH.""CreatedDate"", PWH.""ModifiedBy"", 
		                        PWH.""ModifiedDate"",PWH.""Active"" , CA.""FullName"" as ""CreatedByName"",
		                        CM.""FullName"" as ""ModifiedByName"",L.""Name"" as ""LocationName"",PWH.""LocationId""
	                        FROM ""InventoryWareHouse"" PWH
	                        join ""Account"" CA on CA.""AccountId"" = PWH.""CreatedBy""
	                        left join ""Location"" L on L.""LocationId"" = PWH.""LocationId""
	                        left join ""Account"" CM on CM.""AccountId"" = PWH.""ModifiedBy""
                            {where}                           
                            order by PWH.""CreatedDate"" desc";
            if (model.PageIndex != null && model.PageSize != null)
            {
                model.PageIndex = model.PageIndex > 0 ? model.PageIndex - 1 : model.PageIndex;
                query += $@" limit {model.PageSize} offset {model.PageIndex * model.PageSize}";
            }
            var response = await this.unitOfWork.Current.QueryAsync<InventoryWareHouseModel>(query);
            if (response == null)
            {
                return response;
            }

            foreach (var data in response)
            {
                data.Users = new List<InventoryWareHouseUserModel>();

                var queryUser = $@"SELECT PW.""InventoryWareHouseUserId"", PW.""InventoryWareHouseId"", PW.""AccountId"",A.""FullName""
	                                    FROM ""InventoryWareHouseUser"" PW
	                                    join ""Account"" A on A.""AccountId"" = PW.""AccountId""
	                                    where PW.""InventoryWareHouseId"" = {data.InventoryWareHouseId}";
                data.Users = (await this.unitOfWork.Current.QueryAsync<InventoryWareHouseUserModel>(queryUser)).ToList();
            }

            return response;
        }

        /// <inheritdoc />
        public async Task<int> ActivateOrDeactivateWareHouse(InventoryWareHouseModel model)
        {
            var query = $@"UPDATE ""InventoryWareHouse""
                             SET ""ModifiedBy""={model.CreatedBy}, ""ModifiedDate""=now()::timestamp, ""Active""= {model.Active}
                             WHERE ""InventoryWareHouseId""= {model.InventoryWareHouseId}";
            return await this.unitOfWork.Current.ExecuteAsync(query);
        }

        /// <inheritdoc />
        public async Task<int> ModifyInventoryDepartment(PharmacyDepartmentModel model)
        {
            var checkIfQuery = $@"Select count(*) from ""InventoryDepartment"" where lower(""Name"") = '{model.Name.ToLower()}' and ""LocationId"" = {model.LocationId}";

            var transaction = this.unitOfWork.BeginTransaction();

            int returnFunction()
            {
                transaction.Rollback();
                return -2;
            }

            var inventoryDepartment = new InventoryDepartment
            {
                Active = true,
                CreatedBy = model.CreatedBy,
                CreatedDate = DateTime.Now,
                LocationId = (int)model.LocationId,
                Name = model.Name
            };

            if (model.InventoryDepartmentId == 0)
            {
                var checkIf = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<int>(checkIfQuery);
                if (checkIf > 0)
                {
                    transaction.Rollback();
                    return -1;
                }

                inventoryDepartment.InventoryDepartmentId = await this.unitOfWork.InventoryDepartments.InsertAsync(inventoryDepartment, transaction);
                if (inventoryDepartment.InventoryDepartmentId == 0)
                {
                    returnFunction();
                }
            }
            else
            {
                checkIfQuery += $@"  and ""InventoryDepartmentId"" <> {model.InventoryDepartmentId}";
                var checkIf = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<int>(checkIfQuery);
                if (checkIf > 0)
                {
                    transaction.Rollback();
                    return -1;
                }

                var previousRecord = await this.unitOfWork.InventoryDepartments.FindAsync(x => x.InventoryDepartmentId == model.InventoryDepartmentId);
                if (previousRecord == null)
                {
                    returnFunction();
                }

#pragma warning disable S2259 // Null pointers should not be dereferenced
                previousRecord.Name = inventoryDepartment.Name;
#pragma warning restore S2259 // Null pointers should not be dereferenced
                previousRecord.ModifiedBy = model.CreatedBy;
                previousRecord.ModifiedDate = inventoryDepartment.CreatedDate;
                inventoryDepartment.InventoryDepartmentId = previousRecord.InventoryDepartmentId;
                var updateDepartment = await this.unitOfWork.InventoryDepartments.UpdateAsync(previousRecord, transaction);
                if (updateDepartment == 0)
                {
                    returnFunction();
                }

                var userDeleteQuery = $@"Delete from ""InventoryDepartmentUser"" where ""InventoryDepartmentId"" = {inventoryDepartment.InventoryDepartmentId}";
                var userDeleteResponse = await this.unitOfWork.Current.ExecuteAsync(userDeleteQuery, transaction);
                if (userDeleteResponse == 0)
                {
                    returnFunction();
                }

            }

            var departmentUsers = model.UserIds.Split(",").Select(id => new InventoryDepartmentUser
            {
                AccountId = int.Parse(id),
                InventoryDepartmentId = inventoryDepartment.InventoryDepartmentId
            });

            var departmentUserResponse = await this.unitOfWork.InventoryDepartmentUsers.BulkInsertAsync(departmentUsers, transaction);
            if (departmentUserResponse == 0)
            {
                returnFunction();
            }

            transaction.Commit();
            return inventoryDepartment.InventoryDepartmentId;
        }

        /// <inheritdoc />
        public async Task<IEnumerable<PharmacyDepartmentModel>> FetchInventoryDepartmentAsync(PharmacyDepartmentModel model)
        {
            var where = "where 1=1";

            if (model.LocationId != null)
            {
                where += $@"  and PD.""LocationId"" = {model.LocationId}";
            }

            if (model.Active != null)
            {
                if ((bool)model.Active)
                {
                    where += $@" and PD.""Active"" is true";
                }
                else
                {
                    where += $@" and PD.""Active"" is false";
                }
            }

            if (model.AccountBasedAccessId != null)
            {
                var getId = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<string>($@"Select string_agg(""InventoryDepartmentId""::text,',')  from ""InventoryDepartmentUser"" where ""AccountId"" = {model.AccountBasedAccessId}");
                if (!string.IsNullOrEmpty(getId))
                {
                    where += $@" and PD.""InventoryDepartmentId"" in ({getId})";
                }
            }

            var query = $@"SELECT PD.""InventoryDepartmentId"", PD.""Name"", PD.""Active"", PD.""CreatedBy"", PD.""CreatedDate"", PD.""ModifiedBy"", 
                                   PD.""ModifiedDate"", PD.""LocationId"", CR.""FullName"" as ""CreatedByName"", MR.""FullName"" as ""ModifiedByName""
	                            FROM ""InventoryDepartment"" PD
	                            join ""Account"" CR on CR.""AccountId"" = PD.""CreatedBy""
	                            left join ""Account"" MR on MR.""AccountId"" = PD.""ModifiedBy""
                                {where}
	                            order by PD.""CreatedDate"" desc";
            var inventoryDepartments = await this.unitOfWork.Current.QueryAsync<PharmacyDepartmentModel>(query);
            foreach (var item in inventoryDepartments)
            {
                item.Users = new List<PharmacyDepartmentUserModel>();
                var userQuery = $@"SELECT PDU.""InventoryDepartmentUserId"", PDU.""InventoryDepartmentId"", PDU.""AccountId"",
		                                    A.""FullName"", R.""RoleName""
	                                    FROM ""InventoryDepartmentUser"" PDU
	                                    join ""Account"" A on A.""AccountId"" = PDU.""AccountId""
	                                    join ""Role"" R on R.""RoleId""= A.""RoleId""
	                                    where  PDU.""InventoryDepartmentId"" = {item.InventoryDepartmentId}
	                                    order by PDU.""InventoryDepartmentUserId"" desc";
                item.Users = (await this.unitOfWork.Current.QueryAsync<PharmacyDepartmentUserModel>(userQuery)).ToList();
            }

            return inventoryDepartments;
        }

        /// <inheritdoc />
        public async Task<IEnumerable<PharmacyDepartmentModel>> FetchInventoryDepartmentBasedOnUserAsync(PharmacyDepartmentModel model)
        {
            var where = "where 1=1";

            if (model.LocationId != null)
            {
                where += $@"  and PD.""LocationId"" = {model.LocationId}";
            }

            if (model.Active != null)
            {
                if ((bool)model.Active)
                {
                    where += $@" and PD.""Active"" is true";
                }
                else
                {
                    where += $@" and PD.""Active"" is false";
                }
            }

            if (model.AccountBasedAccessId != null)
            {
                var getId = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<string>($@"Select string_agg(""InventoryDepartmentId""::text,',')  from ""InventoryDepartmentUser"" where ""AccountId"" = {model.AccountBasedAccessId}");
                if (!string.IsNullOrEmpty(getId))
                {
                    where += $@" and PD.""InventoryDepartmentId"" in ({getId})";
                }
            }

            var query = $@"SELECT PD.""InventoryDepartmentId"", PD.""Name"", PD.""Active"", PD.""CreatedBy"", PD.""CreatedDate"", PD.""ModifiedBy"", 
                                   PD.""ModifiedDate"", PD.""LocationId"", CR.""FullName"" as ""CreatedByName"", MR.""FullName"" as ""ModifiedByName""
	                            FROM ""InventoryDepartment"" PD
	                            join ""Account"" CR on CR.""AccountId"" = PD.""CreatedBy""
	                            left join ""Account"" MR on MR.""AccountId"" = PD.""ModifiedBy""
                                {where}
	                            order by PD.""CreatedDate"" desc";
            return await this.unitOfWork.Current.QueryAsync<PharmacyDepartmentModel>(query);
        }

        /// <inheritdoc />
        public async Task<IEnumerable<InventoryWareHouseModel>> FetchAllWarehousesBasedOnUserAsync(InventoryWareHouseModel model)
        {
            var where = "where 1=1";

            if (model.Active != null)
            {
                if ((bool)model.Active)
                {
                    where += $@" and PWH.""Active"" is true";
                }
                else
                {
                    where += $@" and PWH.""Active"" is false";
                }
            }

            if (model.LocationId != null)
            {
                where += $@" and PWH.""LocationId""={model.LocationId} or PWH.""LocationId"" is null"; // Changing for central pharmacy.
            }

            if(model.AccountBasedAccessId != null)
            {
                var getId = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<string>($@"select string_agg(""InventoryWareHouseId""::text,',') from ""InventoryWareHouseUser"" where ""AccountId"" = {model.AccountBasedAccessId}");
                if (!string.IsNullOrEmpty(getId))
                {
                    where += $@" and PWH.""InventoryWareHouseId"" in ({getId})";
                }
            }

            var query = $@"SELECT count(PWH.*) over() as ""TotalItems"", PWH.""InventoryWareHouseId"", PWH.""Name"", PWH.""CreatedBy"", PWH.""CreatedDate"", PWH.""ModifiedBy"", 
		                        PWH.""ModifiedDate"",PWH.""Active"" , CA.""FullName"" as ""CreatedByName"",
		                        CM.""FullName"" as ""ModifiedByName"",L.""Name"" as ""LocationName"",PWH.""LocationId""
	                        FROM ""InventoryWareHouse"" PWH
	                        join ""Account"" CA on CA.""AccountId"" = PWH.""CreatedBy""
	                        left join ""Location"" L on L.""LocationId"" = PWH.""LocationId""
	                        left join ""Account"" CM on CM.""AccountId"" = PWH.""ModifiedBy""
                            {where}                           
                            order by PWH.""CreatedDate"" desc";
            
            return await this.unitOfWork.Current.QueryAsync<InventoryWareHouseModel>(query);
        }

        /// <inheritdoc />
        public async Task<IEnumerable<InventoryDepartmentalStockModel>> FetchInventoryDepartmentalStockAsync(InventoryDepartmentalStockModel model)
        {
            var where = $@"where 1=1 and (IDS.""QuantityIn"" - IDS.""QuantityOut"") > 0";

            if (!string.IsNullOrEmpty(model.UnavailableStock))
            {
                if (model.UnavailableStock == "yes")
                {
                    where = $@"where 1=1 and (IDS.""QuantityIn"" - IDS.""QuantityOut"") >= 0";
                }
                if (model.UnavailableStock == "no")
                {
                    where = $@"where 1=1 and (IDS.""QuantityIn"" - IDS.""QuantityOut"") > 0";
                }
            }

            if (model.LocationId != null)
            {
                where += $@" and ID.""LocationId"" = {model.LocationId}";
            }

            if (model.InventoryDepartmentId != null)
            {
                where += $@"  and IDS.""InventoryDepartmentId"" = {model.InventoryDepartmentId}";
            }


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

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

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

            if (model.IsExpiryProduct)
            {
                where += $@" and IDS.""ExpiryDate""::date <= current_date ";
            }

            var query = $@"SELECT count(IDS.""InventoryDepartmentalStockId"") over() as ""TotalItems"", IDS.""InventoryDepartmentalStockId"", IDS.""InventoryProductId"", IDS.""TaxId"", IDS.""QuantityIn"", IDS.""QuantityOut"", IDS.""BatchNumber"", 
		                        IDS.""ExpiryDate"", IDS.""PurchaseRate"", IDS.""InventoryStockId"", IDS.""CreatedBy"", IDS.""CreatedDate"", IDS.""ModifiedBy"", IDS.""ModifiedDate"", 
		                        IDS.""InventoryDepartmentId"",IP.""ProductName"",(IDS.""QuantityIn"" - IDS.""QuantityOut"") as ""AvailableQuantity"",
		                        Tax.""Name""::int as ""TaxPercentage"",Cat.""Name"" as ""CategoryName"",CR.""FullName"" as ""CreatedByName"", MR.""FullName"" as ""ModifiedByName"",
		                        ID.""Name"" as ""DepartmentName"",IPR.""RackName"",IPD.""InventoryProductDetailId"",IPD.""ROQ"",IPD.""ROL""
	                                   FROM ""InventoryDepartmentalStock"" IDS	           
	                                   join ""InventoryDepartment"" ID on ID.""InventoryDepartmentId"" = IDS.""InventoryDepartmentId""	           
	                                   join ""InventoryProduct"" IP on IP.""InventoryProductId"" = IDS.""InventoryProductId""	           
	                                   join ""LookupValue"" Tax on Tax.""LookupValueId"" = IDS.""TaxId""
                                       join ""LookupValue"" Cat on Cat.""LookupValueId"" = IP.""CategoryId""
	                                   join ""Account"" CR on CR.""AccountId"" = IDS.""CreatedBy""
	                                   left join ""Account"" MR on MR.""AccountId"" = IDS.""ModifiedBy""	           
	                                   left join ""InventoryProductDetail"" IPD on IPD.""InventoryProductId"" = IDS.""InventoryProductId"" and IPD.""InventoryDepartmentId"" = IDS.""InventoryDepartmentId""
	                                   left join ""InventoryProductRack"" IPR on IPR.""InventoryProductRackId"" = IPD.""InventoryProductRackId""
                                        {where}
	                                    order by IDS.""CreatedDate"" 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 await this.unitOfWork.Current.QueryAsync<InventoryDepartmentalStockModel>(query);

        }

        /// <inheritdoc />
        public async Task<int> AddInventoryProductAdditionalDetailsAsync(InventoryProductDetailModel model)
        {
            using var transaction = this.unitOfWork.BeginTransaction();

            int returnFunction()
            {
                transaction.Rollback();
                return -1;
            }

            var getRackNameQuery = $@"SELECT ""InventoryProductRackId"" FROM ""InventoryProductRack"" where lower(""RackName"") = '{model.RackName.ToLower()}' ";
            if (model.InventoryDepartmentId != null)
            {
                getRackNameQuery += $@" and ""InventoryDepartmentId"" = {model.InventoryDepartmentId} ";
            }

            if (model.InventoryWareHouseId != null)
            {
                getRackNameQuery += $@" and ""InventoryWareHouseId"" ={model.InventoryWareHouseId} ";
            }

            var getRackResponse = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<InventoryProductRack>(getRackNameQuery);
            if (getRackResponse == null)
            {
                getRackResponse = new InventoryProductRack();
                var rack = new InventoryProductRack
                {
                    CreatedBy = model.CreatedBy,
                    CreatedDate = DateTime.Now,
                    RackName = model.RackName,
                    InventoryDepartmentId = model.InventoryDepartmentId,
                    InventoryWareHouseId = model.InventoryWareHouseId
                };

                getRackResponse.InventoryProductRackId = await this.unitOfWork.InventoryProductRacks.InsertAsync(rack, transaction);
                if (getRackResponse.InventoryProductRackId == 0)
                {
                    returnFunction();
                }
            }

            if (model.InventoryProductDetailId == 0)
            {
                var detail = new InventoryProductDetail
                {
                    CreatedBy = model.CreatedBy,
                    CreatedDate = DateTime.Now,
                    InventoryDepartmentId = model.InventoryDepartmentId,
                    InventoryProductId = model.InventoryProductId,
                    InventoryProductRackId = getRackResponse.InventoryProductRackId,
                    ROL = model.ROL,
                    ROQ = model.ROQ,
                    InventoryWareHouseId = model.InventoryWareHouseId
                };

                model.InventoryProductDetailId = await this.unitOfWork.InventoryProductDetails.InsertAsync(detail, transaction);
                if (model.InventoryProductDetailId == 0)
                {
                    returnFunction();
                }
            }
            else
            {
                var findOldOne = await this.unitOfWork.InventoryProductDetails.FindAsync(x => x.InventoryProductDetailId == model.InventoryProductDetailId);
                if (findOldOne != null)
                {
                    findOldOne.ROL = model.ROL;
                    findOldOne.ROQ = model.ROQ;
                    findOldOne.InventoryProductRackId = getRackResponse.InventoryProductRackId;
                    findOldOne.ModifiedBy = model.CreatedBy;
                    findOldOne.ModifiedDate = DateTime.Now;
                    var updateResponse = await this.unitOfWork.InventoryProductDetails.UpdateAsync(findOldOne, transaction);
                    if (updateResponse == 0)
                    {
                        returnFunction();
                    }
                }
                else
                {
                    returnFunction();
                }
            }

            transaction.Commit();
            return model.InventoryProductDetailId;
        }

        /// <inheritdoc />
        public async Task<IEnumerable<InventoryProductRackModel>> FetchRackDepartmentWiseAsync(InventoryProductRackModel model)
        {
            var where = "where 1=1";
            if (model.InventoryDepartmentId != null)
            {
                where += $@" and ""InventoryDepartmentId"" = {model.InventoryDepartmentId} ";
            }

            if (model.InventoryWareHouseId != null)
            {
                where += $@" and ""InventoryWareHouseId"" = {model.InventoryWareHouseId} ";
            }

            var query = $@"Select * from ""InventoryProductRack"" {where}";

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

        /// <inheritdoc />
        public async Task<int> AddDepartmentStockConsumptionAsync(InventoryDepartmentConsumptionModel model)
        {
            var transaction = this.unitOfWork.Current.BeginTransaction();

            var getStock = await this.unitOfWork.InventoryDepartmentalStocks.FindAsync(d => d.InventoryDepartmentalStockId == model.InventoryDepartmentalStockId);
            if (getStock == null)
            {
                transaction.Rollback();
                return -1;
            }

            var consumption = new InventoryDepartmentConsumption
            {
                ConsumedBy = (int)model.ConsumedBy,
                CreatedBy = model.CreatedBy,
                CreatedDate = DateTime.Now,
                InventoryDepartmentalStockId = (int)model.InventoryDepartmentalStockId,
                QuantityConsumed = model.QuantityConsumed,
                ReasonForConsumption = model.ReasonForConsumption,
                QuantityBeforeConsumption = (getStock.QuantityIn - getStock.QuantityOut)
            };

            consumption.InventoryDepartmentConsumptionId = await this.unitOfWork.InventoryDepartmentConsumptions.InsertAsync(consumption, transaction);
            if (consumption.InventoryDepartmentConsumptionId == 0)
            {
                transaction.Rollback();
                return -1;
            }

            

            getStock.QuantityOut += model.QuantityConsumed;
            getStock.ModifiedBy = model.ConsumedBy;
            getStock.ModifiedDate = DateTime.Now;

            var updateResponse = await this.unitOfWork.InventoryDepartmentalStocks.UpdateAsync(getStock, transaction);
            if (updateResponse == 0)
            {
                transaction.Rollback();
                return -1;
            }

            transaction.Commit();
            return consumption.InventoryDepartmentConsumptionId;
        }

        /// <inheritdoc />
        public async Task<IEnumerable<InventoryDepartmentConsumptionModel>> FetchDepartmentalConsumptionProductsAsync(InventoryDepartmentConsumptionModel model)
        {
            var where = " where 1=1";

            if (model.InventoryDepartmentalStockId != null)
            {
                where += $@" and IDC.""PharmacyDepartmentalStockId"" = {model.InventoryDepartmentalStockId}";
            }

            if (!string.IsNullOrEmpty(model.FromDate))
            {
                where += $@" and IDC.""CreatedDate""::date >= '{Convert.ToDateTime(model.FromDate)::yyyy-MM-dd}'";
            }

            if (!string.IsNullOrEmpty(model.ToDate))
            {
                where += $@" and IDC.""CreatedDate""::date <= '{Convert.ToDateTime(model.ToDate)::yyyy-MM-dd}'";
            }

            if (model.InventoryDepartmentId != null)
            {
                where += $@"  and IDS.""InventoryDepartmentId"" = {model.InventoryDepartmentId}";
            }

            if (!string.IsNullOrEmpty(model.InventoryProductIds))
            {
                where += $@" and IP.""InventoryProductId"" in ({model.InventoryProductIds})";
            }

            if (model.ConsumedBy != null)
            {
                where += $@" and IDC.""ConsumedBy"" = {model.ConsumedBy}";
            }

            var query = $@"SELECT IDC.""InventoryDepartmentConsumptionId"", IDC.""InventoryDepartmentalStockId"", IDC.""QuantityConsumed"", IDC.""ConsumedBy"",
		                            IDC.""CreatedDate"", IDC.""ReasonForConsumption"", IDC.""CreatedBy"", IDC.""QuantityBeforeConsumption"" ,
	                                  IDS.""BatchNumber"",IDS.""ExpiryDate"",IDS.""CreatedDate"" as ""StockCreditedDate"",
	                                  IP.""ProductName"",Tax.""Name""::int as ""TaxPercentage"",IDS.""QuantityIn"",IDS.""QuantityOut"",
                                      (IDS.""QuantityIn"" - IDS.""QuantityOut"") as ""AvailableQuantity"",
	                                  Cat.""Name"" as ""CategoryName"",CON.""FullName"" as ""ConsumedByName"",CONR.""RoleName"" as ""ConsumedByRole"",
	                                  CR.""FullName"" as ""CreatedByName"",CRR.""RoleName""  as ""CreatedByRole""	
	                                     FROM ""InventoryDepartmentConsumption"" IDC
	                                     join ""InventoryDepartmentalStock"" IDS on IDS.""InventoryDepartmentalStockId"" = IDC.""InventoryDepartmentalStockId"" 
	                                     join ""InventoryProduct"" IP on IP.""InventoryProductId"" = IDS.""InventoryProductId"" 
	                                     join ""LookupValue"" Tax on Tax.""LookupValueId"" = IDS.""TaxId""
                                         join ""LookupValue"" Cat on Cat.""LookupValueId"" = IP.""CategoryId""
                                         join ""Account"" CON on CON.""AccountId""  = IDC.""ConsumedBy"" 
                                         join ""Role"" CONR on CONR.""RoleId"" = CON.""RoleId"" 
                                         join ""Account"" CR on CR.""AccountId""  = IDC.""CreatedBy""  
                                         join ""Role"" CRR on CRR.""RoleId"" = CR.""RoleId""  
                                         {where}
                                         order by IDC.""CreatedDate"" desc";

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