using System;
using System.Linq;
using Hims.Domain.Entities;
using Hims.Domain.Entities.Enums;
using Hims.Shared.UserModels.ServiceOrder;
using Newtonsoft.Json;

namespace Hims.Infrastructure.Services
{
    using System.Collections.Generic;
    using System.Threading.Tasks;
    using Dapper;
    using Hims.Domain.Repositories.UnitOfWork;
    using Hims.Domain.Services;
    using Admission = Shared.UserModels.ServiceOrder.Admission;
    using PharmacyIndent = Shared.UserModels.ServiceOrder.PharmacyIndent;
    using Package = Shared.UserModels.ServiceOrder.Package;
    using Hims.Shared.UserModels.Common;
    using Hims.Shared.UserModels.Labs;
    using Hims.Domain.Entities.Labs;
    using Hims.Shared.UserModels.ServiceOrder.Counselling;
    using System.Transactions;

    public class ServiceOrderService : IServiceOrderService
    {
        /// <summary>
        /// The unit of work.
        /// </summary>
        private readonly IUnitOfWork unitOfWork;

        /// <inheritdoc cref="IIconService" />
        public ServiceOrderService(IUnitOfWork unitOfWork)
        {
            this.unitOfWork = unitOfWork;
        }

        public async Task<double> GetTotalPaidAsync(FilterModel model)
        {
            var records = model.IsAdmission
                ? await this.unitOfWork.Receipt.FindAllAsync(x => x.AdmissionId == model.AdmissionId && x.Active)
                : await this.unitOfWork.Receipt.FindAllAsync(x => x.AppointmentId == model.AdmissionId && x.Active);

            var recordList = records.ToList();
            if (!recordList.Any()) return 0;

            var totalPaid = recordList.Where(x => x.ReceiptTypeId == ReceiptType.Cash).Sum(x => x.Cost);
            var totalRefunded = recordList.Where(x => x.ReceiptTypeId == ReceiptType.Refund).Sum(x => x.Cost);

            return totalPaid - totalRefunded;
        }

        public async Task<IEnumerable<ViewModel>> FetchAsync(FilterModel model)
        {
            var typeCondition = model.IsAdmission ? @"""AdmissionId""" : @"""AppointmentId""";
            var activeCondition = model.ActiveOnly ? @"AND s.""Active"" IS TRUE" : string.Empty;
            var query = $@"SELECT C
	                        .""ChargeName"",
	                        c.""ChargeId"",
                            s.""ServiceOrderId"",
	                        cg.""ChargeGroupName"",
	                        d.""DepartmentName"",
	                        s.""Unit"",
	                        s.""Cost"",
	                        c.""RepeatTypeId"",
                            c.""AutomaticTypeId"",
                            s.""IsMain"",
                            s.""ChargeTypeMainId"",
                            pp.""FullName"" AS ""ChargeTypeMainName"",
                            C.""ChargeTypeId"",
                            C.""ModulesMasterId"",
                            s.""AdmissionPackageId"",
                            s.""PackageModuleDetailId"",
                            s.""UsedQuantity"",
                            s.""UsedCost"",
                            s.""Active"",
                            s.""CreatedDate"",
                            s.""ModifiedDate"",
                            s.""DiscountType"",
                            s.""DiscountPercentage"",
                            s.""DiscountAmount"",
                            s.""Discount"",
                            a.""FullName"" AS ""CreatedByName"",
                            m.""FullName"" AS ""ModifiedByName""
                        FROM
	                        ""ServiceOrder"" s
	                        JOIN ""Charge"" C ON C.""ChargeId"" = s.""ChargeId""
	                        JOIN ""ChargeGroup"" cg ON cg.""ChargeGroupId"" = C.""ChargeGroupId""
	                        JOIN ""Department"" d ON d.""DepartmentId"" = cg.""DepartmentId""
	                        JOIN ""Account"" a ON a.""AccountId"" = s.""CreatedBy""
	                        LEFT JOIN ""Account"" m ON m.""AccountId"" = s.""ModifiedBy""
                            LEFT JOIN ""Provider"" pp on (pp.""ProviderId"" = s.""ChargeTypeMainId"" AND s.""ChargeTypeId"" = 1)
                        WHERE s.{typeCondition} = {model.AdmissionId} {activeCondition}
                        ORDER BY
	                        ""ServiceOrderId"" DESC";

            var response = await this.unitOfWork.Current.QueryAsync<ViewModel>(query);
            return response;
        }

        public async Task<IEnumerable<SurgeryViewModel>> FetchSurgeryServiceAsync(FilterModel model)
        {
            var typeCondition = model.IsAdmission ? @"""AdmissionId""" : @"""AppointmentId""";
            var activeCondition = model.ActiveOnly ? @"AND s.""Active"" IS TRUE" : string.Empty;
            var query = $@"
SELECT C
	                        .""ChargeCategoryName"",
	                        c.""ChargeCategoryId"",
                            s.""SurgeryServiceId"",
	                       -- d.""DepartmentName"",
	                        s.""Unit"",
	                        s.""Cost"",
	                      --  c.""RepeatTypeId"",
                        --c.""AutomaticTypeId"",
                            s.""IsMain"",
                           -- s.""ChargeTypeMainId"",
                           -- pp.""FullName"" AS ""ChargeTypeMainName"",
                           -- C.""ChargeTypeId"",
                          --  C.""ModulesMasterId"",
                            s.""AdmissionPackageId"",
                            s.""PackageModuleDetailId"",
                            s.""UsedQuantity"",
                            s.""UsedCost"",
                            s.""Active"",
                            s.""CreatedDate"",
                            s.""ModifiedDate"",
                            a.""FullName"" AS ""CreatedByName"",
                            m.""FullName"" AS ""ModifiedByName"",
                            s.""SurgeryId"",
                            Sr.""Name""
                        FROM
	                        ""SurgeryService"" s
	                      left  JOIN ""ChargeCategory"" C ON C.""ChargeCategoryId"" = s.""ChargeCategoryId""
                        left join ""Surgery"" Sr on Sr.""SurgeryId"" = s.""SurgeryId""
                          --  JOIN ""Department"" d ON d.""DepartmentId"" = cg.""DepartmentId""
	                        JOIN ""Account"" a ON a.""AccountId"" = s.""CreatedBy""
	                        LEFT JOIN ""Account"" m ON m.""AccountId"" = s.""ModifiedBy""
                            --LEFT JOIN ""Provider"" pp on (pp.""ProviderId"" = s.""ChargeTypeMainId"" AND s.""ChargeTypeId"" = 1)
                        WHERE s.{typeCondition} = {model.AdmissionId} {activeCondition}
                        ORDER BY
	                        ""SurgeryServiceId"" DESC";

            var response = await this.unitOfWork.Current.QueryAsync<SurgeryViewModel>(query);
            return response;
        }


