/*
 * The Kuali Financial System, a comprehensive financial management system for higher education.
 *
 * Copyright 2005-2016 The Kuali Foundation
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.kuali.kfs.module.ar.businessobject.lookup;

import org.apache.commons.lang.StringUtils;
import org.kuali.kfs.kns.web.struts.form.LookupForm;
import org.kuali.kfs.krad.document.Document;
import org.kuali.kfs.krad.service.DocumentService;
import org.kuali.kfs.krad.util.GlobalVariables;
import org.kuali.kfs.krad.util.KRADConstants;
import org.kuali.kfs.module.ar.ArConstants;
import org.kuali.kfs.module.ar.ArPropertyConstants;
import org.kuali.kfs.module.ar.businessobject.ContractsGrantsPaymentHistoryReport;
import org.kuali.kfs.module.ar.businessobject.InvoicePaidApplied;
import org.kuali.kfs.module.ar.document.ContractsGrantsInvoiceDocument;
import org.kuali.kfs.module.ar.document.PaymentApplicationDocument;
import org.kuali.kfs.sys.KFSKeyConstants;
import org.kuali.kfs.sys.KFSPropertyConstants;
import org.kuali.kfs.sys.document.service.FinancialSystemDocumentService;
import org.kuali.kfs.sys.util.KfsDateUtils;
import org.kuali.rice.core.api.datetime.DateTimeService;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.core.web.format.CurrencyFormatter;
import org.kuali.rice.core.web.format.FormatException;
import org.kuali.rice.core.web.format.Formatter;
import org.kuali.rice.kew.api.exception.WorkflowException;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * Defines a custom lookup for the Payment History Report.
 */
public class ContractsGrantsPaymentHistoryReportLookupableHelperServiceImpl extends ContractsGrantsReportLookupableHelperServiceImplBase {
    protected DateTimeService dateTimeService;
    protected DocumentService documentService;
    protected FinancialSystemDocumentService financialSystemDocumentService;

    /**
     * Overridden to validate the invoie amount and payment date fields to make sure they are parsable
     *
     * @see org.kuali.rice.kns.lookup.AbstractLookupableHelperServiceImpl#validateSearchParameters(java.util.Map)
     */
    @Override
    public void validateSearchParameters(Map<String, String> fieldValues) {
        if (!StringUtils.isBlank(fieldValues.get(ArPropertyConstants.PAYMENT_DATE))) {
            validateDateField(fieldValues.get(ArPropertyConstants.PAYMENT_DATE), ArPropertyConstants.PAYMENT_DATE, getDateTimeService());
        }
        if (!StringUtils.isBlank(fieldValues.get(ArPropertyConstants.RANGE_LOWER_BOUND_KEY_PREFIX + ArPropertyConstants.PAYMENT_DATE))) {
            validateDateField(fieldValues.get(ArPropertyConstants.RANGE_LOWER_BOUND_KEY_PREFIX + ArPropertyConstants.PAYMENT_DATE), ArPropertyConstants.RANGE_LOWER_BOUND_KEY_PREFIX + ArPropertyConstants.PAYMENT_DATE, getDateTimeService());
        }
        if (!StringUtils.isBlank(fieldValues.get(ArPropertyConstants.INVOICE_AMOUNT))) {
            try {
                Formatter f = new CurrencyFormatter();
                f.format(fieldValues.get(ArPropertyConstants.INVOICE_AMOUNT));
            } catch (FormatException fe) {
                // we'll assume this was a parse exception
                final String label = getDataDictionaryService().getAttributeLabel(getBusinessObjectClass(), ArPropertyConstants.INVOICE_AMOUNT);
                GlobalVariables.getMessageMap().putError(ArPropertyConstants.INVOICE_AMOUNT, KFSKeyConstants.ERROR_NUMERIC, label);
            }
        }
        super.validateSearchParameters(fieldValues);
    }

