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

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

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

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

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

            var header = new IssueHeader
            {
                ApprovedDate = DateTime.Now,
                CreatedBy = model.CreatedBy,
                CreatedDate = DateTime.Now,
                IssueDate = DateTime.Now,
                OperationIndentHeaderId = model.OperationIndentHeaderId
            };

            header.IssueHeaderId = await this.unitOfWork.IssueHeaders.InsertAsync(header, transaction);
            if (header.IssueHeaderId == 0)
            {
                returnFunction();
            }

            foreach (var product in model.Products)
            {
                var issueDetail = new IssueDetail
                {
                    IssueHeaderId = header.IssueHeaderId,
                    ProductId = product.PharmacyProductId,
                    Quantity = product.Quantity,
                    StockId = (int)product.PharmacyStockId
                };

                issueDetail.IssueDetailId = await this.unitOfWork.IssueDetails.InsertAsync(issueDetail, transaction);
                if (issueDetail.IssueDetailId == 0)
                {
                    returnFunction();
                }

                var pharmacyStock = await this.unitOfWork.PharmacyStocks.FindAsync(s => s.PharmacyStockId == (int)product.PharmacyStockId);
                if (pharmacyStock == null)
                {
                    returnFunction();
                }

                var otStock = new OperationStock
                {
                    PharmacyStockId = pharmacyStock.PharmacyStockId,
                    PharmacyProductId = pharmacyStock.PharmacyProductId,
                    Barcode = pharmacyStock.Barcode,
                    BatchNumber = pharmacyStock.BatchNumber,
                    CreatedBy = model.CreatedBy,
                    CreatedDate = DateTime.Now,
                    ExpiryDate = pharmacyStock.ExpiryDate,
                    Mrp = pharmacyStock.Mrp,
                    PurchaseRate = pharmacyStock.PurchaseRate,
                    QuantityIn = product.Quantity,
                    QuantityOut = 0
                };

                var oldOTStock = await this.unitOfWork.OperationStocks.FindAsync(s => s.PharmacyStockId == pharmacyStock.PharmacyStockId);
                if (oldOTStock == null)
                {
                    otStock.OperationStockId = await this.unitOfWork.OperationStocks.InsertAsync(otStock, transaction);
                    if (otStock.OperationStockId == 0)
                    {
                        returnFunction();
                    }
                }
                else
                {
                    oldOTStock.QuantityIn += product.Quantity;
                    oldOTStock.ModifiedBy = model.CreatedBy;
                    oldOTStock.ModifiedDate = DateTime.Now;

                    var otUpdateResponse = await this.unitOfWork.OperationStocks.UpdateAsync(oldOTStock, transaction);
                    if (otUpdateResponse == 0)
                    {
                        returnFunction();
                    }
                }

                pharmacyStock.QuantityOut += product.Quantity;
                pharmacyStock.ModifiedBy = model.CreatedBy;
                pharmacyStock.ModifiedDate = DateTime.Now;

                var pharmacyStockResponse = await this.unitOfWork.PharmacyStocks.UpdateAsync(pharmacyStock, transaction);
                if (pharmacyStockResponse == 0)
                {
                    returnFunction();
                }

                var getOTIndentProducts = await this.unitOfWork.OperationIndentDetails.FindAsync(x => x.OperationIndentHeaderId == model.OperationIndentHeaderId && x.PharmacyProductId == product.PharmacyProductId);
                if (getOTIndentProducts == null)
                {
                    returnFunction();
                }
                else
                {
                    getOTIndentProducts.Status = "A";
                }
                var indentProductUpdateResponse = await this.unitOfWork.OperationIndentDetails.UpdateAsync(getOTIndentProducts, transaction);
                if (indentProductUpdateResponse == 0)
                {
                    returnFunction();
                }
            }

            transaction.Commit();

            await this.CheckOTIndentStatus(model.OperationIndentHeaderId, model.CreatedBy);

            return header.IssueHeaderId;
        }

        /// <inheritdoc/>
        public async Task<int> ApprovePharmacyIndents(OTIssueModel model)
        {
            var transaction = this.unitOfWork.BeginTransaction();
            var indentHeader = await this.unitOfWork.IndentHeaders.FindAsync(h => h.IndentHeaderId == model.IndentHeaderId);
            if (indentHeader == null)
            {
                transaction.Rollback();
                return -1;
            }

            var header = new IssueHeader
            {
                ApprovedDate = DateTime.Now,
                CreatedBy = model.CreatedBy,
                CreatedDate = DateTime.Now,
                IssueDate = DateTime.Now,
                IndentHeaderId = model.IndentHeaderId,
                PharmacyWareHouseId = model.PharmacyWareHouseId
            };

            header.IssueHeaderId = await this.unitOfWork.IssueHeaders.InsertAsync(header, transaction);
            if (header.IssueHeaderId == 0)
            {
                transaction.Rollback();
                return -1;
            }

            var pharmacyIssueStockHeader = new PharmacyIssuedStockHeader
            {
                Comment = model.Comment,
                IssuedBy = model.CreatedBy,
                IssuedDate = DateTime.Now,
                IssueHeaderId = header.IssueHeaderId,
                HandOverTo = model.HandOverTo,
                IssueNumber = await this.GetIssueNumber(String.Empty)
            };

            var pharmacyIssueStockDetail = new List<PharmacyIssuedStockDetail>();

            foreach (var product in model.Products)
            {
                var issueDetail = new IssueDetail
                {
                    IssueHeaderId = header.IssueHeaderId,
                    ProductId = product.PharmacyProductId,
                    Quantity = product.Quantity,
                    StockId = (int)product.PharmacyStockId
                };

                issueDetail.IssueDetailId = await this.unitOfWork.IssueDetails.InsertAsync(issueDetail, transaction);
                if (issueDetail.IssueDetailId == 0)
                {
                    transaction.Rollback();
                    return -1;
                }

                var pharmacyStock = await this.unitOfWork.PharmacyStocks.FindAsync(s => s.PharmacyStockId == (int)product.PharmacyStockId);
                if (pharmacyStock == null)
                {
                    transaction.Rollback();
                    return -1;
                }

                if (indentHeader.PharmacyDepartmentId == null)
                {
                    var wareHouseRetailLink = await this.unitOfWork.RetailWareHouseLinks.FindAsync(x => x.PharmacyWareHouseId == pharmacyStock.PharmacyWareHouseId && x.RetailPharmacyId == indentHeader.RetailPharmacyId);

                    if (wareHouseRetailLink == null)
                    {
                        transaction.Rollback();
                        return -1;
                    }

                    pharmacyIssueStockHeader.RetailWareHouseLinkId = wareHouseRetailLink?.RetailWareHouseLinkId;
                }

                if (indentHeader.PharmacyDepartmentId != null)
                {
                    pharmacyIssueStockHeader.PharmacyDepartmentId = indentHeader?.PharmacyDepartmentId;
                }

                if (pharmacyIssueStockHeader.PharmacyIssuedStockHeaderId == 0)
                {
                    pharmacyIssueStockHeader.PharmacyIssuedStockHeaderId = await this.unitOfWork.PharmacyIssuedStockHeaders.InsertAsync(pharmacyIssueStockHeader, transaction);
                    if (pharmacyIssueStockHeader.PharmacyIssuedStockHeaderId == 0)
                    {
                        transaction.Rollback();
                        return -1;
                    }
                }

                var pharmacyIssueDetail = new PharmacyIssuedStockDetail
                {
                    BatchNumber = pharmacyStock.BatchNumber,
                    ExpiryDate = pharmacyStock.ExpiryDate,
                    Mrp = pharmacyStock.Mrp,
                    PharmacyProductId = pharmacyStock.PharmacyProductId,
                    PharmacyStockId = pharmacyStock.PharmacyStockId,
                    PurchaseRate = pharmacyStock.PurchaseRate,
                    QuantityIn = product.Quantity,
                    TaxId = (int)pharmacyStock.TaxId,
                    PharmacyIssuedStockHeaderId = pharmacyIssueStockHeader.PharmacyIssuedStockHeaderId
                };

                pharmacyIssueStockDetail.Add(pharmacyIssueDetail);

                pharmacyStock.QuantityOut += product.Quantity;
                pharmacyStock.ModifiedBy = model.CreatedBy;
                pharmacyStock.ModifiedDate = DateTime.Now;

                var pharmacyStockResponse = await this.unitOfWork.PharmacyStocks.UpdateAsync(pharmacyStock, transaction);
                if (pharmacyStockResponse == 0)
                {
                    transaction.Rollback();
                    return -1;
                }
            }

            indentHeader.ApprovedBy = model.CreatedBy;
            indentHeader.ApprovedDate = DateTime.Now;
            var indentHeaderResponse = await this.unitOfWork.IndentHeaders.UpdateAsync(indentHeader, transaction);
            if (indentHeaderResponse == 0)
            {
                transaction.Rollback();
                return -1;
            }

            var pharmacyIssueStockDetailResponse = await this.unitOfWork.PharmacyIssuedStockDetails.BulkInsertAsync(pharmacyIssueStockDetail, transaction);
            if (pharmacyIssueStockDetailResponse == 0)
            {
                transaction.Rollback();
                return -1;
            }

            transaction.Commit();

            return header.IssueHeaderId;
        }

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

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

            var query = $@"select count(pish.*) over() as ""TotalItems"",pish.""PharmacyIssuedStockHeaderId"", pish.""IssuedDate"", pish.""IssuedBy"", pish.""HandOverTo"", pish.""Comment"", 
 	                            pish.""RetailWareHouseLinkId"", pish.""PharmacyDepartmentId"", pish.""ReceivedBy"", pish.""ReceivedDate"", pish.""IssueHeaderId"", pish.""IssueNumber"",
 	                            indcr.""FullName"" as ""IndentRaisedByName"",ind.""CreatedDate"" as ""IndentCreatedDate"",
 	                            handover.""FullName"" as ""HandOverName"", ihl.""Name"" as ""RaisedFromLocation"",rp.""RetailName"" as ""RaisedForRetail"",
 	                            pwh.""WareHouseName"" as ""ApprovedFromWareHouse"", ind.""ReasonForRequirement"" , pd.""Name"" as ""RaisedForDepartment"",
 	                            received.""FullName"" as ""ReceivedByName"",issue.""FullName"" as ""IssuedByName""
	 	                            FROM ""PharmacyIssuedStockHeader"" pish
	 	                            join ""IssueHeader"" ih ON ih.""IssueHeaderId"" = pish.""IssueHeaderId"" 
	 	                            join ""IndentHeader"" ind on ind.""IndentHeaderId"" = ih.""IndentHeaderId"" 
		                            join ""Location"" ihl on ihl.""LocationId"" = ind.""LocationId"" 
		                            join ""Account"" indCr on indcr.""AccountId"" = ind.""CreatedBy""
		                            join ""PharmacyWareHouse"" pwh on pwh.""PharmacyWareHouseId"" = ih.""PharmacyWareHouseId"" 
		                            left join ""Account"" handOver on handover.""AccountId"" = pish.""HandOverTo""  	 
		                            left join ""RetailPharmacy"" rp on rp.""RetailPharmacyId"" = ind.""RetailPharmacyId"" 
		                            left join ""PharmacyDepartment"" pd ON pd.""PharmacyDepartmentId"" = ind.""PharmacyDepartmentId""
		                            left join ""Account"" received on received.""AccountId"" = pish.""ReceivedBy"" 
                                    left join ""Account"" issue on issue.""AccountId"" = pish.""IssuedBy""
		                             {where}
		                            order by pish.""IssuedDate"" 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}";
            }

            var stockHeaderResponse = await this.unitOfWork.Current.QueryAsync<PharmacyIssuedStockHeaderModel>(query);

            foreach (var header in stockHeaderResponse)
            {
                header.Products = new List<PharmacyIssuedStockDetailModel>();
                var detailQuery = $@"SELECT pisd.""PharmacyIssuedStockDetailId"", pisd.""PharmacyIssuedStockHeaderId"", pisd.""PharmacyProductId"", pisd.""TaxId"", pisd.""QuantityIn"", pisd.""BatchNumber"",
                                           pisd.""ExpiryDate"", pisd.""PurchaseRate"", pisd.""Mrp"", pisd.""Barcode"", pisd.""PharmacyStockId"",
                                           pp.""ProductName"" ,pp.""GenericName"" , cat.""Name"" as ""Category"", tax.""Name"" as ""TaxPercentage""
                                               FROM ""PharmacyIssuedStockDetail"" pisd
                                               join ""PharmacyProduct"" pp on pp.""PharmacyProductId"" =pisd.""PharmacyProductId"" 
                                               join ""LookupValue"" cat on cat.""LookupValueId"" = pp.""CategoryId"" 
                                               join ""LookupValue"" tax on tax.""LookupValueId"" = pisd.""TaxId"" 
		                                       where 1=1 and pisd.""PharmacyIssuedStockHeaderId"" = {header.PharmacyIssuedStockHeaderId}";
                header.Products = (await this.unitOfWork.Current.QueryAsync<PharmacyIssuedStockDetailModel>(detailQuery)).ToList();
            }

            return stockHeaderResponse;
        }

        /// <inheritdoc/>
        public async Task<int> AcknowledeReceivedProductsAsync(PharmacyIssuedStockHeaderModel model)
        {
            var issueHeader = await this.unitOfWork.PharmacyIssuedStockHeaders.FindAsync(h => h.IssueHeaderId == model.IssueHeaderId);

            if (issueHeader == null)
            {
                return 0;
            }

            var getIssueDetail = await this.unitOfWork.PharmacyIssuedStockDetails.FindAllAsync(d => d.PharmacyIssuedStockHeaderId == issueHeader.PharmacyIssuedStockHeaderId);

            if (getIssueDetail == null)
            {
                return 0;
            }

            var transaction = this.unitOfWork.BeginTransaction();

            foreach (var issueDetail in getIssueDetail)
            {
                var pharmacyStock = await this.unitOfWork.PharmacyStocks.FindAsync(p => p.PharmacyStockId == issueDetail.PharmacyStockId);

                if (issueHeader.PharmacyDepartmentId == null)
                {
                    var retail = new PharmacyRetailStock
                    {
                        PharmacyStockId = issueDetail.PharmacyStockId,
                        PharmacyProductId = issueDetail.PharmacyProductId,
                        Barcode = pharmacyStock.Barcode,
                        BatchNumber = issueDetail.BatchNumber,
                        CreatedBy = (int)model.ReceivedBy,
                        CreatedDate = DateTime.Now,
                        ExpiryDate = issueDetail.ExpiryDate,
                        Mrp = issueDetail.Mrp,
                        PurchaseRate = issueDetail.PurchaseRate,
                        QuantityIn = issueDetail.QuantityIn,
                        QuantityOut = 0,
                        RetailWareHouseLinkId = (int)issueHeader.RetailWareHouseLinkId,
                        TaxId = issueDetail.TaxId,
                        IsIGST = pharmacyStock.IsIGST,
                        IsSGST = pharmacyStock.IsSGST
                    };

                    var pharmacyRetailStock = await this.unitOfWork.PharmacyRetailStocks.FindAsync(r => r.PharmacyStockId == retail.PharmacyStockId && r.BatchNumber == pharmacyStock.BatchNumber && r.RetailWareHouseLinkId == retail.RetailWareHouseLinkId);

                    if (pharmacyRetailStock == null)
                    {
                        retail.PharmacyRetailStockId = await this.unitOfWork.PharmacyRetailStocks.InsertAsync(retail, transaction);
                        if (retail.PharmacyRetailStockId == 0)
                        {
                            transaction.Rollback();
                            return -1;
                        }
                    }
                    else
                    {
                        retail.PharmacyRetailStockId = pharmacyRetailStock.PharmacyRetailStockId;
                        pharmacyRetailStock.QuantityIn += retail.QuantityIn;
                        pharmacyRetailStock.ModifiedBy = retail.CreatedBy;
                        pharmacyRetailStock.ModifiedDate = DateTime.Now;
                        pharmacyRetailStock.ManuallyUpdated += 1;
                        pharmacyRetailStock.IsSGST = retail.IsSGST;
                        pharmacyRetailStock.IsIGST = retail.IsIGST;
                        var updateResponse = await this.unitOfWork.PharmacyRetailStocks.UpdateAsync(pharmacyRetailStock, transaction);
                        if (updateResponse <= 0)
                        {
                            transaction.Rollback();
                            return -1;
                        }
                    }
                }

                if (issueHeader.PharmacyDepartmentId != null)
                {
                    var department = new PharmacyDepartmentalStock
                    {
                        PharmacyStockId = issueDetail.PharmacyStockId,
                        PharmacyProductId = issueDetail.PharmacyProductId,
                        Barcode = pharmacyStock.Barcode,
                        BatchNumber = pharmacyStock.BatchNumber,
                        CreatedBy = (int)model.ReceivedBy,
                        CreatedDate = DateTime.Now,
                        ExpiryDate = pharmacyStock.ExpiryDate,
                        Mrp = pharmacyStock.Mrp,
                        PurchaseRate = pharmacyStock.PurchaseRate,
                        QuantityIn = issueDetail.QuantityIn,
                        QuantityOut = 0,
                        PharmacyRetailStockId = null,
                        TaxId = (int)pharmacyStock.TaxId,
                        PharmacyDepartmentId = (int)issueHeader.PharmacyDepartmentId
                    };

                    var pharmacyDepartmentStock = await this.unitOfWork.PharmacyDepartmentalStocks.FindAsync(r => r.PharmacyStockId == pharmacyStock.PharmacyStockId && r.BatchNumber == pharmacyStock.BatchNumber && r.PharmacyDepartmentId == department.PharmacyDepartmentId);

                    if (pharmacyDepartmentStock != null)
                    {
                        department.PharmacyDepartmentalStockId = pharmacyDepartmentStock.PharmacyDepartmentalStockId;
                        pharmacyDepartmentStock.QuantityIn += department.QuantityIn;
                        pharmacyDepartmentStock.ModifiedBy = department.CreatedBy;
                        pharmacyDepartmentStock.ModifiedDate = DateTime.Now;
                        var updateResponse = await this.unitOfWork.PharmacyDepartmentalStocks.UpdateAsync(pharmacyDepartmentStock, transaction);
                        if (updateResponse <= 0)
                        {
                            transaction.Rollback();
                            return -1;
                        }
                    }
                    else
                    {
                        department.PharmacyDepartmentalStockId = await this.unitOfWork.PharmacyDepartmentalStocks.InsertAsync(department, transaction);
                        if (department.PharmacyDepartmentalStockId == 0)
                        {
                            transaction.Rollback();
                            return -1;
                        }
                    }
                }
            }

            issueHeader.ReceivedBy = model.ReceivedBy;
            issueHeader.ReceivedDate = DateTime.Now;

            var issueUpdateResponse = await this.unitOfWork.PharmacyIssuedStockHeaders.UpdateAsync(issueHeader, transaction);
            if (issueUpdateResponse == 0)
            {
                transaction.Rollback();
                return -1;
            }

            transaction.Commit();

            return issueUpdateResponse;
        }

        /// <inheritdoc/>
        public async Task<int> ApproveInventoryIndents(OTIssueModel model)
        {
            var transaction = this.unitOfWork.BeginTransaction();
            var indentHeader = await this.unitOfWork.IndentHeaders.FindAsync(h => h.IndentHeaderId == model.IndentHeaderId);
            if (indentHeader == null)
            {
                transaction.Rollback();
                return -1;
            }

            var header = new IssueHeader
            {
                ApprovedDate = DateTime.Now,
                CreatedBy = model.CreatedBy,
                CreatedDate = DateTime.Now,
                IssueDate = DateTime.Now,
                IndentHeaderId = model.IndentHeaderId,
                InventoryWareHouseId = model.InventoryWareHouseId
            };

            header.IssueHeaderId = await this.unitOfWork.IssueHeaders.InsertAsync(header, transaction);
            if (header.IssueHeaderId == 0)
            {
                transaction.Rollback();
                return -1;
            }

            var inventoryIssueStockHeader = new InventoryIssuedStockHeader
            {
                Comment = model.Comment,
                IssuedBy = model.CreatedBy,
                IssuedDate = DateTime.Now,
                IssueHeaderId = header.IssueHeaderId,
                HandOverTo = model.HandOverTo,
                IssueNumber = await this.GetIssueNumber("fromInventory")
            };

            var inventoryIssueStockDetails = new List<InventoryIssuedStockDetail>();

            foreach (var product in model.Products)
            {
                var issueDetail = new IssueDetail
                {
                    IssueHeaderId = header.IssueHeaderId,
                    ProductId = product.InventoryProductId,
                    Quantity = product.Quantity,
                    StockId = (int)product.InventoryStockId
                };

                issueDetail.IssueDetailId = await this.unitOfWork.IssueDetails.InsertAsync(issueDetail, transaction);
                if (issueDetail.IssueDetailId == 0)
                {
                    transaction.Rollback();
                    return -1;
                }

                var inventoryStock = await this.unitOfWork.InventoryStocks.FindAsync(s => s.InventoryStockId == (int)product.InventoryStockId);
                if (inventoryStock == null)
                {
                    transaction.Rollback();
                    return -1;
                }

                if (indentHeader.InventoryDepartmentId != null)
                {
                    inventoryIssueStockHeader.InventoryDepartmentId = indentHeader?.InventoryDepartmentId;
                }

                if (inventoryIssueStockHeader.InventoryIssuedStockHeaderId == 0)
                {
                    inventoryIssueStockHeader.InventoryIssuedStockHeaderId = await this.unitOfWork.InventoryIssuedStockHeaders.InsertAsync(inventoryIssueStockHeader, transaction);
                    if (inventoryIssueStockHeader.InventoryIssuedStockHeaderId == 0)
                    {
                        transaction.Rollback();
                        return -1;
                    }
                }

                var inventoryIssueDetail = new InventoryIssuedStockDetail
                {
                    BatchNumber = inventoryStock.BatchNumber,
                    ExpiryDate = inventoryStock.ExpiryDate,
                    InventoryProductId = inventoryStock.InventoryProductId,
                    InventoryStockId = inventoryStock.InventoryStockId,
                    PurchaseRate = inventoryStock.PurchaseRate,
                    QuantityIn = product.Quantity,
                    TaxId = inventoryStock.TaxId,
                    InventoryIssuedStockHeaderId = inventoryIssueStockHeader.InventoryIssuedStockHeaderId
                };

                inventoryIssueStockDetails.Add(inventoryIssueDetail);

                inventoryStock.QuantityOut += product.Quantity;
                inventoryStock.ModifiedBy = model.CreatedBy;
                inventoryStock.ModifiedDate = DateTime.Now;

                var inventoryStockResponse = await this.unitOfWork.InventoryStocks.UpdateAsync(inventoryStock, transaction);
                if (inventoryStockResponse == 0)
                {
                    transaction.Rollback();
                    return -1;
                }
            }

            indentHeader.ApprovedBy = model.CreatedBy;
            indentHeader.ApprovedDate = DateTime.Now;
            var indentHeaderResponse = await this.unitOfWork.IndentHeaders.UpdateAsync(indentHeader, transaction);
            if (indentHeaderResponse == 0)
            {
                transaction.Rollback();
                return -1;
            }
            var inventoryIssueStockResponse = await this.unitOfWork.InventoryIssuedStockDetails.BulkInsertAsync(inventoryIssueStockDetails, transaction);
            if (inventoryIssueStockResponse == 0)
            {
                transaction.Rollback();
                return -1;
            }


            transaction.Commit();

            return header.IssueHeaderId;
        }

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

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

            var query = $@"select count(iish.*) over() as ""TotalItems"",iish.""InventoryIssuedStockHeaderId"", iish.""IssuedDate"", iish.""IssuedBy"", iish.""HandOverTo"", iish.""Comment"", 
 	                            iish.""InventoryDepartmentId"", iish.""ReceivedBy"", iish.""ReceivedDate"", iish.""IssueHeaderId"", iish.""IssueNumber"",
 	                            indcr.""FullName"" as ""IndentRaisedByName"",ind.""CreatedDate"" as ""IndentCreatedDate"",
 	                            handover.""FullName"" as ""HandOverName"", ihl.""Name"" as ""RaisedFromLocation"",
 	                            iwh.""Name"" as ""ApprovedFromWareHouse"", ind.""ReasonForRequirement"" , id.""Name"" as ""RaisedForDepartment"",
 	                            received.""FullName"" as ""ReceivedByName"",issue.""FullName"" as ""IssuedByName""
	 	                            FROM ""InventoryIssuedStockHeader"" iish
	 	                            join ""IssueHeader"" ih ON ih.""IssueHeaderId"" = iish.""IssueHeaderId"" 
	 	                            join ""IndentHeader"" ind on ind.""IndentHeaderId"" = ih.""IndentHeaderId"" 
		                            join ""Location"" ihl on ihl.""LocationId"" = ind.""LocationId"" 
		                            join ""Account"" indCr on indcr.""AccountId"" = ind.""CreatedBy""
		                            join ""InventoryWareHouse"" iwh on iwh.""InventoryWareHouseId"" = ih.""InventoryWareHouseId"" 
		                            join ""InventoryDepartment"" id ON id.""InventoryDepartmentId"" = iish.""InventoryDepartmentId"" 
		                            left join ""Account"" handOver on handover.""AccountId"" = iish.""HandOverTo""
		                            left join ""Account"" received on received.""AccountId"" = iish.""ReceivedBy"" 
                                    left join ""Account"" issue on issue.""AccountId"" = iish.""IssuedBy""
		                            {where}
		                            order by iish.""IssuedDate"" 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}";
            }

            var stockHeaderResponse = await this.unitOfWork.Current.QueryAsync<InventoryIssuedStockHeaderModel>(query);

            foreach (var header in stockHeaderResponse)
            {
                header.Products = new List<InventoryIssuedStockDetailModel>();
                var detailQuery = $@"SELECT iisd.""InventoryIssuedStockDetailId"", iisd.""InventoryIssuedStockHeaderId"", iisd.""InventoryProductId"", 
		                                   iisd.""TaxId"", iisd.""QuantityIn"", iisd.""BatchNumber"",
                                           iisd.""ExpiryDate"", iisd.""PurchaseRate"", iisd.""InventoryStockId"",
                                           ip.""ProductName"" ,cat.""Name"" as ""Category"", tax.""Name"" as ""TaxPercentage""
                                       FROM ""InventoryIssuedStockDetail"" iisd 
                                       join ""InventoryProduct"" ip on ip.""InventoryProductId"" =iisd.""InventoryProductId"" 
                                       join ""LookupValue"" cat on cat.""LookupValueId"" = ip.""CategoryId"" 
                                       join ""LookupValue"" tax on tax.""LookupValueId"" = iisd.""TaxId"" 
		                               where 1=1 and iisd.""InventoryIssuedStockHeaderId"" = {header.InventoryIssuedStockHeaderId}";
                header.Products = (await this.unitOfWork.Current.QueryAsync<InventoryIssuedStockDetailModel>(detailQuery)).ToList();
            }

            return stockHeaderResponse;
        }

        /// <inheritdoc/>
        public async Task<int> AcknowledeReceivedProductsInventoryAsync(InventoryIssuedStockHeaderModel model)
        {
            var issueHeader = await this.unitOfWork.InventoryIssuedStockHeaders.FindAsync(h => h.IssueHeaderId == model.IssueHeaderId);

            if (issueHeader == null)
            {
                return 0;
            }

            var getIssueDetail = await this.unitOfWork.InventoryIssuedStockDetails.FindAllAsync(d => d.InventoryIssuedStockHeaderId == issueHeader.InventoryIssuedStockHeaderId);

            if (getIssueDetail == null)
            {
                return 0;
            }

            var transaction = this.unitOfWork.BeginTransaction();

            foreach (var issueDetail in getIssueDetail)
            {
                var inventoryStock = await this.unitOfWork.InventoryStocks.FindAsync(p => p.InventoryStockId == issueDetail.InventoryStockId);

                if (issueHeader.InventoryDepartmentId != null)
                {
                    var department = new InventoryDepartmentalStock
                    {
                        InventoryStockId = issueDetail.InventoryStockId,
                        InventoryProductId = issueDetail.InventoryProductId,
                        BatchNumber = inventoryStock.BatchNumber,
                        CreatedBy = (int)model.ReceivedBy,
                        CreatedDate = DateTime.Now,
                        ExpiryDate = inventoryStock.ExpiryDate,
                        PurchaseRate = inventoryStock.PurchaseRate,
                        QuantityIn = issueDetail.QuantityIn,
                        QuantityOut = 0,
                        TaxId = inventoryStock.TaxId,
                        InventoryDepartmentId = (int)issueHeader.InventoryDepartmentId
                    };

                    var checkCondition = $@"where 1=1 and ""InventoryStockId"" = {inventoryStock.InventoryStockId} and ""InventoryDepartmentId"" = {department.InventoryDepartmentId}";

                    if (!string.IsNullOrEmpty(inventoryStock.BatchNumber))
                    {
                        checkCondition += $@" and ""BatchNumber"" = '{inventoryStock.BatchNumber}' ";
                    }

                    if (inventoryStock.ExpiryDate != null && department.ExpiryDate != null)
                    {
                        checkCondition += $@" and ""ExpiryDate""::date = '{department.ExpiryDate?.ToString("yyyy-MM-dd")}'::date ";
                    }

                    var query = $@"Select * from ""InventoryDepartmentalStock"" {checkCondition}";
                    var inventoryDepartmentStock = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<InventoryDepartmentalStock>(query);

                    if (inventoryDepartmentStock != null)
                    {
                        department.InventoryDepartmentalStockId = inventoryDepartmentStock.InventoryDepartmentalStockId;
                        inventoryDepartmentStock.QuantityIn += department.QuantityIn;
                        inventoryDepartmentStock.ModifiedBy = department.CreatedBy;
                        inventoryDepartmentStock.ModifiedDate = DateTime.Now;
                        var updateResponse = await this.unitOfWork.InventoryDepartmentalStocks.UpdateAsync(inventoryDepartmentStock, transaction);
                        if (updateResponse <= 0)
                        {
                            transaction.Rollback();
                            return -1;
                        }
                    }
                    else
                    {
                        department.InventoryDepartmentalStockId = await this.unitOfWork.InventoryDepartmentalStocks.InsertAsync(department, transaction);
                        if (department.InventoryDepartmentalStockId == 0)
                        {
                            transaction.Rollback();
                            return -1;
                        }
                    }
                }
            }

            issueHeader.ReceivedBy = model.ReceivedBy;
            issueHeader.ReceivedDate = DateTime.Now;

            var issueUpdateResponse = await this.unitOfWork.InventoryIssuedStockHeaders.UpdateAsync(issueHeader, transaction);
            if (issueUpdateResponse == 0)
            {
                transaction.Rollback();
                return -1;
            }

            transaction.Commit();

            return issueUpdateResponse;
        }

        /// <inheritdoc/>
        public async Task<int> RetailWareHouseLinkIdAsync(int wareHouseId, int retailPharmacyId)
        {
            var query = $@"select rwhl.""RetailWareHouseLinkId"" from ""RetailWareHouseLink"" rwhl where rwhl.""RetailPharmacyId"" = {retailPharmacyId} and rwhl.""PharmacyWareHouseId"" = {wareHouseId}";
            return await this.unitOfWork.Current.QuerySingleOrDefaultAsync<int>(query);
        }

        /// <inheritdoc/>
        public async Task<int> AddRetailStoreStockFromExcelSheetAsync(PharmacyRetailStockModel model)
        {
            var retail = new PharmacyRetailStock
            {
                PharmacyStockId = (int?)null,
                PharmacyProductId = model.PharmacyProductId,
                Barcode = model.Barcode,
                BatchNumber = model.BatchNumber,
                CreatedBy = model.CreatedBy,
                CreatedDate = DateTime.Now,
                ExpiryDate = model.ExpiryDate,
                Mrp = model.Mrp,
                PurchaseRate = model.PurchaseRate,
                QuantityIn = model.QuantityIn,
                QuantityOut = 0,
                RetailWareHouseLinkId = (int)model.RetailWareHouseLinkId,
                TaxId = model.TaxId,
                IsAddedDirectly = true
            };

            var pharmacyRetailStock = await this.unitOfWork.PharmacyRetailStocks.FindAsync(r => r.PharmacyStockId == (int?)null && r.PharmacyProductId == retail.PharmacyProductId && r.BatchNumber == retail.BatchNumber && r.RetailWareHouseLinkId == retail.RetailWareHouseLinkId);

            if (pharmacyRetailStock == null)
            {
                retail.PharmacyRetailStockId = await this.unitOfWork.PharmacyRetailStocks.InsertAsync(retail);
                if (retail.PharmacyRetailStockId == 0)
                {
                    return -1;
                }
            }
            else
            {
                retail.PharmacyRetailStockId = pharmacyRetailStock.PharmacyRetailStockId;
                pharmacyRetailStock.QuantityIn += retail.QuantityIn;
                pharmacyRetailStock.ModifiedBy = retail.CreatedBy;
                pharmacyRetailStock.ModifiedDate = DateTime.Now;
                pharmacyRetailStock.ManuallyUpdated += 1;
                var updateResponse = await this.unitOfWork.PharmacyRetailStocks.UpdateAsync(pharmacyRetailStock);
                if (updateResponse <= 0)
                {
                    return -1;
                }
            }

            return retail.PharmacyRetailStockId;
        }

        /// <inheritdoc/>
        public async Task<int> AddStocksToRetailViaPurchaseBillAsync(PharmacyPurchaseBill model)
        {
            var getPharmacyPurchaseHeader = await this.unitOfWork.PharmacyPurchaseHeaders.FindAsync(x => x.PharmacyPurchaseHeaderId == model.PharmacyPurchaseHeaderId);
            if (getPharmacyPurchaseHeader == null)
            {
                return -1;
            }

            var getAllProducts = (await this.unitOfWork.PharmacyPurchaseDetails.FindAllAsync(d => d.PharmacyPurchaseHeaderId == getPharmacyPurchaseHeader.PharmacyPurchaseHeaderId)).ToList();
            if (getAllProducts.Count == 0)
            {
                return -1;
            }

            var getWareHouseLinkId = await this.RetailWareHouseLinkIdAsync(getPharmacyPurchaseHeader.PharmacyWareHouseId, (int)model.RetailPharmacyId);

            if(getWareHouseLinkId == 0)
            {
                var link = new RetailWareHouseLink
                {
                    PharmacyWareHouseId = getPharmacyPurchaseHeader.PharmacyWareHouseId,
                    RetailPharmacyId = (int)model.RetailPharmacyId
                };

                getWareHouseLinkId = await this.unitOfWork.RetailWareHouseLinks.InsertAsync(link);
            }

            var transaction = this.unitOfWork.BeginTransaction(); 

            foreach (var product in getAllProducts)
            {
                var getMainStock = await this.unitOfWork.PharmacyStocks.FindAsync(p => p.PharmacyStockId == product.PharmacyStockId);
                if (getMainStock == null)
                {
                    return -2;
                }

                var retail = new PharmacyRetailStock
                {
                    PharmacyStockId = getMainStock.PharmacyStockId,
                    PharmacyProductId = getMainStock.PharmacyProductId,
                    Barcode = getMainStock.Barcode,
                    BatchNumber = getMainStock.BatchNumber,
                    CreatedBy = model.CreatedBy,
                    CreatedDate = DateTime.Now,
                    ExpiryDate = getMainStock.ExpiryDate,
                    Mrp = getMainStock.Mrp,
                    PurchaseRate = getMainStock.PurchaseRate,
                    QuantityIn = (getMainStock.QuantityIn - getMainStock.QuantityOut),
                    QuantityOut = 0,
                    RetailWareHouseLinkId = getWareHouseLinkId,
                    TaxId = (int)getMainStock.TaxId,
                    IsIGST = getMainStock.IsIGST,
                    IsSGST = getMainStock.IsSGST
                };

                var pharmacyRetailStock = await this.unitOfWork.PharmacyRetailStocks.FindAsync(r => r.PharmacyStockId == retail.PharmacyStockId && r.BatchNumber == retail.BatchNumber && r.RetailWareHouseLinkId == retail.RetailWareHouseLinkId);

                if (pharmacyRetailStock == null)
                {
                    retail.PharmacyRetailStockId = await this.unitOfWork.PharmacyRetailStocks.InsertAsync(retail, transaction);
                    if (retail.PharmacyRetailStockId == 0)
                    {
                        transaction.Rollback();
                        return -3;
                    }
                }
                else
                {
                    retail.PharmacyRetailStockId = pharmacyRetailStock.PharmacyRetailStockId;
                    pharmacyRetailStock.QuantityIn += retail.QuantityIn;
                    pharmacyRetailStock.ModifiedBy = retail.CreatedBy;
                    pharmacyRetailStock.ModifiedDate = DateTime.Now;
                    pharmacyRetailStock.ManuallyUpdated += 1;
                    pharmacyRetailStock.IsSGST = retail.IsSGST;
                    pharmacyRetailStock.IsIGST = retail.IsIGST;
                    var updateResponse = await this.unitOfWork.PharmacyRetailStocks.UpdateAsync(pharmacyRetailStock, transaction);
                    if (updateResponse <= 0)
                    {
                        transaction.Rollback();
                        return -3;
                    }
                }

                getMainStock.QuantityOut = retail.QuantityIn;
                getMainStock.ModifiedBy = retail.CreatedBy;
                getMainStock.ModifiedDate = DateTime.Now;
                var pharmacyUpdateResponse = await this.unitOfWork.PharmacyStocks.UpdateAsync(getMainStock, transaction);   
                if(pharmacyUpdateResponse <= 0)
                {
                    transaction.Rollback();
                    return -4;
                }
            }

            transaction.Commit();
            return 1;
        }

        /// <summary>
        /// Checks the ot indent status.
        /// </summary>
        /// <param name="otIndentHeaderId">The ot indent header identifier.</param>
        private async Task CheckOTIndentStatus(int otIndentHeaderId, int approvedBy)
        {
            var header = await this.unitOfWork.OperationIndentHeaders.FindAsync(h => h.OperationIndentHeaderId == otIndentHeaderId);
            if (header != null)
            {
                var details = (await this.unitOfWork.OperationIndentDetails.FindAllAsync(d => d.OperationIndentHeaderId == header.OperationIndentHeaderId && d.Status == "P")).ToList();
                if (details.Count > 0)
                {
                    header.Status = "PA";
                }
                else
                {
                    header.Status = "A";
                }
                header.ApprovedBy = approvedBy;
                header.ApprovedDate = DateTime.Now;
                await this.unitOfWork.OperationIndentHeaders.UpdateAsync(header);
            }
        }

        /// <summary>
        /// Gets the issue number.
        /// </summary>
        /// <param name="from">From.</param>
        /// <returns></returns>
        private async Task<string> GetIssueNumber(string from)
        {
            var billQuery = $@"SELECT ""IssueNumber"" FROM ""PharmacyIssuedStockHeader""
                                        ORDER BY ""PharmacyIssuedStockHeaderId"" DESC LIMIT 1";
            if (!string.IsNullOrEmpty(from) && from == "fromInventory")
            {
                billQuery = $@"SELECT ""IssueNumber"" FROM ""InventoryIssuedStockHeader""
                                        ORDER BY ""InventoryIssuedStockHeaderId"" DESC LIMIT 1";
            }

            var oldBillNumber = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<string>(billQuery);
            if (string.IsNullOrEmpty(oldBillNumber))
            {
                // PHAB2021040101
                return "IS" + DateTime.Now.ToString("yyyyMMdd") + "1";
            }
            else
            {
                var stringLiterals = oldBillNumber.Substring(0, 2);
                var previousBillDateWithCount = oldBillNumber.Substring(2);
                var counter = previousBillDateWithCount.Substring(8);
                var date = previousBillDateWithCount.Substring(0, 8);
                if (date != DateTime.Now.ToString("yyyyMMdd"))
                {
                    return "IS" + DateTime.Now.ToString("yyyyMMdd") + "1";
                }

                counter = (int.Parse(counter) + 1).ToString();
                return stringLiterals + date + counter;
            }
        }
    }
}