        public async Task<IEnumerable<ScanViewModel>> FetchScanServiceAsync(FilterModel model)
        {
            var typeCondition = model.IsAdmission ? @"""AdmissionId""" : @"""AppointmentId""";
            var activeCondition = model.ActiveOnly ? @"AND s.""Active"" IS TRUE" : string.Empty;
            var query = $@"
SELECT C
	                        .""ChargeCategoryName"",
	                        c.""ChargeCategoryId"",
                            s.""ScanServiceId"",
	                       -- d.""DepartmentName"",
	                        s.""Unit"",
	                        s.""Cost"",
	                      --  c.""RepeatTypeId"",
                        --c.""AutomaticTypeId"",
                            s.""IsMain"",
                           -- s.""ChargeTypeMainId"",
                           -- pp.""FullName"" AS ""ChargeTypeMainName"",
                           -- C.""ChargeTypeId"",
                          --  C.""ModulesMasterId"",
                            s.""AdmissionPackageId"",
                            s.""PackageModuleDetailId"",
                            s.""UsedQuantity"",
                            s.""UsedCost"",
                            s.""Active"",
                            s.""CreatedDate"",
                            s.""ModifiedDate"",
                            a.""FullName"" AS ""CreatedByName"",
                            m.""FullName"" AS ""ModifiedByName"",
                            s.""ScanTestMasterId"",
                            Sr.""ScanTestName""
                        FROM
	                        ""ScanService"" s
	                      left  JOIN ""ChargeCategory"" C ON C.""ChargeCategoryId"" = s.""ChargeCategoryId""
                        left join ""ScanTestMaster"" Sr on Sr.""ScanTestMasterId"" = s.""ScanTestMasterId""
                          --  JOIN ""Department"" d ON d.""DepartmentId"" = cg.""DepartmentId""
	                        JOIN ""Account"" a ON a.""AccountId"" = s.""CreatedBy""
	                        LEFT JOIN ""Account"" m ON m.""AccountId"" = s.""ModifiedBy""
                            --LEFT JOIN ""Provider"" pp on (pp.""ProviderId"" = s.""ChargeTypeMainId"" AND s.""ChargeTypeId"" = 1)
                        WHERE s.{typeCondition} = {model.AdmissionId} {activeCondition}
                        ORDER BY
	                        ""ScanServiceId"" DESC";

            var response = await this.unitOfWork.Current.QueryAsync<ScanViewModel>(query);
            return response;
        }

        public async Task<IEnumerable<AdmissionNewLabsModel>> FetchLabServicesAsync(FilterModel model)
        {
            var typeCondition = model.IsAdmission ? @"""AdmissionId""" : @"""AppointmentId""";
            var joinOnCondition = model.IsAdmission ? @" nlbh.""AdmissionId"" = s.""AdmissionId"" " : @"nlbh.""AppointmentId"" = s.""AppointmentId""";
            var activeCondition = model.ActiveOnly ? @"AND s.""Active"" IS TRUE" : string.Empty;
            var queryForNewLabs = $@"SELECT nlbh.""NewLabBookingHeaderId"",nlbd.""NewLabBookingDetailId"",C.""TestName"",
                            case when s.""Active"" = true then lbs.""Status"" else null end ""LabStatus"",
                            c.""TestCode"",
                            s.""LabServicesId"",
	                        s.""Unit"",
	                        s.""Cost"",
                            s.""IsMain"",
                            C.""LabMainDetailId"",
                            C.""ModulesMasterId"",
                            s.""Active"",
                            s.""AdmissionPackageId"",
                            s.""PackageModuleDetailId"",
                            s.""UsedQuantity"",
                            s.""UsedCost"",
                            s.""CreatedDate"",
                            s.""ModifiedDate"",
                            s.""DiscountType"",
                            s.""DiscountPercentage"",
                            s.""DiscountAmount"",
                            s.""Discount"",
                            a.""FullName"" AS ""CreatedByName"",
                            m.""FullName"" AS ""ModifiedByName""
                        FROM
	                        ""LabServices"" s
	                        JOIN ""LabMainDetail"" C ON C.""LabMainDetailId"" = s.""LabMainDetailId""
                            JOIN ""NewLabBookingHeader"" nlbh on {joinOnCondition}
							JOIN ""NewLabBookingDetail"" nlbd on nlbd.""NewLabBookingHeaderId"" = nlbh.""NewLabBookingHeaderId"" and nlbd.""LabMainDetailId"" = C.""LabMainDetailId"" and nlbd.""LabServicesId"" = s.""LabServicesId""
                            Join ""LabBookingStatus"" lbs on lbs.""LabBookingStatusId"" = nlbd.""LabBookingStatusId""
                            JOIN ""Account"" a ON a.""AccountId"" = s.""CreatedBy""
	                        LEFT JOIN ""Account"" m ON m.""AccountId"" = s.""ModifiedBy""
                        WHERE s.{typeCondition} = {model.AdmissionId} {activeCondition}
                        ORDER BY
	                        ""LabServicesId"" DESC";
            var response = await this.unitOfWork.Current.QueryAsync<AdmissionNewLabsModel>(queryForNewLabs);
            return response;
        }

        public async Task<Admission.BasicViewModel> GetAdmissionCostInfoAsync(FilterModel model, bool isProviderCost)
        {
            var admission = model.IsAdmission ? await this.unitOfWork.Admission.FindAsync(x => x.AdmissionId == model.AdmissionId) : new Domain.Entities.Admission();
            var appointment = !model.IsAdmission ? await this.unitOfWork.Appointments.FindAsync(x => x.AppointmentId == model.AdmissionId) : new Appointment();
            var discharge = model.IsAdmission ? await this.unitOfWork.Discharge.FindAsync(x => x.AdmissionId == model.AdmissionId) : new Discharge();

            var data = new Admission.BasicViewModel();
            if (admission.Active && admission.IsDischarged == false)
            {
                data = new Admission.BasicViewModel
                {
                    BedCost = 0,
                    AdmissionDate = model.IsAdmission ? admission.AdmissionDate : appointment.AppointmentDate,
                    DischargeDate = model.IsAdmission ? DateTime.Now.Date : appointment.CreatedDate
                };
            }
            else
            {
                data = new Admission.BasicViewModel
                {
                    BedCost = 0,
                    AdmissionDate = model.IsAdmission ? admission.AdmissionDate : appointment.AppointmentDate,
                    DischargeDate = model.IsAdmission ? discharge.DischargeDate : appointment.CreatedDate
                };
            }

            if (model.IsAdmission && admission.BedId != null)
            {
                var bed = await this.unitOfWork.Beds.FindAsync(x => x.BedId == admission.BedId);
                var room = await this.unitOfWork.Rooms.FindAsync(x => x.RoomId == bed.RoomId);
                var bedAmountQuery = $@"select ""Amount"" from ""ChargeModuleDetails""
                  where ""ReferenceId"" = {room.RoomId} and ""ChargeModuleCategoryId"" = (
                  SELECT ""ChargeModuleCategoryId"" FROM ""ChargeModuleCategory"" 
                  WHERE ""ModulesMasterId"" = (SELECT ""ModulesMasterId"" FROM ""ModulesMaster"" WHERE ""ModuleName"" = 'Room') 
	                                        AND ""ChargeCategoryId"" = (Select ""ChargeCategoryId"" FROM ""Room"" WHERE ""RoomId"" = {room.RoomId}) 
                                            AND ""ChargeModuleTemplateId"" = (select ""ChargeModuleTemplateId"" from ""ChargeModuleTemplate"" where ""IsInUse"" is true and ""Active"" is true and ""LocationId"" =(select f.""LocationId"" from ""Room"" r
                                            join ""Ward"" w on w.""WardId"" = r.""WardId""
                                        join ""Floor"" f on f.""FloorId"" = w.""FloorId""
                                        join  ""Location"" l on l.""LocationId"" = f.""LocationId"" 
                                        where r.""RoomId"" ={room.RoomId} ))) ;";
                var bedAmount = await this.unitOfWork.Current.QuerySingleAsync<double>(bedAmountQuery);
                data.BedCost = bedAmount;
                data.ChargeCategoryId = (int)room.ChargeCategoryId;
                data.IsBed = true;
                var chargeCategory = await this.unitOfWork.ChargeCategorys.FindAsync(x => x.ChargeCategoryId == data.ChargeCategoryId);
                data.ChargeCategoryName = chargeCategory.ChargeCategoryName;
            }
            else
            {
                var chargeCategory = !model.IsAdmission ? await this.unitOfWork.ChargeCategorys.FindAsync(x => x.Default == true) : new ChargeCategory();
                data.ChargeCategoryId = chargeCategory.ChargeCategoryId;
                data.ChargeCategoryName = chargeCategory.ChargeCategoryName;
                data.IsBed = false;
            }

            //if (admission.AdmissionPayTypeId != null && admission.AdmissionPayTypeId > 0)
            //{
            //    var query = $@"SELECT ""AdmissionPayTypeName"" FROM ""AdmissionPayType"" WHERE ""AdmissionPayTypeId"" = {admission.AdmissionPayTypeId}";
            //    data.ModuleTypeName = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<string>(query);
            //}


            if (!isProviderCost) return data;

            var location = model.IsAdmission
                ? await this.unitOfWork.ProviderLocations.FindAllAsync(x => x.ProviderId == admission.ProviderId)
                : await this.unitOfWork.ProviderLocations.FindAllAsync(x => x.ProviderId == appointment.ProviderId);
            var providerLocations = location.ToList();
            if (!providerLocations.Any()) return data;

            var costs = JsonConvert.DeserializeObject<IEnumerable<Admission.ProviderCostModel>>(providerLocations.First().Availability);
            data.ProviderCost = costs;
            return data;
        }