    /**
     * This method performs the lookup and returns a collection of lookup items
     *
     * @param lookupForm
     * @param kualiLookupable
     * @param resultTable
     * @param bounded
     * @return
     */
    @Override
    public Collection performLookup(LookupForm lookupForm, Collection resultTable, boolean bounded) {
        Map lookupFormFields = lookupForm.getFieldsForLookup();

        setBackLocation((String) lookupForm.getFieldsForLookup().get(KRADConstants.BACK_LOCATION));
        setDocFormKey((String) lookupForm.getFieldsForLookup().get(KRADConstants.DOC_FORM_KEY));

        Collection<ContractsGrantsPaymentHistoryReport> displayList = new ArrayList<ContractsGrantsPaymentHistoryReport>();

        Map<String, String> invoiceAppliedLookupFields = new HashMap<>();
        invoiceAppliedLookupFields.put(ArPropertyConstants.CUSTOMER_INVOICE_DOCUMENT + "." + KFSPropertyConstants.DOCUMENT_HEADER + "." + KFSPropertyConstants.WORKFLOW_DOCUMENT_TYPE_NAME, ArConstants.ArDocumentTypeCodes.CONTRACTS_GRANTS_INVOICE);
        if (lookupFormFields.containsKey(ArPropertyConstants.PAYMENT_NUMBER)) {
            invoiceAppliedLookupFields.put(KFSPropertyConstants.DOCUMENT_NUMBER, (String) lookupFormFields.get(ArPropertyConstants.PAYMENT_NUMBER));
        }
        if (lookupFormFields.containsKey(ArPropertyConstants.CustomerFields.CUSTOMER_NUMBER)) {
            invoiceAppliedLookupFields.put(ArPropertyConstants.CUSTOMER_INVOICE_DOCUMENT + "." + ArPropertyConstants.CustomerFields.CUSTOMER_NUMBER, (String) lookupFormFields.get(ArPropertyConstants.CustomerFields.CUSTOMER_NUMBER));
        }
        if (lookupFormFields.containsKey(ArPropertyConstants.PAYMENT_AMOUNT)) {
            invoiceAppliedLookupFields.put(ArPropertyConstants.CustomerInvoiceDetailFields.INVOICE_ITEM_APPLIED_AMOUNT, (String) lookupFormFields.get(ArPropertyConstants.PAYMENT_AMOUNT));
        }
        if (lookupFormFields.containsKey(ArPropertyConstants.INVOICE_NUMBER)) {
            invoiceAppliedLookupFields.put(ArPropertyConstants.CustomerInvoiceDocumentFields.FINANCIAL_DOCUMENT_REF_INVOICE_NUMBER, (String) lookupFormFields.get(ArPropertyConstants.INVOICE_NUMBER));
        }

        Collection<InvoicePaidApplied> invoicePaidApplieds = getLookupService().findCollectionBySearchHelper(InvoicePaidApplied.class, invoiceAppliedLookupFields, true);

        // build search result fields
        // For each Cash Control doc, get a list of payment app doc numbers
        try {
            for (InvoicePaidApplied invoicePaidApplied : invoicePaidApplieds) {
                boolean useInvoicePaidApplied = true;
                final Document doc = getDocumentService().getByDocumentHeaderId(invoicePaidApplied.getDocumentNumber());
                if (doc instanceof PaymentApplicationDocument) {
                    final PaymentApplicationDocument paymentApp = (PaymentApplicationDocument) doc;
                    if (getFinancialSystemDocumentService().getUnsuccessfulDocumentStatuses().contains(paymentApp.getFinancialSystemDocumentHeader().getWorkflowDocumentStatusCode())) {
                        useInvoicePaidApplied = false;
                    }
                    if (!StringUtils.isBlank((String) lookupFormFields.get(ArPropertyConstants.APPLIED_INDICATOR))) {
                        final String appliedIndicator = (String) lookupFormFields.get(ArPropertyConstants.APPLIED_INDICATOR);
                        if (KRADConstants.YES_INDICATOR_VALUE.equals(appliedIndicator) && !getFinancialSystemDocumentService().getSuccessfulDocumentStatuses().contains(paymentApp.getFinancialSystemDocumentHeader().getWorkflowDocumentStatusCode())) {
                            useInvoicePaidApplied = false;
                        } else if (KRADConstants.NO_INDICATOR_VALUE.equals(appliedIndicator) && !getFinancialSystemDocumentService().getPendingDocumentStatuses().contains(paymentApp.getFinancialSystemDocumentHeader().getWorkflowDocumentStatusCode())) {
                            useInvoicePaidApplied = false;
                        }
                    }
                    final java.util.Date paymentAppFinalDate = paymentApp.getDocumentHeader().getWorkflowDocument().getDateFinalized().toDate();
                    if (!StringUtils.isBlank((String) lookupFormFields.get(ArPropertyConstants.PAYMENT_DATE))) {
                        final java.util.Date toPaymentDate = getDateTimeService().convertToDate((String) lookupFormFields.get(ArPropertyConstants.PAYMENT_DATE));
                        if (!KfsDateUtils.isSameDay(paymentAppFinalDate, toPaymentDate) && toPaymentDate.before(paymentAppFinalDate)) {
                            useInvoicePaidApplied = false;
                        }
                    }
                    if (!StringUtils.isBlank((String) lookupFormFields.get(ArPropertyConstants.RANGE_LOWER_BOUND_KEY_PREFIX + ArPropertyConstants.PAYMENT_DATE))) {
                        final java.util.Date fromPaymentDate = getDateTimeService().convertToDate((String) lookupFormFields.get(ArPropertyConstants.RANGE_LOWER_BOUND_KEY_PREFIX + ArPropertyConstants.PAYMENT_DATE));
                        if (!KfsDateUtils.isSameDay(paymentAppFinalDate, fromPaymentDate) && fromPaymentDate.after(paymentAppFinalDate)) {
                            useInvoicePaidApplied = false;
                        }
                    }

                    final ContractsGrantsInvoiceDocument cgInvoiceDocument = getBusinessObjectService().findBySinglePrimaryKey(ContractsGrantsInvoiceDocument.class, invoicePaidApplied.getFinancialDocumentReferenceInvoiceNumber());
                    if (!StringUtils.isBlank((String) lookupFormFields.get(ArPropertyConstants.INVOICE_AMOUNT))) {
                        final KualiDecimal invoiceAmount = new KualiDecimal((String) lookupFormFields.get(ArPropertyConstants.INVOICE_AMOUNT));
                        if (!invoiceAmount.equals(cgInvoiceDocument.getTotalDollarAmount())) {
                            useInvoicePaidApplied = false;
                        }
                    }
                    if (!StringUtils.isBlank((String) lookupFormFields.get(KFSPropertyConstants.AWARD_NUMBER))) {
                        if (!StringUtils.equals(cgInvoiceDocument.getInvoiceGeneralDetail().getAward().getProposalNumber().toString(), (String) lookupFormFields.get(KFSPropertyConstants.AWARD_NUMBER))) {
                            useInvoicePaidApplied = false;
                        }
                    }
                    if (!StringUtils.isBlank((String) lookupFormFields.get(ArPropertyConstants.REVERSED_INDICATOR))) {
                        final String reversedIndicator = (String) lookupFormFields.get(ArPropertyConstants.REVERSED_INDICATOR);
                        if (KRADConstants.YES_INDICATOR_VALUE.equals(reversedIndicator) && !cgInvoiceDocument.isInvoiceReversal()) {
                            useInvoicePaidApplied = false;
                        } else if (KRADConstants.NO_INDICATOR_VALUE.equals(reversedIndicator) && cgInvoiceDocument.isInvoiceReversal()) {
                            useInvoicePaidApplied = false;
                        }
                    }

                    if (useInvoicePaidApplied) {
                        ContractsGrantsPaymentHistoryReport cgPaymentHistoryReport = new ContractsGrantsPaymentHistoryReport();

                        cgPaymentHistoryReport.setPaymentNumber(invoicePaidApplied.getDocumentNumber());
                        cgPaymentHistoryReport.setPaymentAmount(invoicePaidApplied.getInvoiceItemAppliedAmount());
                        cgPaymentHistoryReport.setInvoiceNumber(invoicePaidApplied.getFinancialDocumentReferenceInvoiceNumber());

                        cgPaymentHistoryReport.setPaymentDate(new java.sql.Date(paymentApp.getDocumentHeader().getWorkflowDocument().getDateFinalized().getMillis()));
                        cgPaymentHistoryReport.setAppliedIndicator(getFinancialSystemDocumentService().getSuccessfulDocumentStatuses().contains(paymentApp.getFinancialSystemDocumentHeader().getWorkflowDocumentStatusCode()));

                        cgPaymentHistoryReport.setAwardNumber(cgInvoiceDocument.getInvoiceGeneralDetail().getProposalNumber());
                        cgPaymentHistoryReport.setReversedIndicator(cgInvoiceDocument.isInvoiceReversal());
                        cgPaymentHistoryReport.setCustomerNumber(cgInvoiceDocument.getCustomerNumber());
                        cgPaymentHistoryReport.setCustomerName(cgInvoiceDocument.getCustomer().getCustomerName());
                        cgPaymentHistoryReport.setInvoiceAmount(cgInvoiceDocument.getTotalDollarAmount());

                        displayList.add(cgPaymentHistoryReport);
                    }
                }
            }
            buildResultTable(lookupForm, displayList, resultTable);
        } catch (WorkflowException we) {
            throw new RuntimeException("Could not open payment application document related to search", we);
        } catch (ParseException pe) {
            throw new RuntimeException("I tried to validate the date and amount fields related to search, I really did.  But...I guess I didn't try hard enough", pe);
        }
        return displayList;
    }

    public DateTimeService getDateTimeService() {
        return dateTimeService;
    }

    public void setDateTimeService(DateTimeService dateTimeService) {
        this.dateTimeService = dateTimeService;
    }

    public DocumentService getDocumentService() {
        return documentService;
    }

    public void setDocumentService(DocumentService documentService) {
        this.documentService = documentService;
    }

    public FinancialSystemDocumentService getFinancialSystemDocumentService() {
        return financialSystemDocumentService;
    }

    public void setFinancialSystemDocumentService(FinancialSystemDocumentService financialSystemDocumentService) {
        this.financialSystemDocumentService = financialSystemDocumentService;
    }

}
