package org.labkey.wnprc_billing.pipeline;

import org.apache.commons.lang3.time.DateUtils;
import org.labkey.api.ehr_billing.pipeline.BillingPipelineJobProcess;
import org.labkey.api.ehr_billing.pipeline.BillingPipelineJobSupport;
import org.labkey.api.ehr_billing.pipeline.InvoicedItemsProcessingService;
import org.labkey.wnprc_billing.WNPRC_BillingSchema;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Implements methods to define the set of queries (and their column mappings) to be included in the Billing Task processing.
 * Rows from the processed queries will be written to the ehr_billing.invoicedItems table.
 */
public class InvoicedItemsProcessingServiceImpl implements InvoicedItemsProcessingService
{
    @Override
    public List<BillingPipelineJobProcess> getProcessList()
    {
        List<BillingPipelineJobProcess> processes = new ArrayList<>();

        BillingPipelineJobProcess perDiemProcess = new BillingPipelineJobProcess("Per Diems", WNPRC_BillingSchema.NAME, "perDiemFeeRates", getPerDiemColMap())
        {
            @Override
            public Map<String, Object> getQueryParams(BillingPipelineJobSupport support)
            {
                Map<String, Object> params = super.getQueryParams(support);
                Long numDays = Math.round(((Long) (support.getEndDate().getTime() - support.getStartDate().getTime())).doubleValue() / DateUtils.MILLIS_PER_DAY);
                numDays++;
                params.put("NumDays", numDays.intValue());
                return params;
            }
        };
        perDiemProcess.setRequiredFields(Arrays.asList("date", "unitCost", "totalcost"));
        processes.add(perDiemProcess);

        BillingPipelineJobProcess proceduresProcess = new BillingPipelineJobProcess("Procedure Fees", WNPRC_BillingSchema.NAME, "procedureFeeRates", getProceduresColMap());
        proceduresProcess.setRequiredFields(Arrays.asList("date", "unitCost", "totalcost"));
        processes.add(proceduresProcess);

        BillingPipelineJobProcess miscChargesProcess = new BillingPipelineJobProcess("Misc Charges", WNPRC_BillingSchema.NAME, "miscChargesFeeRates", getMisChargesColMap());
        miscChargesProcess.setRequiredFields(Arrays.asList("date", "unitCost", "totalcost"));
        miscChargesProcess.setUseEHRContainer(true);
        miscChargesProcess.setMiscCharges(true);
        processes.add(miscChargesProcess);

        return processes;
    }

    private Map<String, String> getPerDiemColMap()
    {
        Map<String, String> colMap = new HashMap<>();
        colMap.put("Id", "Id");
        colMap.put("date", "date");
        colMap.put("project", "project");
        colMap.put("quantity", "quantity");
        colMap.put("unitCost", "unitCost");
        colMap.put("totalcost", "totalcost");
        colMap.put("unitCostDirect", "unitCostDirect");
        colMap.put("totalCostDirect", "totalCostDirect");
        colMap.put("comment", "comment");
        colMap.put("debitedAccount", "debitedAccount");
        colMap.put("chargeId", "chargeId");
        colMap.put("item", "item");
        colMap.put("category", "category");
        colMap.put("serviceCenter", "serviceCenter");
        return colMap;
    }

    private Map<String, String> getProceduresColMap()
    {
        Map<String, String> colMap = new HashMap<>();
        colMap.put("Id", "Id");
        colMap.put("date", "date");
        colMap.put("project", "project");
        colMap.put("quantity", "quantity");
        colMap.put("unitCost", "unitCost");
        colMap.put("totalcost", "totalcost");
        colMap.put("unitCostDirect", "unitCostDirect");
        colMap.put("totalCostDirect", "totalCostDirect");
        colMap.put("sourceRecord", "sourceRecord");
        colMap.put("comment", "comment");
        colMap.put("debitedAccount", "debitedAccount");
        colMap.put("chargeId", "chargeId");
        colMap.put("item", "item");
        colMap.put("category", "category");
        colMap.put("serviceCenter", "serviceCenter");
        return colMap;
    }

    private Map<String, String> getMisChargesColMap()
    {
        Map<String, String> colMap = new HashMap<>();
        colMap.put("Id", "Id");
        colMap.put("date", "date");
        colMap.put("project", "project");
        colMap.put("quantity", "quantity");
        colMap.put("unitCost", "unitCost");
        colMap.put("totalcost", "totalcost");
        colMap.put("unitCostDirect", "unitCostDirect");
        colMap.put("totalCostDirect", "totalCostDirect");
        colMap.put("sourceRecord", "sourceRecord");
        colMap.put("comment", "comment");
        colMap.put("debitedAccount", "debitedAccount");
        colMap.put("chargeId", "chargeId");
        colMap.put("item", "item");
        colMap.put("category", "category");
        colMap.put("serviceCenter", "serviceCenter");
        colMap.put("creditedAccount", "creditedAccount");
        return colMap;
    }

    @Override
    public String getInvoiceNum(Map<String, Object> row, Date billingPeriodEndDate)
    {
        SimpleDateFormat f = new SimpleDateFormat("MMddyyyy");//d = day in a month, D = day in a year, yyyy = calendar year
        String dateStr = f.format(billingPeriodEndDate);
        String debitedAcct = (String)row.getOrDefault("debitedAccount", "Unknown");
        return dateStr + debitedAcct.toUpperCase();
    }
}
@@ -5427,16 +5427,491 @@
 known%22);
+%0A%0A        if (null == debitedAcct)%0A        %7B%0A            Integer project = (Integer) row.get(%22project%22);%0A            String animalId = (String) row.get(%22Id%22);%0A            throw new RuntimeException(%22Unable to complete Billing Run. Debit Account not found for category '%22 + row.get(%22category%22) + %22'%22 +%0A                    ((null != project) ? (%22, where project = %22 + project) : %22%22) +%0A                    ((null != animalId) ? (%22 and animal Id = %22 + animalId) : %22%22));%0A        %7D
 %0A       
@@ -5940,16 +5940,23 @@
 tedAcct.
+trim().
 toUpperC

Support Ticket 37310: Error handling when expected debited account is not found during a billing run.