        public async Task<IEnumerable<PharmacyIndent.ViewModel>> FetchPharmacyAsync(FilterModel model)
        {
            var query = $@"with main as (SELECT
	                        sd.""PharmacyIssueDetailId"",
	                        c.""Name"" ""CategoryName"",
	                        pp.""ProductName"",
	                        prs.""Mrp""  AS ""Cost"",
	                        sd.""Quantity"" - coalesce(sum(pir.""ReturnQuantity""),0)  AS ""Unit"",
	                        sd.""DiscountPercentage"",
	                        sd.""PharmacyProductId"" AS ""ProductId"",
                            i.""CreatedDate"",
                            a.""FullName"" AS ""CreatedByName"",
							s.""CreatedDate"" AS ""ModifiedDate"",
                            m.""FullName"" AS ""ModifiedByName""
                        FROM
	                        ""PharmacyIndentHeader"" i
                            LEFT JOIN ""Account"" a ON a.""AccountId"" = i.""CreatedBy""
	                        LEFT JOIN ""PharmacyIssueHeader"" s ON s.""PharmacyIndentHeaderId"" = i.""PharmacyIndentHeaderId""
                            LEFT JOIN ""Account"" m ON m.""AccountId"" = s.""CreatedBy""
	                        LEFT JOIN ""PharmacyIssueDetail"" sd ON sd.""PharmacyIssueHeaderId"" = s.""PharmacyIssueHeaderId""
	                        left join public.""PharmacyRetailStock"" prs on prs.""PharmacyRetailStockId"" = sd.""RetailStockId"" 
	                        LEFT JOIN ""PharmacyProduct"" pp on pp.""PharmacyProductId"" = sd.""PharmacyProductId""
	                        LEFT JOIN ""LookupValue"" c on c.""LookupValueId"" = pp.""CategoryId""
	                        left join public.""PharmacyInPatientReturn"" pir on pir.""PharmacyIssueDetailId""  = sd.""PharmacyIssueDetailId"" and pir.""AcceptedBy"" is not null 
                        WHERE
	                        i.""AdmissionId"" = {model.AdmissionId} AND sd.""PharmacyIssueDetailId"" IS NOT NULL
						group by sd.""PharmacyIssueDetailId"",c.""Name"",pp.""ProductName"", prs.""Mrp"",sd.""Quantity"",sd.""DiscountPercentage"",
	                       	 sd.""PharmacyProductId"", i.""CreatedDate"",a.""FullName"",s.""CreatedDate"",m.""FullName"")
	                       	select * from main where ""Unit"" > 0 ";

            var response = await this.unitOfWork.Current.QueryAsync<PharmacyIndent.ViewModel>(query);
            return response;
        }

        public async Task<IEnumerable<PackageModuleModel>> FetchPackagesAsync(FilterModel model)
        {
            var typeCondition = model.IsAdmission ? @"""AdmissionId""" : @"""AppointmentId""";
            var activeCondition = model.ActiveOnly ? @"AND ap.""Active"" IS TRUE" : string.Empty;
            var query = $@"SELECT
	                        ap.""AdmissionPackageId"",
                            ap.""CounsellingId"",
	                        p.""PackageModuleId"",
	                        p.""LocationId"",
	                        p.""ChargeModuleTemplateId"",
	                        p.""ModulesMasterIds"",
	                        p.""PackageName"",
	                        p.""ModuleTypeId"",
	                        PTL.""Name"" AS ""ModuleTypeName"",
	                        p.""Quantity"",
	                        p.""FreeQuantity"",
	                        p.""ExpiresIn"",
	                        0 AS ""Total"",
                            ap.""Active"",
                            ap.""CreatedDate"",
                            crt.""FullName"" AS ""CreatedByName"",
							ap.""ModifiedDate"",
                            mdf.""FullName"" AS ""ModifiedByName""
                        FROM
                            ""AdmissionPackage"" ap
	                        JOIN ""PackageModule"" p on p.""PackageModuleId"" = ap.""PackageId""
	                        JOIN ""LookupValue"" PTL on PTL.""LookupValueId"" = p.""ModuleTypeId""
                            JOIN ""Account"" crt ON crt.""AccountId"" = ap.""CreatedBy""
                            LEFT JOIN ""Account"" mdf ON mdf.""AccountId"" = ap.""ModifiedBy""
                        WHERE ap.{typeCondition} = {model.AdmissionId} {activeCondition}";

            var records = await this.unitOfWork.Current.QueryAsync<PackageModuleModel>(query);
            return records;
        }

        public async Task<bool> IsAutomaticPossibleAsync(int admissionId)
        {
            var allServices = await this.unitOfWork.ServiceOrder.FindAllAsync(x =>
                   x.AdmissionId == admissionId);
            var serviceOrders = allServices.ToList();
            return serviceOrders.Count(x => x.IsMain) > 0;
        }

        public async Task<bool> IsFirstTimeAsync(AutomaticFilterModel model)
        {
            var allServices = model.IsAdmission
                ? await this.unitOfWork.ServiceOrder.FindAllAsync(x => x.AdmissionId == model.AdmissionId)
                : await this.unitOfWork.ServiceOrder.FindAllAsync(x => x.AppointmentId == model.AdmissionId);

            var packages = model.IsAdmission
                ? await this.unitOfWork.AdmissionPackage.FindAllAsync(x =>
                    x.AdmissionId == model.AdmissionId && x.Active)
                : await this.unitOfWork.AdmissionPackage.FindAllAsync(x =>
                    x.AppointmentId == model.AdmissionId && x.Active);

            return !allServices.Any() && !packages.Any();
        }

        public async Task<IEnumerable<basicCounsellingModel>> GetCounsellingsAsync(AutomaticFilterModel model)
        {
            var patientWhere = model.IsAdmission
                ? $@"(SELECT ""PatientId"" FROM ""Admission"" WHERE ""AdmissionId"" = {model.Id})"
                : $@"(SELECT ""PatientId"" FROM ""Appointment"" WHERE ""AppointmentId"" = {model.Id})";

            var type = model.IsAdmission ? "IP" : "OP";

            var query = $@"select c.""CounsellingId"", c.""PatientId"", c.""PackageModuleId"", c.""CounsellingNo"" from ""Counselling"" c
                            JOIN ""PackageModule"" PM on PM.""PackageModuleId"" = c.""PackageModuleId""
                            JOIN ""LookupValue"" PTL ON PTL.""LookupValueId"" = PM.""PackageTypeId""
                            WHERE c.""PatientId"" = {patientWhere} 
                            AND c.""Active"" IS TRUE AND c.""IsInUse"" IS FALSE AND c.""IsDraft"" IS FALSE AND PTL.""Name"" ILIKE '%{type}%' AND PM.""LocationId"" = {model.LocationId}";
            return await this.unitOfWork.Current.QueryAsync<basicCounsellingModel>(query);
        }
        
