namespace Hims.Infrastructure.Services
{
    using System;
    using System.Collections.Generic;
    using System.Threading.Tasks;
    using System.Linq;

    using Dapper;
    using Domain.Entities;
    using Domain.Repositories.UnitOfWork;
    using Domain.Services;
    using Shared.EntityModels;
    using Shared.UserModels;
    using Shared.UserModels.OperationTheater;

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

        /// <inheritdoc cref="IOperationService" />
        public OperationServices(IUnitOfWork unitOfWork) => this.unitOfWork = unitOfWork;

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

            var indentHeader = new OperationIndentHeader
            {
                CreatedBy = model.CreatedBy,
                CreatedDate = DateTime.Now,
                Reason = model.Reason,
                Status = "P",
                RequiredDate = model.RequiredDate,
                RetailPharmacyId = model.RetailPharmacyId
            };

            indentHeader.OperationIndentHeaderId = await this.unitOfWork.OperationIndentHeaders.InsertAsync(indentHeader, transaction);
            if (indentHeader.OperationIndentHeaderId == 0)
            {
                transaction.Rollback();
                return -1;
            }

            var indentDetails = model.Products.Select(m => new OperationIndentDetail
            {
                OperationIndentHeaderId = indentHeader.OperationIndentHeaderId,
                PharmacyProductId = m.PharmacyProductId,
                Quantity = m.Quantity,
                Status = "P"
            });

            var detailsResponse = await this.unitOfWork.OperationIndentDetails.BulkInsertAsync(indentDetails, transaction);
            if (detailsResponse == 0)
            {
                transaction.Rollback();
                return -1;
            }

