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

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

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

        /// <inheritdoc/>
        public async Task<long> OnIncomingQuotationAsync(IncomingQuotationHeaderModel model)
        {
            var checkIfQuery = $@"select count(*) from vendors.""IncomingQuotationHeader"" iqh where iqh.""ProductForQuotationHeaderId"" = {model.ProductForQuotationHeaderId} and iqh.""SupplierId"" = {model.SupplierId} ";

            using var transaction = this.unitOfWork.BeginTransaction();
            var header = new IncomingQuotationHeader
            {
                CreatedBy = model.CreatedBy,
                CreatedDate = DateTime.Now,
                DeliveryDate = model.DeliveryDate,
                IsFinalized = false,
                ProductForQuotationHeaderId = model.ProductForQuotationHeaderId,
                SupplierId = model.SupplierId,
                TotalAmount = Math.Round(model.TotalAmount, 2),
                TotalDiscount = Math.Round(model.TotalDiscount, 2),
                TotalNetAmount = Math.Round(model.TotalNetAmount, 2),
                TotalTax = Math.Round(model.TotalTax, 2),
                IncomingQuotationHeaderId = model.IncomingQuotationHeaderId
            };

            if (header.IncomingQuotationHeaderId > 0)
            {
                checkIfQuery += $@" and iqh.""IncomingQuotationHeaderId"" <> {header.IncomingQuotationHeaderId}";
                var checkIf = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<int>(checkIfQuery);
                if (checkIf > 0)
                {
                    transaction.Rollback();
                    return -2;
                }
                var headerOld = await this.unitOfWork.IncomingQuotationHeaders.FindAsync(h => h.IncomingQuotationHeaderId == header.IncomingQuotationHeaderId);
                if (headerOld == null)
                {
                    transaction.Rollback();
                    return -1;
                }

                headerOld.ModifiedBy = model.CreatedBy;
                headerOld.ModifiedDate = DateTime.Now;
                headerOld.SupplierId = model.SupplierId;
                headerOld.DeliveryDate = model.DeliveryDate;
                headerOld.TotalAmount = Math.Round(model.TotalAmount, 2);
                headerOld.TotalDiscount = Math.Round(model.TotalDiscount, 2);
                headerOld.TotalNetAmount = Math.Round(model.TotalNetAmount, 2);
                headerOld.TotalTax = Math.Round(model.TotalTax, 2);

                var updateHeader = await this.unitOfWork.IncomingQuotationHeaders.UpdateAsync(headerOld, transaction);
                if (updateHeader == 0)
                {
                    transaction.Rollback();
                    return -1;
                }
            }
            else
            {
                var checkIf = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<int>(checkIfQuery);
                if (checkIf > 0)
                {
                    transaction.Rollback();
                    return -2;
                }
                header.IncomingQuotationHeaderId = await this.unitOfWork.IncomingQuotationHeaders.InsertAsync(header, transaction);
                if (header.IncomingQuotationHeaderId == 0)
                {
                    transaction.Rollback();
                    return -1;
                }
            }

            var suppliers = await this.unitOfWork.Supplier.FindAsync(s => s.SupplierId == model.SupplierId);
            if (suppliers == null)
            {
                transaction.Rollback();
                return -1;
            }

            suppliers.DueDays = model.PaymentDueDays != null ? model.PaymentDueDays : 10;
            var updateSup = await this.unitOfWork.Supplier.UpdateAsync(suppliers, transaction);
            if (updateSup == 0)
            {
                transaction.Rollback();
                return -1;
            }

            foreach (var prod in model.Products)
            {
                if (prod.IncomingQuotationDetailId > 0)
                {
                    var detailOld = await this.unitOfWork.IncomingQuotationDetails.FindAsync(d => d.IncomingQuotationDetailId == prod.IncomingQuotationDetailId);
                    if (detailOld == null)
                    {
                        transaction.Rollback();
                        return -1;
                    }

                    detailOld.ProductForQuotationDetailId = prod.ProductForQuotationDetailId;
                    detailOld.MRP = Math.Round(prod.MRP, 2);
                    detailOld.Tax = prod.Tax;
                    detailOld.PharmacyProductApprovalId = prod.PharmacyProductApprovalId;
                    detailOld.Amount = Math.Round(prod.Amount, 2);
                    detailOld.DiscountAmount = Math.Round(prod.DiscountAmount, 2);
                    detailOld.DiscountPercentage = prod.DiscountPercentage;
                    detailOld.Quantity = prod.Quantity;
                    detailOld.PharmacyProductId = prod.PharmacyProductId;
                    detailOld.Free = prod.Free;
                    detailOld.IncomingQuotationHeaderId = header.IncomingQuotationHeaderId;
                    detailOld.NetAmount = Math.Round(prod.NetAmount, 2);
                    detailOld.PurchaseRate = Math.Round(prod.PurchaseRate, 2);
                    detailOld.TaxAmount = Math.Round(prod.TaxAmount, 2);

                    var detUpdate = await this.unitOfWork.IncomingQuotationDetails.UpdateAsync(detailOld, transaction);
                    if (detUpdate == 0)
                    {
                        transaction.Rollback();
                        return -1;
                    }
                }
                else
                {
                    var detail = new IncomingQuotationDetail
                    {
                        Amount = Math.Round(prod.Amount, 2),
                        DiscountAmount = Math.Round(prod.DiscountAmount, 2),
                        DiscountPercentage = prod.DiscountPercentage,
                        Free = prod.Free,
                        IncomingQuotationHeaderId = header.IncomingQuotationHeaderId,
                        ProductForQuotationDetailId = prod.ProductForQuotationDetailId,
                        MRP = Math.Round(prod.MRP, 2),
                        PurchaseRate = Math.Round(prod.PurchaseRate, 2),
                        NetAmount = Math.Round(prod.NetAmount, 2),
                        PharmacyProductApprovalId = prod.PharmacyProductApprovalId,
                        PharmacyProductId = prod.PharmacyProductId,
                        Quantity = prod.Quantity,
                        Tax = prod.Tax,
                        TaxAmount = Math.Round(prod.TaxAmount, 2)
                    };

                    detail.IncomingQuotationDetailId = await this.unitOfWork.IncomingQuotationDetails.InsertAsync(detail, transaction);
                    if (detail.IncomingQuotationDetailId == 0)
                    {
                        transaction.Rollback();
                        return -1;
                    }
                }

                var checkInSupplierProduct = $@"SELECT sph.""SupplierProductHeaderId"", sph.""SupplierId"", sph.""StartDate"", sph.""EndDate"",
                                                spd.""SupplierProductDetailId"" , spd.""PharmacyProductId""
                                                FROM vendors.""SupplierProductDetail"" spd
                                                join vendors.""SupplierProductHeader"" sph on sph.""SupplierProductHeaderId""=spd.""SupplierProductHeaderId""
                                                where sph.""SupplierId"" = {header.SupplierId} and spd.""PharmacyProductId"" = {prod.PharmacyProductId}
                                                and date(sph.""StartDate"") >= date(now())";
                var res = (await this.unitOfWork.Current.QueryAsync<SupplierNewProductModel>(checkInSupplierProduct)).ToList();
                if (res.Count == 0)
                {
                    var getRunningQuoteHeaderQuery = $@"SELECT sph.""SupplierProductHeaderId"", sph.""SupplierId"", sph.""StartDate"", sph.""EndDate""
	                                                     FROM vendors.""SupplierProductHeader"" sph	
                                                         where sph.""SupplierId"" = {header.SupplierId} and now()::date >= sph.""StartDate""::date and now()::date <= sph.""EndDate""::date";
                    var runningQuoteYear = (await this.unitOfWork.Current.QueryAsync<SupplierProductHeader>(getRunningQuoteHeaderQuery)).ToList();
                    int supplierProductHeaderId = 0;
                    if (runningQuoteYear.Count > 0)
                    {
                        var getMaxYear = runningQuoteYear.Max(x => x.EndDate);
                        var findRec = runningQuoteYear.Find(x => x.EndDate == getMaxYear);
                        supplierProductHeaderId = findRec.SupplierProductHeaderId;
                    }
                    else
                    {
                        var suppHeader = new SupplierProductHeader
                        {
                            CreatedBy = model.CreatedBy,
                            CreatedDate = DateTime.Now,
                            SupplierId = model.SupplierId,
                            StartDate = DateTime.Now,
                            EndDate = DateTime.Now.AddDays(365)
                        };

                        supplierProductHeaderId = await this.unitOfWork.SupplierProductHeaders.InsertAsync(suppHeader, transaction);
                        if (supplierProductHeaderId == 0)
                        {
                            transaction.Rollback();
                            return -1;
                        }
                    }

                    var detailPro = new SupplierProductDetail
                    {
                        Mrp = Math.Round(prod.MRP, 2),
                        PharmacyProductId = prod.PharmacyProductId,
                        PurchaseRate = Math.Round(prod.PurchaseRate, 2),
                        SupplierProductHeaderId = supplierProductHeaderId
                    };

                    detailPro.SupplierProductDetailId = await this.unitOfWork.SupplierProductDetails.InsertAsync(detailPro, transaction);
                    if (detailPro.SupplierProductDetailId == 0)
                    {
                        transaction.Rollback();
                        return -1;
                    }
                }
            }

            var findProduct = await this.unitOfWork.ProductForQuotationHeaders.FindAsync(h => h.ProductForQuotationHeaderId == model.ProductForQuotationHeaderId);
            if (findProduct != null)
            {
                var status = await this.unitOfWork.TendorStatus.FindAsync(t => t.Status == "Quoatation Received");
                findProduct.TenderStatusId = status.TenderStatusId;
                var update = await this.unitOfWork.ProductForQuotationHeaders.UpdateAsync(findProduct, transaction);
                if (update == 0)
                {
                    transaction.Rollback();
                    return -1;
                }
            }

            transaction.Commit();
            return header.IncomingQuotationHeaderId;
        }

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

            if (model.ProductForQuotationHeaderId > 0)
            {
                where += $@" and  iqh.""ProductForQuotationHeaderId"" = {model.ProductForQuotationHeaderId}";
            }

            var headerQuery = $@"SELECT iqh.""IncomingQuotationHeaderId"", iqh.""ProductForQuotationHeaderId"", iqh.""SupplierId"", iqh.""CreatedBy"", iqh.""CreatedDate"", iqh.""ModifiedBy"", iqh.""ModifiedDate"", 
		                                iqh.""IsFinalized"", iqh.""FinalizedBy"", iqh.""FinalizedDate"", iqh.""DeliveryDate"", iqh.""TotalAmount"", iqh.""TotalTax"", iqh.""TotalNetAmount"", 
		                                iqh.""TotalDiscount"", S.""Name"" as ""SupplierName"", a.""FullName"" as ""CreatedByName"", m.""FullName"" as ""ModifiedByName"",
		                                f.""FullName"" as ""FinalizedByName""
	                                FROM vendors.""IncomingQuotationHeader"" iqh
	                                join public.""Supplier"" s on s.""SupplierId"" = iqh.""SupplierId"" 
	                                left join public.""Account"" a on a.""AccountId"" =iqh.""CreatedBy"" 
	                                left join public.""Account"" m on m.""AccountId"" = iqh.""ModifiedBy"" 
	                                left join public.""Account"" f on f.""AccountId"" = iqh.""FinalizedBy""
	                                {where}
	                                order by iqh.""CreatedDate"" desc";
            var header = await this.unitOfWork.Current.QueryAsync<IncomingQuotationHeaderModel>(headerQuery);

            foreach (var item in header)
            {
                item.Products = new List<IncomingQuotationDetailModel>();
                var query = $@"SELECT iqd.""IncomingQuotationDetailId"", iqd.""PharmacyProductApprovalId"", iqd.""IncomingQuotationHeaderId"", iqd.""ProductForQuotationDetailId"", iqd.""PharmacyProductId"", iqd.""Tax"", iqd.""PurchaseRate"", iqd.""MRP"",
		                                iqd.""Amount"", iqd.""TaxAmount"", iqd.""DiscountPercentage"", iqd.""DiscountAmount"", iqd.""NetAmount"", iqd.""Quantity"", iqd.""Free"",
		                                pp.""ProductName"" ,pp.""GenericName"" ,c.""Name"" as ""CompanyName"", pp.""InventoryItem""
	                                FROM vendors.""IncomingQuotationDetail"" iqd
	                                join public.""PharmacyProduct"" pp on pp.""PharmacyProductId""  =iqd.""PharmacyProductId"" 
	                                join public.""Company"" c on c.""CompanyId"" = pp.""CompanyId"" 
	                                where iqd.""IncomingQuotationHeaderId"" = {item.IncomingQuotationHeaderId}
	                                order by pp.""ProductName"" asc";
                item.Products = (await this.unitOfWork.Current.QueryAsync<IncomingQuotationDetailModel>(query)).ToList();
            }

            return header;
        }

        /// <inheritdoc/>
        public async Task<int> DeleteQuotationAsync(long incomingQuotationHeaderId)
        {
            var query = $@"DELETE FROM vendors.""IncomingQuotationDetail"" WHERE  ""IncomingQuotationHeaderId"" = {incomingQuotationHeaderId}";
            await this.unitOfWork.Current.ExecuteAsync(query);
            var query2 = $@"DELETE FROM vendors.""IncomingQuotationHeader"" WHERE  ""IncomingQuotationHeaderId"" = {incomingQuotationHeaderId}";
            return await this.unitOfWork.Current.ExecuteAsync(query2);
        }

        /// <inheritdoc/>
        public async Task<int> OnAcceptQuotation(long incomingQuotationHeaderId, int createdBy)
        {
            var findHeader = await this.unitOfWork.IncomingQuotationHeaders.FindAsync(h => h.IncomingQuotationHeaderId == incomingQuotationHeaderId);
            if (findHeader == null)
            {
                return -1;
            }

            var findDetails = await this.unitOfWork.IncomingQuotationDetails.FindAllAsync(d => d.IncomingQuotationHeaderId == findHeader.IncomingQuotationHeaderId);
            var status = await this.unitOfWork.TendorStatus.FindAsync(x => x.Status == "Moved To Tender");

            var updateOtherActive = $@"update vendors.""IncomingQuotationHeader"" set ""IsFinalized"" = false , ""FinalizedBy"" = null, ""FinalizedDate""= null where ""ProductForQuotationHeaderId"" = {findHeader.ProductForQuotationHeaderId}";
            await this.unitOfWork.Current.ExecuteAsync(updateOtherActive);
            var findQuotationHeader = await this.unitOfWork.ProductForQuotationHeaders.FindAsync(z => z.ProductForQuotationHeaderId == findHeader.ProductForQuotationHeaderId);
            
            foreach (var d in findDetails)
            {
                var findApp = await this.unitOfWork.PharmacyProductApprovals.FindAsync(a => a.PharmacyProductApprovalId == d.PharmacyProductApprovalId);
                findApp.TenderStatusId = status.TenderStatusId;
                findApp.SupplierId = findHeader.SupplierId;
                findApp.Mrp = d.MRP;
                findApp.PurchaseRate = d.PurchaseRate;
                findApp.Free = d.Free;
                findApp.Quantity = d.Quantity;
                findApp.ProductForQuotationHeaderId = findHeader.ProductForQuotationHeaderId;

                var update = await this.unitOfWork.PharmacyProductApprovals.UpdateAsync(findApp);
                if (update == 0)
                {
                    return -1;
                }
            }

            findHeader.IsFinalized = true;
            findHeader.FinalizedBy = createdBy;
            findHeader.FinalizedDate = DateTime.Now;


            if (findQuotationHeader != null)
            {
                var statusComp = await this.unitOfWork.TendorStatus.FindAsync(s => s.Status == "Quotation Selected");
                findQuotationHeader.TenderStatusId = statusComp.TenderStatusId;
                await this.unitOfWork.ProductForQuotationHeaders.UpdateAsync(findQuotationHeader);
            }

            return await this.unitOfWork.IncomingQuotationHeaders.UpdateAsync(findHeader);
        }
    }
}