        public async Task<int> ApplyCounsellingAsync(AutomaticFilterModel model)
        {
            var transaction = this.unitOfWork.BeginTransaction();
            var counselling = await this.unitOfWork.Counsellings.FindAsync(x => x.CounsellingId == Convert.ToInt32(model.Id), transaction);

            if (counselling.IsInUse == true || counselling.Active == false)
            {
                transaction.Rollback();
                return 0;
            }

            counselling.ModifiedBy = model.CreatedBy;
            counselling.ModifiedDate = DateTime.UtcNow;
            counselling.IsInUse = true;
            var counsellingResponse = await this.unitOfWork.Counsellings.UpdateAsync(counselling, transaction);
            if (counsellingResponse <= 0)
            {
                transaction.Rollback();
                return 0;
            }

            if (counselling.PackageModuleId != 0)
            {
                // update package
                var admissionPackage = new AdmissionPackage
                {
                    Active = true,
                    CreatedDate = DateTime.Now,
                    CreatedBy = model.CreatedBy,
                    AdmissionId = null,
                    AppointmentId = model.AdmissionId,
                    PackageId = counselling.PackageModuleId,
                    CounsellingId = Convert.ToInt32(model.Id)
                };

                admissionPackage.AdmissionPackageId = await this.unitOfWork.AdmissionPackage.InsertAsync(admissionPackage, transaction);
                if (admissionPackage.AdmissionPackageId == 0)
                {
                    transaction.Rollback();
                    return -1;
                }
            }

            transaction.Commit();
            return 1;
        }

        public async Task<int> AutomaticInsertAsync(AutomaticInsertModel model)
        {
            var transaction = this.unitOfWork.BeginTransaction();
            var automaticCharges = await this.unitOfWork.Charges.FindAllAsync(x => x.AutomaticTypeId != null, transaction);
            foreach (var charge in automaticCharges)
            {
                var isExits = await this.unitOfWork.ServiceOrder.FindAsync(x => x.AdmissionId == model.AdmissionId && x.ChargeId == charge.ChargeId && x.IsMain, transaction);
                if (isExits != null && !isExits.Active) continue;
                if (charge.AutomaticTypeId == (int)AutomaticType.BedCharges && !model.IsBed) continue;

                var unit = charge.RepeatTypeId == (int)RepeatType.Day ? model.NoOfDays : 1;
                var cost = charge.AutomaticTypeId == (int)AutomaticType.BedCharges
                    ? model.BedCost
                    : charge.AutomaticTypeId == (int)AutomaticType.DoctorCharges
                        // charge.RepeatTypeId == (int)RepeatType.Day ? model.DoctorCost : model.DoctorCostOnce
                        ? model.DoctorCostOnce
                        : 0;
                //here previously it is charge.cost but as we removed cost column from charge table.so we kept 0.
                //charge.Cost;

                if (isExits != null)
                {
                    isExits.Unit = unit;
                    isExits.Cost = cost;

                    var updateResponse = await this.unitOfWork.ServiceOrder.UpdateAsync(isExits, transaction);
                    if (updateResponse > 0) continue;

                    transaction.Rollback();
                    return -1;
                }

                var record = new ServiceOrder
                {
                    Active = true,
                    CreatedDate = DateTime.Now,
                    CreatedBy = model.CreatedBy,
                    AdmissionId = model.AdmissionId,
                    Cost = cost,
                    ChargeId = charge.ChargeId,
                    Unit = unit,
                    IsMain = true
                };
                var insertResponse = await this.unitOfWork.ServiceOrder.InsertAsync(record, transaction);
                if (insertResponse > 0) continue;

                transaction.Rollback();
                return -1;
            }

            transaction.Commit();
            return 1;
        }

        public async Task<int> InsertPackageAsync(Package.InsertModel model)
        {
            var isExists = await this.unitOfWork.AdmissionPackage.CountAsync(x => x.PackageId == model.PackageId && x.AdmissionId == model.AdmissionId && x.Active);
            if (isExists > 0)
            {
                return -2;
            }

            var record = new AdmissionPackage
            {
                Active = true,
                CreatedDate = DateTime.Now,
                CreatedBy = model.CreatedBy,
                AdmissionId = model.IsAdmission ? (int?)model.AdmissionId : null,
                AppointmentId = !model.IsAdmission ? (int?)model.AdmissionId : null,
                PackageId = model.PackageId
            };

            var response = await this.unitOfWork.AdmissionPackage.InsertAsync(record);
            return response;
        }

        public async Task<int> InsertAsync(InsertModel model)
        {
            //commented bcoz while adding only packages(without services), unable to insert service order 
            //if (model.Records.Count == 0)
            //    return 0;

                var transaction = this.unitOfWork.BeginTransaction();
                var admissionPackageId = 0;

                // Package
                if (model.Packages.Count > 0)
                {
                    var found = await this.unitOfWork.AdmissionPackage.FindAsync(x => x.AdmissionId == (model.IsAdmission ? (int?)model.AdmissionId : null)
                    && x.AppointmentId == (!model.IsAdmission ? (int?)model.AdmissionId : null), transaction);
                    if (found == null)
                    {
                        var admissionPackage = new AdmissionPackage
                        {
                            Active = true,
                            CreatedDate = DateTime.Now,
                            CreatedBy = model.CreatedBy,
                            AdmissionId = model.IsAdmission ? (int?)model.AdmissionId : null,
                            AppointmentId = !model.IsAdmission ? (int?)model.AdmissionId : null,
                            PackageId = model.Packages.FirstOrDefault()
                        };

                        admissionPackage.AdmissionPackageId = await this.unitOfWork.AdmissionPackage.InsertAsync(admissionPackage, transaction);
                        admissionPackageId = admissionPackage.AdmissionPackageId;
                        if (admissionPackageId == 0)
                        {
                            transaction.Rollback();
                            return -1;
                        }
                    }
                    else
                    {
                        admissionPackageId = found.AdmissionPackageId;
                    }
                }

                // Services
                var records = model.Records.Select(record => new ServiceOrder
                {
                    Active = true,
                    CreatedDate = DateTime.Now,
                    CreatedBy = model.CreatedBy,
                    AdmissionId = model.IsAdmission ? (int?)model.AdmissionId : null,
                    AppointmentId = !model.IsAdmission ? (int?)model.AdmissionId : null,
                    Cost = record.Cost,
                    ChargeId = record.ChargeId,
                    Unit = record.Unit,
                    ChargeTypeMainId = record.ChargeTypeMainId,
                    ChargeTypeId = record.ChargeTypeId,
                    Notes = record.Notes,
                    AdmissionPackageId = model.Packages.Contains(record.AdmissionPackageId ?? 0) && admissionPackageId > 0 ? (int?)admissionPackageId : null,
                    PackageModuleDetailId = record.PackageModuleDetailId,
                    UsedCost = record.UsedCost,
                    UsedQuantity = record.UsedQuantity,
                    DiscountType = record.DiscountType,
                    DiscountPercentage = record.DiscountPercentage ?? 0,
                    DiscountAmount = record.DiscountAmount ?? 0,
                    Discount = ((record.DiscountPercentage ?? 0) > 0 || (record.DiscountAmount ?? 0) > 0) ? (record.Discount ?? 0) : 0,
                }).ToList();

                // Automatic Appointment Charges for Appointment (OP)
                if (!model.IsAdmission)
                {
                    var appointmentReceipt = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<Receipt>($@"Select * from ""Receipt"" where ""AppointmentId"" = {model.AdmissionId} and ""IsAppointmentReceipt"" is true", transaction);
                    var appointCharge = await this.unitOfWork.Charges.FindAsync(x => x.ChargeTypeId == 3, transaction);
                    var found = records.Find(x => x.ChargeId == appointCharge.ChargeId);
                    if (found != null)
                        found.Cost = appointmentReceipt.Cost;
                    else
                    {
                        records.Add(new ServiceOrder
                        {
                            Active = true,
                            CreatedDate = DateTime.Now,
                            CreatedBy = model.CreatedBy,
                            AdmissionId = null,
                            AppointmentId = model.AdmissionId,
                            ChargeId = appointCharge.ChargeId,
                            Unit = 1,
                            ChargeTypeMainId = null,
                            ChargeTypeId = appointCharge.ChargeTypeId,
                            Notes = "Automatically Added Appointment Charges",
                            Discount = 0
                        });
                    }
                }
                var response = await this.unitOfWork.ServiceOrder.BulkInsertAsync(records, transaction);
                transaction.Commit();
                return response;

            }