            transaction.Commit();
            return indentHeader.OperationIndentHeaderId;
        }

        /// <inheritdoc/>
        public async Task<IEnumerable<OTIndentModel>> FetchRaisedIndents(OTIndentModel model)
        {
            var where = "where 1=1";
            if (!string.IsNullOrEmpty(model.Status))
            {
                where += $@" and  OTH.""Status"" = '{model.Status}'";
            }

            if (!string.IsNullOrEmpty(model.Type))
            {
                if (model.Type == "M")
                {
                    where += $@" and OTH.""RetailPharmacyId"" is null";
                }
                else if (model.Type == "R")
                {
                    where += $@" and OTH.""RetailPharmacyId"" is not null";
                }
            }

            var query = $@"SELECT OTH.""OperationIndentHeaderId"", OTH.""Reason"",OTH.""RequiredDate"", OTH.""ApprovedBy"", OTH.""ApprovedDate"", 
                                OTH.""CreatedBy"", OTH.""CreatedDate"",
		                        OTH.""Status"", OTH.""RetailPharmacyId"",CA.""FullName"" as ""CreatedByName"",CR.""RoleName"" as ""CreatedByRole"",
		                        AA.""FullName"" as ""ApprovedByName"",AR.""RoleName"" as ""ApprovedByRole"", RP.""RetailName""
	                        FROM ""OperationIndentHeader"" OTH
	                        join ""Account"" CA on CA.""AccountId"" = OTH.""CreatedBy""
	                        join ""Role"" CR on CR.""RoleId"" = CA.""RoleId""	
	                        left join ""RetailPharmacy"" RP on RP.""RetailPharmacyId"" = OTH.""RetailPharmacyId""
	                        left join ""Account"" AA on AA.""AccountId"" = OTH.""ApprovedBy""
	                        left join ""Role"" AR on AR.""RoleId"" = AA.""RoleId""	
                            {where}
	                        order by OTH.""CreatedDate"" desc";
            return await this.unitOfWork.Current.QueryAsync<OTIndentModel>(query);
        }

        /// <inheritdoc/>
        public async Task<IEnumerable<ProductModel>> FetchIndentDetails(int headerId)
        {
            var query = $@"SELECT PP.""PharmacyProductId"", PP.""ProductName"", PP.""MaxQuantity"", PP.""MinQuantity"", PP.""RolQuantity"", PP.""GenericName"", 
                                    PP.""Barcode"",PP.""PurchaseUnitQty"", PP.""SaleUnitQty"", PP.""CategoryId"", PP.""CompanyId"", PP.""SupplierId"", PP.""PurchaseUnit"",
                                    PP.""SaleUnit"", PP.""TaxId"",PP.""RackId"", PP.""IsProductExpire"", PP.""Active"", PP.""CreatedBy"", PP.""CreatedDate"",
                                    C.""Name"" as ""CompanyName"",Cat.""Name"" as ""CategoryName"",Tax.""Name"" as ""Tax"",Rack.""Name"" as ""RackName"",
                                    PurchaseUnit.""Name"" as ""PurchaseUnitName"", SaleUnit.""Name"" as ""SaleUnitName"",
									OID.""Quantity"", OID.""Status""
                                   from ""OperationIndentDetail"" OID								 
	                                    join ""PharmacyProduct"" PP on PP.""PharmacyProductId"" = OID.""PharmacyProductId""
										join ""Company"" C on C.""CompanyId"" = PP.""CompanyId""
	                                    join ""LookupValue"" Cat on Cat.""LookupValueId"" = PP.""CategoryId""
	                                    join ""LookupValue"" Tax on Tax.""LookupValueId"" = PP.""TaxId""
	                                    join ""LookupValue"" Rack on Rack.""LookupValueId"" = PP.""RackId""
	                                    join ""LookupValue"" PurchaseUnit on PurchaseUnit.""LookupValueId"" = PP.""PurchaseUnit""
	                                    join ""LookupValue"" SaleUnit on SaleUnit.""LookupValueId"" = PP.""SaleUnit""
										where 1=1 and OID.""OperationIndentHeaderId"" = {headerId}";
            return await this.unitOfWork.Current.QueryAsync<ProductModel>(query);
        }

        /// <inheritdoc/>
        public async Task<int> GetPendingIndentCount(string type)
        {
            var where = $@" where ""Status"" = 'P'";
            if (type == "M")
            {
                where += $@" and ""RetailPharmacyId"" is null";
            }
            else if (type == "R")
            {
                where += $@" and ""RetailPharmacyId"" is not null";
            }

            var query = $@"Select count(*) from ""OperationIndentHeader"" {where}";
            return await this.unitOfWork.Current.QuerySingleOrDefaultAsync<int>(query);
        }

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

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

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

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

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

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

            if (model.PharmacyStockId > 0)
            {
                where += $@" and PS.""PharmacyStockId"" = {model.PharmacyStockId}";
            }
            if (model.IsExpiryProduct == true)
            {
                where += $@" and PS.""ExpiryDate""::date <= current_date ";
            }

            var query =
                $@"SELECT count(PS.*) over() as ""TotalItems"", PS.""PharmacyStockId"", PS.""PharmacyProductId"", PP.""TaxId"", PS.""QuantityIn"", PS.""QuantityOut"", PS.""BatchNumber"", PS.""ExpiryDate"",
                                    PS.""PurchaseRate"", PS.""Mrp"", PS.""Barcode"",  PP.""ProductName"",PP.""GenericName"",PS.""CreatedDate"",
                                    (PS.""QuantityIn"" - PS.""QuantityOut"") as ""AvailableQuantity"",Cat.""Name"" as ""CategoryName"",Tax.""Name""::int as ""TaxPercentage"",
	                                    PS.""PharmacyRetailStockId"",PS.""OperationStockId""
										FROM ""OperationStock"" PS  
	                                    join ""PharmacyProduct"" PP on PP.""PharmacyProductId"" = PS.""PharmacyProductId""
	                                    join ""LookupValue"" Cat on Cat.""LookupValueId"" = PP.""CategoryId""
	                                    join ""LookupValue"" Tax on Tax.""LookupValueId"" = PP.""TaxId"" 
										{where}
										order by PP.""ProductName"" 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<PharmacyStockModel>(query);
        }

        /// <inheritdoc/>
        public async Task<int> InsertSurgeryAsync(SurgeryModel model)
        {
            var checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(""SurgeryId"")  FROM ""Surgery"" WHERE TRIM(UPPER(""Name"")) = '{model.Name.ToUpper().Trim()}'");
            var surgeryModuleMaster = await this.unitOfWork.ModulesMasters.FindAsync(m => m.ModuleName == "OT");
            if (checkIf > 0)
            {
                return -1;
            }
            if (surgeryModuleMaster == null)
            {
                return -2;
            }
            var transaction = this.unitOfWork.BeginTransaction();            
                var surgery = new Surgery
                {
                    Active = true,
                    CreatedBy = model.CreatedBy,
                    CreatedDate = DateTime.Now,
                    ExpectedAmount = model.ExpectedAmount,
                    Name = model.Name,                  
                    Duration = model.Duration,
                    LocationId=model.LocationId,
                    ModulesMasterId=surgeryModuleMaster.ModulesMasterId
                };
                surgery.SurgeryId = await this.unitOfWork.Surgerys.InsertAsync(surgery, transaction);

                if (surgery.SurgeryId == 0)
                {
                    transaction.Rollback();
                    return -1;
                }
                transaction.Commit();
                return surgery.SurgeryId;            
        }


        public async Task<int> UpdateSurgeryAsync(SurgeryModel model)
        {          
            var checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(""SurgeryId"")  FROM ""Surgery"" WHERE TRIM(UPPER(""Name"")) = '{model.Name.ToUpper().Trim()}'  and ""SurgeryId"" <> {model.SurgeryId} ");
            if (checkIf > 0)
            {
                return -1;
            }           
            var previous = await this.unitOfWork.Surgerys.FindAsync(m => m.SurgeryId == model.SurgeryId);
            previous.Name = model.Name;
            previous.Duration = model.Duration;
            previous.LocationId = model.LocationId;
            previous.ModifiedBy = model.CreatedBy;
            previous.ModifiedDate = DateTime.Now;                      
            var updateResponse = await this.unitOfWork.Surgerys.UpdateAsync(previous);
            if (updateResponse == 0)
            {                
                return -1;
            }
            else
            {
                return 1;
            }
        }
        /// <inheritdoc/>
        public async Task<IEnumerable<SurgeryModel>> FetchAllSurgeryAsync(SurgeryModel model)
        {            
                var where = $@"where 1=1";              
            if (model.SurgeryId > 0)
            {
                where += $@" and S.""SurgeryId""={model.SurgeryId}";
            }
            if (model.Active != null)
            {
                where += $@" AND S.""Active"" IS {((bool)model.Active ? "TRUE" : "FALSE")} ";
            }
            if (!string.IsNullOrEmpty(model.Term))
            {
                where += $@" and (lower(S.""Name"") ilike '%{model.Term.ToLower()}%' )";
            }
            if ((model.LocationId > 0) && (model.LocationId !=null))
            {
                where += $@" and S.""LocationId""={model.LocationId}";
            }
            var query = $@"SELECT Distinct S.""SurgeryId"",S.""Name"",count(S.*) over() as ""TotalItems"", S.""ExpectedAmount"", S.""Active"", S.""CreatedBy"", S.""CreatedDate"", S.""Duration"",
		                            S.""ModifiedBy"", S.""ModifiedDate"", S.""LocationId"",L.""Name"" as LocationName,CS.""FullName"" as ""CreatedByName"",CSR.""RoleName"" as ""CreatedByRole"",
		                            MS.""FullName"" as ""ModifiedByName"",MSR.""RoleName"" as ""ModifiedByRole""
	                            FROM ""Surgery"" S
	                            join ""Account"" CS on CS.""AccountId"" = S.""CreatedBy""
	                            join ""Role"" CSR on CSR.""RoleId"" = CS.""RoleId""
	                            left join ""Account"" MS on MS.""AccountId"" =S.""ModifiedBy""								
                            left join ""Location"" L on L.""LocationId"" =S.""LocationId""
	                            left join ""Role"" MSR on MSR.""RoleId"" = MS.""RoleId"" 
                            {where} order by S.""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}";
            }
            var response = await this.unitOfWork.Current.QueryAsync<SurgeryModel>(query);
            return response;           
          
        }

        public async Task<int> ActivateOrDeactivateTest(SurgeryModel model)
        {
            var checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(""SurgeryId"")  FROM ""OTRoomSurgeryMap"" WHERE  ""SurgeryId"" = {model.SurgeryId} ");
            if (checkIf > 0)
            {
                return -1;
            }
            var query = $@"UPDATE ""Surgery""
	                           SET ""ModifiedBy""={model.CreatedBy}, ""ModifiedDate""=now(), ""Active""= {model.Active}
	                           WHERE ""SurgeryId""= {model.SurgeryId}";
            return await this.unitOfWork.Current.ExecuteAsync(query);
        }
    }
}