        public async Task<int> InsertSurgeryServiceAsync(InsertModel model)
        {

            var transaction = this.unitOfWork.BeginTransaction();
            var admissionPackageId = 0;

            // Package
            if (model.Packages.Count > 0)
            {
                var found = await this.unitOfWork.AdmissionPackage.FindAsync(x => x.AdmissionId == (model.IsAdmission ? (int?)model.AdmissionId : null)
                && x.AppointmentId == (!model.IsAdmission ? (int?)model.AdmissionId : null), transaction);
                if (found == null)
                {
                    var admissionPackage = new AdmissionPackage
                    {
                        Active = true,
                        CreatedDate = DateTime.Now,
                        CreatedBy = model.CreatedBy,
                        AdmissionId = model.IsAdmission ? (int?)model.AdmissionId : null,
                        AppointmentId = !model.IsAdmission ? (int?)model.AdmissionId : null,
                        PackageId = model.Packages.FirstOrDefault()
                    };

                    admissionPackage.AdmissionPackageId = await this.unitOfWork.AdmissionPackage.InsertAsync(admissionPackage, transaction);
                    admissionPackageId = admissionPackage.AdmissionPackageId;
                    if (admissionPackageId == 0)
                    {
                        transaction.Rollback();
                        return -1;
                    }
                }
                else
                {
                    admissionPackageId = found.AdmissionPackageId;
                }
            }

            // Surgery Service
            var surgeryService = model.SurgeryServices.Select(surgeryService => new SurgeryService
            {
                Active = true,
                CreatedDate = DateTime.Now,
                CreatedBy = model.CreatedBy,
                AdmissionId = model.IsAdmission ? (int?)model.AdmissionId : null,
                AppointmentId = !model.IsAdmission ? (int?)model.AdmissionId : null,
                Cost = surgeryService.Cost,
                ChargeCategoryId = surgeryService.ChargeCategoryId,
                Unit = surgeryService.Unit,
                Notes = surgeryService.Notes,
                AdmissionPackageId = admissionPackageId > 0 ? (int?)admissionPackageId : null,
                PackageModuleDetailId = surgeryService.PackageModuleDetailId,
                UsedCost = surgeryService.UsedCost,
                UsedQuantity = surgeryService.UsedQuantity,
                SurgeryId = surgeryService.SurgeryId
            }).ToList();

            // Automatic Appointment Charges for Appointment (OP)
            //if (!model.IsAdmission)
            //{
            //    var appointmentReceipt = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<Receipt>($@"Select * from ""Receipt"" where ""AppointmentId"" = {model.AdmissionId} and ""IsAppointmentReceipt"" is true", transaction);
            //    var appointCharge = await this.unitOfWork.Charges.FindAsync(x => x.ChargeTypeId == 3, transaction);
            //    var found = surgeryService.Find(x => x.ChargeCategoryId == appointCharge.ChargeId);
            //    if (found != null)
            //        found.Cost = appointmentReceipt.Cost;
            //    else
            //    {
            //        surgeryService.Add(new SurgeryService
            //        {
            //            Active = true,
            //            CreatedDate = DateTime.Now,
            //            CreatedBy = model.CreatedBy,
            //            AdmissionId = null,
            //            AppointmentId = model.AdmissionId,
            //            ChargeCategoryId = appointCharge.ChargeCategoryId,
            //            Unit = 1,
            //            ChargeTypeMainId = null,
            //            ChargeTypeId = appointCharge.ChargeTypeId,
            //            Notes = "Automatically Added Appointment Charges"
            //        });
            //    }
            //}
            try
            {
                var response = await this.unitOfWork.SurgeryService.BulkInsertAsync(surgeryService, transaction);

                transaction.Commit();
                return response;
            }
            catch (Exception ex)
            {
                ex.Message.ToString();
                transaction.Rollback();
            }
            return 0;
        }

        public async Task<int> InsertScanServiceAsync(InsertModel model)
        {

            var transaction = this.unitOfWork.BeginTransaction();
            var admissionPackageId = 0;

            // Package
            if (model.Packages.Count > 0)
            {
                var found = await this.unitOfWork.AdmissionPackage.FindAsync(x => x.AdmissionId == (model.IsAdmission ? (int?)model.AdmissionId : null)
                && x.AppointmentId == (!model.IsAdmission ? (int?)model.AdmissionId : null), transaction);
                if (found == null)
                {
                    var admissionPackage = new AdmissionPackage
                    {
                        Active = true,
                        CreatedDate = DateTime.Now,
                        CreatedBy = model.CreatedBy,
                        AdmissionId = model.IsAdmission ? (int?)model.AdmissionId : null,
                        AppointmentId = !model.IsAdmission ? (int?)model.AdmissionId : null,
                        PackageId = model.Packages.FirstOrDefault()
                    };

                    admissionPackage.AdmissionPackageId = await this.unitOfWork.AdmissionPackage.InsertAsync(admissionPackage, transaction);
                    admissionPackageId = admissionPackage.AdmissionPackageId;
                    if (admissionPackageId == 0)
                    {
                        transaction.Rollback();
                        return -1;
                    }
                }
                else
                {
                    admissionPackageId = found.AdmissionPackageId;
                }
            }

            // Surgery Service
            var scanService = model.ScanServices.Select(scanService => new ScanService
            {
                Active = true,
                CreatedDate = DateTime.Now,
                CreatedBy = model.CreatedBy,
                AdmissionId = model.IsAdmission ? (int?)model.AdmissionId : null,
                AppointmentId = !model.IsAdmission ? (int?)model.AdmissionId : null,
                Cost = scanService.Cost,
                ChargeCategoryId = scanService.ChargeCategoryId,
                Unit = scanService.Unit,
                Notes = scanService.Notes,
                AdmissionPackageId = admissionPackageId > 0 ? (int?)admissionPackageId : null,
                PackageModuleDetailId = scanService.PackageModuleDetailId,
                UsedCost = scanService.UsedCost,
                UsedQuantity = scanService.UsedQuantity,
                ScanTestMasterId = scanService.ScanTestMasterId
            }).ToList();

            // Automatic Appointment Charges for Appointment (OP)
            //if (!model.IsAdmission)
            //{
            //    var appointmentReceipt = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<Receipt>($@"Select * from ""Receipt"" where ""AppointmentId"" = {model.AdmissionId} and ""IsAppointmentReceipt"" is true", transaction);
            //    var appointCharge = await this.unitOfWork.Charges.FindAsync(x => x.ChargeTypeId == 3, transaction);
            //    var found = surgeryService.Find(x => x.ChargeCategoryId == appointCharge.ChargeId);
            //    if (found != null)
            //        found.Cost = appointmentReceipt.Cost;
            //    else
            //    {
            //        surgeryService.Add(new SurgeryService
            //        {
            //            Active = true,
            //            CreatedDate = DateTime.Now,
            //            CreatedBy = model.CreatedBy,
            //            AdmissionId = null,
            //            AppointmentId = model.AdmissionId,
            //            ChargeCategoryId = appointCharge.ChargeCategoryId,
            //            Unit = 1,
            //            ChargeTypeMainId = null,
            //            ChargeTypeId = appointCharge.ChargeTypeId,
            //            Notes = "Automatically Added Appointment Charges"
            //        });
            //    }
            //}
            try
            {
                var response = await this.unitOfWork.ScanService.BulkInsertAsync(scanService, transaction);

                transaction.Commit();
                return response;
            }
            catch (Exception ex)
            {
                ex.Message.ToString();
                transaction.Rollback();
            }
            return 0;
        }

        public async Task<(int, List<int?>)> UpdateAsync(UpdateModel model)
        {
            var transaction = this.unitOfWork.BeginTransaction();

            // Deleted Services
            var unitChangedRecords = new List<int?>();
            foreach (var id in model.DeletedRecords)
            {
                var order = await this.unitOfWork.ServiceOrder.FindAsync(x => x.ServiceOrderId == id, transaction);
                var found = model.NewRecords.Records.Find(x => x.ServiceOrderId == id);
                if (found != null)
                {
                    var foundPackage = await this.unitOfWork.AdmissionPackage.FindAsync(x =>
                    x.AdmissionId == (model.IsAdmission ? (int?)model.NewRecords.AdmissionId : null)
                    && x.AppointmentId == (model.IsAdmission ? null : (int?)model.NewRecords.AdmissionId) && x.PackageId == found.AdmissionPackageId,
                    transaction);

                    unitChangedRecords.Add(found.ServiceOrderId);
                    order.Unit = found.Unit;
                    order.UsedQuantity = found.UsedQuantity;
                    order.UsedCost = found.UsedCost;
                    order.PackageModuleDetailId = found.PackageModuleDetailId;
                    order.AdmissionPackageId = foundPackage != null ? (int?)foundPackage.AdmissionPackageId : null;
                }
                else
                {
                    order.Active = false;
                }
                order.ModifiedDate = DateTime.Now;
                order.ModifiedBy = model.ModifiedBy;

                var updateResponse = await this.unitOfWork.ServiceOrder.UpdateAsync(order, transaction);
                if (updateResponse > 0) continue;

                transaction.Rollback();
                return (-1, new List<int?>());
            }

            // Deleted Surgery Service
            var unitChangedSurgeryService = new List<int?>();
            foreach (var id in model.DeletedSurgeryServices)
            {
                var order = await this.unitOfWork.SurgeryService.FindAsync(x => x.SurgeryServiceId == id, transaction);
                var found = model.NewRecords.SurgeryServices.Find(x => x.SurgeryServiceId == id);
                if (found != null)
                {
                    var foundPackage = await this.unitOfWork.AdmissionPackage.FindAsync(x =>
                    x.AdmissionId == (model.IsAdmission ? (int?)model.NewRecords.AdmissionId : null)
                    && x.AppointmentId == (model.IsAdmission ? null : (int?)model.NewRecords.AdmissionId) && x.PackageId == found.AdmissionPackageId,
                    transaction);

                    unitChangedSurgeryService.Add(found.SurgeryServiceId);
                    order.Unit = found.Unit;
                    order.UsedQuantity = found.UsedQuantity;
                    order.UsedCost = found.UsedCost;
                    order.PackageModuleDetailId = found.PackageModuleDetailId;
                    order.AdmissionPackageId = foundPackage != null ? (int?)foundPackage.AdmissionPackageId : null;
                }
                else
                {
                    order.Active = false;
                }
                order.ModifiedDate = DateTime.Now;
                order.ModifiedBy = model.ModifiedBy;

                var updateResponse = await this.unitOfWork.SurgeryService.UpdateAsync(order, transaction);
                if (updateResponse > 0) continue;

                transaction.Rollback();
                return (-1, new List<int?>());
            }

            // Deleted Scan Service
            var unitChangedScanService = new List<int?>();
            foreach (var id in model.DeletedScanServices)
            {
                var order = await this.unitOfWork.ScanService.FindAsync(x => x.ScanServiceId == id, transaction);
                var found = model.NewRecords.ScanServices.Find(x => x.ScanServiceId == id);
                if (found != null)
                {
                    var foundPackage = await this.unitOfWork.AdmissionPackage.FindAsync(x =>
                    x.AdmissionId == (model.IsAdmission ? (int?)model.NewRecords.AdmissionId : null)
                    && x.AppointmentId == (model.IsAdmission ? null : (int?)model.NewRecords.AdmissionId) && x.PackageId == found.AdmissionPackageId,
                    transaction);

                    unitChangedScanService.Add(found.ScanServiceId);
                    order.Unit = found.Unit;
                    order.UsedQuantity = found.UsedQuantity;
                    order.UsedCost = found.UsedCost;
                    order.PackageModuleDetailId = found.PackageModuleDetailId;
                    order.AdmissionPackageId = foundPackage != null ? (int?)foundPackage.AdmissionPackageId : null;
                }
                else
                {
                    order.Active = false;
                }
                order.ModifiedDate = DateTime.Now;
                order.ModifiedBy = model.ModifiedBy;

                var updateResponse = await this.unitOfWork.ScanService.UpdateAsync(order, transaction);
                if (updateResponse > 0) continue;

                transaction.Rollback();
                return (-1, new List<int?>());
            }

            // Deleted Labs
            var unitChangedLabRecords = new List<int?>();
            foreach (var id in model.DeletedLabServices)
            {
                var order = await this.unitOfWork.LabServices.FindAsync(x => x.LabServicesId == id, transaction);
                var found = model.NewRecords.LabServices.Find(x => x.LabServicesId == id);
                if (found != null)
                {
                    var foundPackage = await this.unitOfWork.AdmissionPackage.FindAsync(x =>
                    x.AdmissionId == (model.IsAdmission ? (int?)model.NewRecords.AdmissionId : null)
                    && x.AppointmentId == (model.IsAdmission ? null : (int?)model.NewRecords.AdmissionId) && x.PackageId == found.AdmissionPackageId,
                    transaction);

                    unitChangedRecords.Add(found.LabServicesId);
                    order.Unit = found.Unit;
                    order.UsedQuantity = found.UsedQuantity;
                    order.UsedCost = found.UsedCost;
                    order.PackageModuleDetailId = found.PackageModuleDetailId;
                    order.AdmissionPackageId = foundPackage != null ? (int?)foundPackage.AdmissionPackageId : null;
                }
                else
                {
                    order.Active = false;
                }
                order.Active = false;
                order.ModifiedDate = DateTime.Now;
                order.ModifiedBy = model.ModifiedBy;

                var updateResponse = await this.unitOfWork.LabServices.UpdateAsync(order, transaction);
                if (updateResponse > 0) continue;

                transaction.Rollback();
                return (-1, new List<int?>());
            }

            if (model.DeletedLabServices.Count > 0)
            {
                var labOrder = await this.unitOfWork.LabServices.FindAsync(x => x.LabServicesId == model.DeletedLabServices[0], transaction);
                var labServices = model.IsAdmission ? await this.unitOfWork.LabServices.FindAllAsync(x => x.AdmissionId == labOrder.AdmissionId, transaction) as List<LabServices> : await this.unitOfWork.LabServices.FindAllAsync(x => x.AppointmentId == labOrder.AppointmentId, transaction) as List<LabServices>;
                if (labServices.Count > 0 && labServices.FindAll(x => x.Active).Count == 0)
                {
                    model.DeletedLabBookingHeaderIds = new List<int>();
                    var labBookingHeaders = model.IsAdmission ? await this.unitOfWork.NewLabBookingHeaders.FindAllAsync(x => x.AdmissionId == labOrder.AdmissionId, transaction) as List<NewLabBookingHeader> : await this.unitOfWork.NewLabBookingHeaders.FindAllAsync(x => x.AppointmentId == labOrder.AppointmentId, transaction) as List<NewLabBookingHeader>;
                    foreach (var item in labBookingHeaders)
                    {
                        model.DeletedLabBookingHeaderIds.Add((int)item.NewLabBookingHeaderId);
                    }
                }
                else
                {
                    model.DeletedLabBookingHeaderIds = new List<int>();
                }
            }

            foreach (var id in model.DeletedLabBookingHeaderIds)
            {
                var bookingHeaderDetail = await this.unitOfWork.NewLabBookingHeaders.FindAsync(m => m.NewLabBookingHeaderId == id);
                if (bookingHeaderDetail == null)
                {
                    return (-1, new List<int?>());
                }

                bookingHeaderDetail.Active = false;
                bookingHeaderDetail.ModifiedBy = model.ModifiedBy;
                bookingHeaderDetail.ModifiedDate = DateTime.Now;

                var updateResponse = await this.unitOfWork.NewLabBookingHeaders.UpdateAsync(bookingHeaderDetail, transaction);
                if (updateResponse > 0) continue;

                transaction.Rollback();
                return (-1, new List<int?>());
            }

            // Deleted Packages
            foreach (var id in model.DeletedPackages)
            {
                var package = await this.unitOfWork.AdmissionPackage.FindAsync(x => x.AdmissionPackageId == id, transaction);
                package.Active = false;
                package.ModifiedDate = DateTime.Now;
                package.ModifiedBy = model.ModifiedBy;

                if(package.CounsellingId != null)
                {
                    var counselling = await this.unitOfWork.Counsellings.FindAsync(x => x.CounsellingId == package.CounsellingId, transaction);
                    counselling.IsInUse = false;
                    var counsellingUpdate = await this.unitOfWork.Counsellings.UpdateAsync(counselling, transaction);
                    if(counsellingUpdate <= 0)
                    {
                        return (-1, new List<int?>());
                    }
                    package.CounsellingId = null;
                }

                var updateResponse = await this.unitOfWork.AdmissionPackage.UpdateAsync(package, transaction);
                if (updateResponse > 0) continue;

                transaction.Rollback();
                return (-1, new List<int?>());
            }

            var admissionPackageId = 0;

            // New Package
            if (model.NewRecords.Packages.Count > 0)
            {
                var admissionPackage = new AdmissionPackage
                {
                    Active = true,
                    CreatedDate = DateTime.Now,
                    CreatedBy = model.NewRecords.CreatedBy,
                    AdmissionId = model.IsAdmission ? (int?)model.NewRecords.AdmissionId : null,
                    AppointmentId = !model.IsAdmission ? (int?)model.NewRecords.AdmissionId : null,
                    PackageId = model.NewRecords.Packages.FirstOrDefault()
                };

                admissionPackage.AdmissionPackageId = await this.unitOfWork.AdmissionPackage.InsertAsync(admissionPackage, transaction);
                admissionPackageId = admissionPackage.AdmissionPackageId;
                if (admissionPackageId == 0)
                {
                    transaction.Rollback();
                    return (-1, new List<int?>());
                }
            } else
            {
                model.NewRecords.Packages = new List<int>();
                var admissionPackage = !model.IsAdmission ? await this.unitOfWork.AdmissionPackage.FindAsync(x => x.AppointmentId == model.NewRecords.AdmissionId)
                    : await this.unitOfWork.AdmissionPackage.FindAsync(x => x.AdmissionId == model.NewRecords.AdmissionId);
                if(admissionPackage != null)
                {
                    admissionPackageId = admissionPackage.AdmissionPackageId;
                    model.NewRecords.Packages.Add(admissionPackage.PackageId);
                }
            }

            // New Services
            var records = model.NewRecords.Records.Where(x => !unitChangedRecords.Contains(x.ServiceOrderId ?? 0)).Select(record => new ServiceOrder
            {
                Active = true,
                CreatedDate = DateTime.Now,
                CreatedBy = model.NewRecords.CreatedBy,
                AdmissionId = model.IsAdmission ? (int?)model.NewRecords.AdmissionId : null,
                AppointmentId = !model.IsAdmission ? (int?)model.NewRecords.AdmissionId : null,
                Cost = record.Cost,
                ChargeId = record.ChargeId,
                Unit = record.Unit,
                ChargeTypeMainId = record.ChargeTypeMainId,
                ChargeTypeId = record.ChargeTypeId,
                Notes = record.Notes,
                AdmissionPackageId = model.NewRecords.Packages.Contains(record.AdmissionPackageId ?? 0) && admissionPackageId > 0 ? (int?)admissionPackageId : null,
                PackageModuleDetailId = record.PackageModuleDetailId,
                UsedCost = record.UsedCost,
                UsedQuantity = record.UsedQuantity,
                DiscountType = record.DiscountType,
                DiscountPercentage = record.DiscountPercentage ?? 0,
                DiscountAmount = record.DiscountAmount ?? 0,
                Discount = ((record.DiscountPercentage ?? 0) > 0 || (record.DiscountAmount ?? 0) > 0) ? (record.Discount ?? 0) : 0,
            }).ToList();

            if (records.Any())
            {

                    var response = await this.unitOfWork.ServiceOrder.BulkInsertAsync(records, transaction);
                    if (response == 0)
                    {
                        transaction.Rollback();
                        return (-1, new List<int?>());
                    }

            }

            // Surgery Services
            var SurgeryServiceRecords = model.NewRecords.SurgeryServices.Where(x => !unitChangedSurgeryService.Contains(x.SurgeryServiceId ?? 0)).Select(record => new SurgeryService
            {
                Active = true,
                CreatedDate = DateTime.Now,
                CreatedBy = model.NewRecords.CreatedBy,
                AdmissionId = model.IsAdmission ? (int?)model.NewRecords.AdmissionId : null,
                AppointmentId = !model.IsAdmission ? (int?)model.NewRecords.AdmissionId : null,
                Cost = record.Cost,
                ChargeCategoryId = record.ChargeCategoryId,
                Unit = record.Unit,
                Notes = record.Notes,
                AdmissionPackageId = admissionPackageId > 0 ? (int?)admissionPackageId : null,
                PackageModuleDetailId = record.PackageModuleDetailId,
                UsedCost = record.UsedCost,
                UsedQuantity = record.UsedQuantity,
                SurgeryId = record.SurgeryId
            }).ToList();

            if (SurgeryServiceRecords.Any())
            {
                var SurgeryServiceResponse = await this.unitOfWork.SurgeryService.BulkInsertAsync(SurgeryServiceRecords, transaction);
                if (SurgeryServiceResponse == 0)
                {
                    transaction.Rollback();
                    return (-1, new List<int?>());
                }

            }

            // Scan Services
            var ScanServiceRecords = model.NewRecords.ScanServices.Where(x => !unitChangedScanService.Contains(x.ScanServiceId ?? 0)).Select(record => new ScanService
            {
                Active = true,
                CreatedDate = DateTime.Now,
                CreatedBy = model.NewRecords.CreatedBy,
                AdmissionId = model.IsAdmission ? (int?)model.NewRecords.AdmissionId : null,
                AppointmentId = !model.IsAdmission ? (int?)model.NewRecords.AdmissionId : null,
                Cost = record.Cost,
                ChargeCategoryId = record.ChargeCategoryId,
                Unit = record.Unit,
                Notes = record.Notes,
                AdmissionPackageId = admissionPackageId > 0 ? (int?)admissionPackageId : null,
                PackageModuleDetailId = record.PackageModuleDetailId,
                UsedCost = record.UsedCost,
                UsedQuantity = record.UsedQuantity,
                ScanTestMasterId = record.ScanTestMasterId
            }).ToList();

            if (ScanServiceRecords.Any())
            {
                var ScanServiceResponse = await this.unitOfWork.ScanService.BulkInsertAsync(ScanServiceRecords, transaction);
                if (ScanServiceResponse == 0)
                {
                    transaction.Rollback();
                    return (-1, new List<int?>());
                }

            }

            transaction.Commit();
            return (1, unitChangedLabRecords);

        }

        public async Task<Admission.ViewModel> GetAdmissionBasicAsync(FilterModel model)
        {
            var query = model.IsAdmission
                ? $@"SELECT
                                p.""PatientId"",
                                p.""FullName"",
                                p.""FatherOrHusband"",
	                            p.""Gender"",
	                            p.""ThumbnailUrl"",
	                            p.""Guid""::text,
	                            p.""Age"",
	                            p.""UMRNo"",
                                p.""Mobile"",
	                            a.""AdmissionNo"",
	                            b.""BedNumber"",
	                            r.""RoomName"",
	                            w.""WardName"",
	                            d.""DepartmentName"",
	                            a.""AdmissionDate"",
	                            a.""ExpectedDischargeDate"",
                                dr.""DischargeDate"",
                                dr.""DischargeTime"",
                                a.""AdmissionTime"",
	                            a.""AttendantName"",
	                            a.""AttendantContactNo"",
	                            a.""AttendantRelationWithPatient"",
	                            a.""IsDischarged"",
                                a.""TpaId"",
                                a.""PatientOrganization"",
	                            pr.""ProviderId"",
                                c.""ChargeCategoryId"",
                                c.""ChargeCategoryName"",
                                APT.""AdmissionPayTypeName"",
	                            pr.""FullName"" AS ""ProviderName"",
	                            pr.""Gender"" AS ""ProviderGender"",
                                 P.""StreetAddress"",P.""AddressLine2"",P.""City"",P.""State"",P.""Zipcode"",F.""FloorName"",L.""Name"" as ""LocationName""
                            FROM

                                ""Admission""

                                A JOIN ""Patient"" P ON P.""PatientId"" = A.""PatientId""

                                JOIN ""Provider"" pr on pr.""ProviderId"" = a.""ProviderId""

                                LEFT JOIN ""Department"" d on d.""DepartmentId"" = a.""DepartmentId""

                                LEFT JOIN ""Bed"" b on b.""BedId"" = a.""BedId""

                                LEFT JOIN ""Room"" r on r.""RoomId"" = b.""RoomId""

                                LEFT JOIN ""ChargeCategory"" c on c.""ChargeCategoryId"" = r.""ChargeCategoryId""

                                LEFT JOIN ""AdmissionPayType"" APT on APT.""AdmissionPayTypeId"" = A.""AdmissionPayTypeId""

                                LEFT JOIN ""Ward"" w on w.""WardId"" = r.""WardId""

                                LEFT JOIN ""Floor"" F on F.""FloorId"" =w.""FloorId""
                                
                                LEFT JOIN ""Location"" L on L.""LocationId""=F.""LocationId""

                                LEFT JOIN ""Discharge"" dr on dr.""AdmissionId"" = a.""AdmissionId""

                                WHERE a.""AdmissionId"" = {model.AdmissionId}
            "
                : $@"SELECT
                                p.""PatientId"",
                                p.""FullName"",
                                p.""FatherOrHusband"",
	                            p.""Gender"",
	                            p.""ThumbnailUrl"",
	                            p.""Guid""::text,
	                            p.""Age"",
	                            p.""UMRNo"",
                                a.""Status"",
                                p.""Mobile"",
	                            a.""AppointmentNo"" ""AdmissionNo"",
	                            d.""DepartmentName"",
	                            a.""AppointmentDate"" ""AdmissionDate"",
	                            a.""AppointmentTime"" ""AdmissionTime"",
	                            pr.""ProviderId"",
                                c.""ChargeCategoryId"",
                                c.""ChargeCategoryName"",
	                            pr.""FullName"" AS ""ProviderName"",
	                            pr.""Gender"" AS ""ProviderGender"",
                                P.""StreetAddress"",P.""AddressLine2"",P.""City"",P.""State"",P.""Zipcode""
                            FROM
	                            ""Appointment""
	                            A JOIN ""Patient"" P ON P.""PatientId"" = A.""PatientId""
	                            JOIN ""Provider"" pr on pr.""ProviderId"" = a.""ProviderId""
                                JOIN ""ChargeCategory"" c on c.""ChargeCategoryId"" = c.""ChargeCategoryId""
	                            LEFT JOIN ""Department"" d on d.""DepartmentId"" = a.""DepartmentId""
                                WHERE a.""AppointmentId"" = {model.AdmissionId} and c.""Default"" is true";

            var response = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<Admission.ViewModel>(query);
            return response;
        }


        /// <inheritdoc />
        public async Task<string> FetchPatientNameAsync(IdModel model)
        {
            var where = "where 1=1";
            var condition = model.IsAdmission ? $@" join ""Admission"" A on A.""PatientId"" = P.""PatientId""" : $@" join ""Appointment"" A on A.""PatientId"" = P.""PatientId""";

            where += model.IsAdmission ? $@" and  A.""AdmissionId"" = {model.Id}" : $@" and A.""AppointmentId"" = {model.Id}";

            var query = $@"select P.""FullName""
                             from ""Patient"" P
                            {condition}
                            {where}";

            return this.unitOfWork.Current.QueryFirstOrDefault<string>(query);
        }

        public async Task<IEnumerable<ViewModel>> FindAdmissionAsync(FilterModel model)
        {
            var query = "";
            if (model.AdmissionIds.Count <= 0)
            {
                query = $@"SELECT ""AdmissionId"",count(*) from ""ServiceOrder""
                             group by ""AdmissionId""";
            }
            else
            {
                query = $@"SELECT ""AdmissionId"",count(*) from ""ServiceOrder""
                            where ""AdmissionId"" IN ({string.Join(",", model.AdmissionIds.Select(x => x).ToArray())}) group by ""AdmissionId""";
            }
            var records = await this.unitOfWork.Current.QueryAsync<ViewModel>(query);
            return records;
        }

        public async Task<string> CheckPackagesAsync(IdRequest model)
        {
            var query = $@"select * from ""AdmissionPackage"" A
                           join ""Appointment"" AP on AP.""AppointmentId""=A.""AppointmentId""
                           where AP.""PatientId""={model.Id}";

            return this.unitOfWork.Current.QueryFirstOrDefault<string>(query);
        }

        public async Task<IEnumerable<PackageModuleModel>> FetchPreviousPackagesAsync(StringIdRequest model)
        {
            var aptJoin = @"join ""Appointment"" APT on APT.""AppointmentId"" = AP.""AppointmentId""";
            var admJoin = @"join ""Admission"" APT on APT.""AdmissionId"" = AP.""AdmissionId""";
            var join = (bool)model.IsAdmission ? admJoin : aptJoin;

            var subQueryTable = (bool)model.IsAdmission ? @"""Admission"" A" : @"""Appointment"" A";
            var subQueryCondition = (bool)model.IsAdmission ? $@"A.""AdmissionId""={model.MainId}" : $@"A.""AppointmentId""={model.MainId}";
            var subQuery = $@"select ""PatientId"" from {subQueryTable} where {subQueryCondition}";

            var aptWhere = $@" AND apt.""AppointmentId"" NOT IN ({model.MainId}) ";
            var admWhere = $@" AND apt.""AdmissionId"" NOT IN ({model.MainId}) ";
            var idWhere = (bool)model.IsAdmission ? admWhere : aptWhere;

            var query = $@"SELECT
	                        ap.""AdmissionPackageId"",
	                        p.""PackageModuleId"",
	                        p.""LocationId"",
	                        p.""ChargeModuleTemplateId"",
	                        p.""ModulesMasterIds"",
	                        p.""PackageName"",
	                        p.""ModuleTypeId"",
	                        PTL.""Name"" AS ""ModuleTypeName"",
	                        p.""Quantity"",
	                        p.""FreeQuantity"",
	                        p.""ExpiresIn"",
	                        0 AS ""Total"",
                            ap.""Active"",
                            ap.""CreatedDate"",
                            crt.""FullName"" AS ""CreatedByName"",
							ap.""ModifiedDate"",
                            mdf.""FullName"" AS ""ModifiedByName"",
                            ap.""AdmissionId"",
                            ap.""AppointmentId""
                        FROM
                            ""AdmissionPackage"" ap
                            {join}
	                        JOIN ""PackageModule"" p on p.""PackageModuleId"" = ap.""PackageId""
	                        JOIN ""LookupValue"" PTL on PTL.""LookupValueId"" = p.""ModuleTypeId""
                            JOIN ""Account"" crt ON crt.""AccountId"" = ap.""CreatedBy""
                            LEFT JOIN ""Account"" mdf ON mdf.""AccountId"" = ap.""ModifiedBy""
                        WHERE APT.""PatientId""=({subQuery}) {idWhere} and AP.""Active"" IS TRUE and ""IsPackageComplete"" IS FALSE ";

            var records = await this.unitOfWork.Current.QueryAsync<PackageModuleModel>(query);
            return records;
        }
    }
}
