/*
 * 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.purap.document.authorization;

import org.apache.commons.lang.StringUtils;
import org.kuali.kfs.coreservice.framework.parameter.ParameterService;
import org.kuali.kfs.krad.document.Document;
import org.kuali.kfs.krad.util.GlobalVariables;
import org.kuali.kfs.krad.util.ObjectUtils;
import org.kuali.kfs.module.purap.PurapAuthorizationConstants.PaymentRequestEditMode;
import org.kuali.kfs.module.purap.PurapConstants;
import org.kuali.kfs.module.purap.PurapConstants.PaymentRequestStatuses;
import org.kuali.kfs.module.purap.PurapParameterConstants;
import org.kuali.kfs.module.purap.businessobject.PaymentRequestItem;
import org.kuali.kfs.module.purap.document.PaymentRequestDocument;
import org.kuali.kfs.module.purap.document.service.PurapService;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.KfsAuthorizationConstants;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.kfs.sys.service.FinancialSystemWorkflowHelperService;
import org.kuali.kfs.sys.service.impl.KfsParameterConstants;
import org.kuali.rice.kew.api.WorkflowDocument;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;


public class PaymentRequestDocumentPresentationController extends PurchasingAccountsPayableDocumentPresentationController {

    Boolean canHold;
    Boolean canRequestCancel;
    Boolean canEditPreExtraction;

    @Override
    public boolean canSave(Document document) {
        PaymentRequestDocument paymentRequestDocument = (PaymentRequestDocument) document;

        if (StringUtils.equals(paymentRequestDocument.getApplicationDocumentStatus(), PaymentRequestStatuses.APPDOC_INITIATE)) {
            return false;
        }

        if (canEditPreExtraction(paymentRequestDocument)) {
            return true;
        }

        return super.canSave(document);
    }

    @Override
    public boolean canReload(Document document) {
        PaymentRequestDocument paymentRequestDocument = (PaymentRequestDocument) document;

        if (StringUtils.equals(paymentRequestDocument.getApplicationDocumentStatus(), PaymentRequestStatuses.APPDOC_INITIATE)) {
            return false;
        }

        if (canEditPreExtraction(paymentRequestDocument)) {
            return true;
        }

        return super.canReload(document);
    }

    @Override
    public boolean canCancel(Document document) {
        //controlling the cancel button through getExtraButtons in PaymentRequestForm
        return false;
    }

    @Override
    public boolean canApprove(Document document) {
        PaymentRequestDocument paymentRequestDocument = (PaymentRequestDocument) document;

        if (paymentRequestDocument.isPaymentRequestedCancelIndicator() || paymentRequestDocument.isHoldIndicator()) {
            return false;
        }

        return super.canApprove(document);
    }

    @Override
    public boolean canDisapprove(Document document) {
        //disapprove is never allowed for PREQ
        return false;
    }

    /**
     * @see org.kuali.rice.kns.document.authorization.DocumentPresentationControllerBase#canEdit(org.kuali.rice.kns.document.Document)
     */
    @Override
    public boolean canEdit(Document document) {
        PaymentRequestDocument paymentRequestDocument = (PaymentRequestDocument) document;
        boolean fullDocEntryCompleted = SpringContext.getBean(PurapService.class).isFullDocumentEntryCompleted(paymentRequestDocument);

        if (SpringContext.getBean(FinancialSystemWorkflowHelperService.class).isAdhocApprovalRequestedForPrincipal(document.getDocumentHeader().getWorkflowDocument(), GlobalVariables.getUserSession().getPrincipalId())) {
            return false;
        }
        // if the hold or cancel indicator is true, don't allow editing
        if (paymentRequestDocument.isHoldIndicator() || paymentRequestDocument.isPaymentRequestedCancelIndicator()) {
            return false;
        }
        if (fullDocEntryCompleted) {
            //  after fullDocEntry is completed, only fiscal officer reviewers can edit
            if (paymentRequestDocument.isDocumentStoppedInRouteNode(PaymentRequestStatuses.NODE_ACCOUNT_REVIEW)) {
                return true;
            }
            return false;
        } else {
            //before fullDocEntry is completed, document can be edited (could be preroute or enroute)
            return true;
        }
    }

    /**
     * @see org.kuali.rice.kns.document.authorization.TransactionalDocumentPresentationControllerBase#getEditModes(org.kuali.rice.kns.document.Document)
     */
    @Override
    public Set<String> getEditModes(Document document) {
        Set<String> editModes = super.getEditModes(document);

        PaymentRequestDocument paymentRequestDocument = (PaymentRequestDocument) document;

        if (canProcessorCancel(paymentRequestDocument)) {
            editModes.add(PaymentRequestEditMode.ACCOUNTS_PAYABLE_PROCESSOR_CANCEL);
        }

        if (canManagerCancel(paymentRequestDocument)) {
            editModes.add(PaymentRequestEditMode.ACCOUNTS_PAYABLE_MANAGER_CANCEL);
        }

        if (canHold(paymentRequestDocument)) {
            editModes.add(PaymentRequestEditMode.HOLD);
        }

        if (canRequestCancel(paymentRequestDocument)) {
            editModes.add(PaymentRequestEditMode.REQUEST_CANCEL);
        }

        if (canRemoveHold(paymentRequestDocument)) {
            editModes.add(PaymentRequestEditMode.REMOVE_HOLD);
        }

        if (canRemoveRequestCancel(paymentRequestDocument)) {
            editModes.add(PaymentRequestEditMode.REMOVE_REQUEST_CANCEL);
        }

        if (canProcessorInit(paymentRequestDocument)) {
            editModes.add(PaymentRequestEditMode.DISPLAY_INIT_TAB);
        }

        if (ObjectUtils.isNotNull(paymentRequestDocument.getVendorHeaderGeneratedIdentifier())) {
            editModes.add(PaymentRequestEditMode.LOCK_VENDOR_ENTRY);
        }

        if (SpringContext.getBean(PurapService.class).isFullDocumentEntryCompleted(paymentRequestDocument)) {
            editModes.add(PaymentRequestEditMode.FULL_DOCUMENT_ENTRY_COMPLETED);
        } else if (ObjectUtils.isNotNull(paymentRequestDocument.getPurchaseOrderDocument()) && PurapConstants.PurchaseOrderStatuses.APPDOC_OPEN.equals(paymentRequestDocument.getPurchaseOrderDocument().getApplicationDocumentStatus())) {
            editModes.add(PaymentRequestEditMode.ALLOW_CLOSE_PURCHASE_ORDER);
        }

        //FIXME hjs: alter to restrict what AP shouldn't be allowed to edit
        if (canEditPreExtraction(paymentRequestDocument)) {
            editModes.add(PaymentRequestEditMode.EDIT_PRE_EXTRACT);
        }

        // See if purap tax is enabled
        boolean salesTaxInd = SpringContext.getBean(ParameterService.class).getParameterValueAsBoolean(KfsParameterConstants.PURCHASING_DOCUMENT.class, PurapParameterConstants.ENABLE_SALES_TAX_IND);
        if (salesTaxInd) {
            editModes.add(PaymentRequestEditMode.PURAP_TAX_ENABLED);

            if (paymentRequestDocument.isUseTaxIndicator()) {
                // if use tax, don't allow editing of tax fields
                editModes.add(PaymentRequestEditMode.LOCK_TAX_AMOUNT_ENTRY);
            } else {
                // display the "clear all taxes" button if doc is not using use tax
                editModes.add(PaymentRequestEditMode.CLEAR_ALL_TAXES);

            }
        }

        // tax area tab is editable while waiting for tax review
        if (paymentRequestDocument.isDocumentStoppedInRouteNode(PaymentRequestStatuses.NODE_VENDOR_TAX_REVIEW)) {
            editModes.add(PaymentRequestEditMode.TAX_AREA_EDITABLE);
        }
        /*
        if (PaymentRequestStatuses.AWAITING_TAX_REVIEW.equals(paymentRequestDocument.getStatusCode())) {
            editModes.add(PaymentRequestEditMode.TAX_AREA_EDITABLE);
        }
        */

        // the tax tab is viewable to everyone after tax is approved
        if (PaymentRequestStatuses.APPDOC_DEPARTMENT_APPROVED.equals(paymentRequestDocument.getApplicationDocumentStatus()) &&
            // if and only if the preq has gone through tax review would TaxClassificationCode be non-empty
            !StringUtils.isEmpty(paymentRequestDocument.getTaxClassificationCode())) {
            editModes.add(PaymentRequestEditMode.TAX_INFO_VIEWABLE);
        }

        if (paymentRequestDocument.isDocumentStoppedInRouteNode(PaymentRequestStatuses.NODE_ACCOUNT_REVIEW)) {
            // remove FULL_ENTRY because FO cannot edit rest of doc; only their own acct lines
            editModes.add(PaymentRequestEditMode.RESTRICT_FISCAL_ENTRY);

            // only do line item check if the hold/cancel indicator is false, otherwise document editing should be turned off.
            if (!paymentRequestDocument.isHoldIndicator() && !paymentRequestDocument.isPaymentRequestedCancelIndicator()) {
                List lineList = new ArrayList();
                for (Iterator iter = paymentRequestDocument.getItems().iterator(); iter.hasNext(); ) {
                    PaymentRequestItem item = (PaymentRequestItem) iter.next();
                    lineList.addAll(item.getSourceAccountingLines());
                    // If FO has deleted the last accounting line for an item, set entry mode to full so they can add another one
                    if (item.getItemType().isLineItemIndicator() && item.getSourceAccountingLines().size() == 0) {
                        editModes.add(KfsAuthorizationConstants.TransactionalEditMode.EXPENSE_ENTRY);
                    }
                }
            }
        }

        // Remove editBank edit mode if the document has been extracted
        if (paymentRequestDocument.isExtracted()) {
            editModes.remove(KFSConstants.BANK_ENTRY_EDITABLE_EDITING_MODE);
        }

        addACHSignUpInfoMode(document, editModes);

        return editModes;
    }

    protected void addACHSignUpInfoMode(Document document, Set<String> editModes) {
        WorkflowDocument workflowDocument = document.getDocumentHeader().getWorkflowDocument();
        // If document is not final, processed or disapproved, then we can show ACH signup flag.
        if (!(workflowDocument.isFinal() ||
            workflowDocument.isProcessed() ||
            workflowDocument.isDisapproved()))
            editModes.add(PaymentRequestEditMode.ACH_ACCOUNT_INFO_DISPLAYED);
    }

    protected boolean canProcessorInit(PaymentRequestDocument paymentRequestDocument) {
        // if Payment Request is in INITIATE status or NULL returned from getAppDocStatus
        String status = paymentRequestDocument.getApplicationDocumentStatus();
        if (StringUtils.equals(status, PaymentRequestStatuses.APPDOC_INITIATE)) {
            return true;
        }
        return false;
    }


    protected boolean canProcessorCancel(PaymentRequestDocument paymentRequestDocument) {
        // if Payment Request is in INITIATE status, user cannot cancel doc
        if (canProcessorInit(paymentRequestDocument)) {
            return false;
        }

        String docStatus = paymentRequestDocument.getApplicationDocumentStatus();
        boolean requestCancelIndicator = paymentRequestDocument.getPaymentRequestedCancelIndicator();
        boolean holdIndicator = paymentRequestDocument.isHoldIndicator();
        boolean extracted = paymentRequestDocument.isExtracted();

        boolean preroute =
            PaymentRequestStatuses.APPDOC_IN_PROCESS.equals(docStatus) ||
                PaymentRequestStatuses.APPDOC_AWAITING_ACCOUNTS_PAYABLE_REVIEW.equals(docStatus);
        boolean enroute =
            PaymentRequestStatuses.APPDOC_AWAITING_SUB_ACCT_MGR_REVIEW.equals(docStatus) ||
                PaymentRequestStatuses.APPDOC_AWAITING_FISCAL_REVIEW.equals(docStatus) ||
                PaymentRequestStatuses.APPDOC_AWAITING_ORG_REVIEW.equals(docStatus) ||
                PaymentRequestStatuses.APPDOC_AWAITING_TAX_REVIEW.equals(docStatus);
        boolean postroute =
            PaymentRequestStatuses.APPDOC_DEPARTMENT_APPROVED.equals(docStatus) ||
                PaymentRequestStatuses.APPDOC_AUTO_APPROVED.equals(docStatus);

        boolean can = false;
        if (PaymentRequestStatuses.STATUSES_PREROUTE.contains(docStatus)) {
            can = true;
        } else if (PaymentRequestStatuses.STATUSES_ENROUTE.contains(docStatus)) {
            can = requestCancelIndicator;
        } else if (PaymentRequestStatuses.STATUSES_POSTROUTE.contains(docStatus)) {
            can = !requestCancelIndicator && !holdIndicator && !extracted;
        }

        return can;
    }

    protected boolean canManagerCancel(PaymentRequestDocument paymentRequestDocument) {
        // if Payment Request is in INITIATE status, user cannot cancel doc
        if (canProcessorInit(paymentRequestDocument)) {
            return false;
        }

        String docStatus = paymentRequestDocument.getApplicationDocumentStatus();
        boolean requestCancelIndicator = paymentRequestDocument.getPaymentRequestedCancelIndicator();
        boolean holdIndicator = paymentRequestDocument.isHoldIndicator();
        boolean extracted = paymentRequestDocument.isExtracted();

        boolean preroute =
            PaymentRequestStatuses.APPDOC_IN_PROCESS.equals(docStatus) ||
                PaymentRequestStatuses.APPDOC_AWAITING_ACCOUNTS_PAYABLE_REVIEW.equals(docStatus);
        boolean enroute =
            PaymentRequestStatuses.APPDOC_AWAITING_SUB_ACCT_MGR_REVIEW.equals(docStatus) ||
                PaymentRequestStatuses.APPDOC_AWAITING_FISCAL_REVIEW.equals(docStatus) ||
                PaymentRequestStatuses.APPDOC_AWAITING_ORG_REVIEW.equals(docStatus) ||
                PaymentRequestStatuses.APPDOC_AWAITING_TAX_REVIEW.equals(docStatus);
        boolean postroute =
            PaymentRequestStatuses.APPDOC_DEPARTMENT_APPROVED.equals(docStatus) ||
                PaymentRequestStatuses.APPDOC_AUTO_APPROVED.equals(docStatus);

        boolean can = false;
        if (PaymentRequestStatuses.STATUSES_PREROUTE.contains(docStatus) ||
            PaymentRequestStatuses.STATUSES_ENROUTE.contains(docStatus)) {
            can = true;
        } else if (PaymentRequestStatuses.STATUSES_POSTROUTE.contains(docStatus)) {
            can = !requestCancelIndicator && !holdIndicator && !extracted;
        }

        return can;
    }

    /**
     * Determines whether the PaymentRequest Hold button shall be available. Conditions:
     * - Payment Request is not already on hold, and
     * - Payment Request is not already being requested to be canceled, and
     * - Payment Request has not already been extracted to PDP, and
     * - Payment Request status is not in the list of "STATUSES_DISALLOWING_HOLD" or document is being adhoc routed; and
     *
     * @return True if the document state allows placing the Payment Request on hold.
     */
    protected boolean canHold(PaymentRequestDocument paymentRequestDocument) {
        if (canHold == null) {

            boolean can = !paymentRequestDocument.isHoldIndicator() && !paymentRequestDocument.isPaymentRequestedCancelIndicator() && !paymentRequestDocument.isExtracted();
            if (can) {
                can = SpringContext.getBean(FinancialSystemWorkflowHelperService.class).isAdhocApprovalRequestedForPrincipal(paymentRequestDocument.getFinancialSystemDocumentHeader().getWorkflowDocument(), GlobalVariables.getUserSession().getPrincipalId());
                can = can || !PaymentRequestStatuses.STATUSES_DISALLOWING_HOLD.contains(paymentRequestDocument.getApplicationDocumentStatus());
            }
            canHold = can;
        }

        return canHold;
    }

    /**
     * Determines whether the Request Cancel PaymentRequest button shall be available. Conditions:
     * - Payment Request is not already on hold, and
     * - Payment Request is not already being requested to be canceled, and
     * - Payment Request has not already been extracted to PDP, and
     * - Payment Request status is not in the list of "STATUSES_DISALLOWING_REQUEST_CANCEL" or document is being adhoc routed; and
     *
     * @return True if the document state allows placing the request that the Payment Request be canceled.
     */
    protected boolean canRequestCancel(PaymentRequestDocument paymentRequestDocument) {
        if (canRequestCancel == null) {
            boolean can = !paymentRequestDocument.isPaymentRequestedCancelIndicator() && !paymentRequestDocument.isHoldIndicator() && !paymentRequestDocument.isExtracted();
            if (can) {
                can = SpringContext.getBean(FinancialSystemWorkflowHelperService.class).isAdhocApprovalRequestedForPrincipal(paymentRequestDocument.getFinancialSystemDocumentHeader().getWorkflowDocument(), GlobalVariables.getUserSession().getPrincipalId());
                can = can || !PaymentRequestStatuses.STATUSES_DISALLOWING_REQUEST_CANCEL.contains(paymentRequestDocument.getApplicationDocumentStatus());
            }
            canRequestCancel = can;
        }
        return canRequestCancel;
    }

    /**
     * Determines whether the Remove Hold button shall be available. Conditions:
     * - the hold indicator is set to true
     * <p>
     * Because the state of the Payment Request cannot be changed while the document is on hold,
     * we should not have to check the state of the document to remove the hold.
     * For example, the document should not be allowed to be approved or extracted while on hold.
     *
     * @return True if the document state allows removing the Payment Request from hold.
     */
    protected boolean canRemoveHold(PaymentRequestDocument paymentRequestDocument) {
        return paymentRequestDocument.isHoldIndicator();
    }

    /**
     * Determines whether the Remove Request Cancel button shall be available. Conditions:
     * - the request cancel indicator is set to true;  and
     * <p>
     * Because the state of the Payment Request cannot be changed while the document is set to request cancel,
     * we should not have to check the state of the document to remove the request cancel.
     * For example, the document should not be allowed to be approved or extracted while set to request cancel.
     *
     * @return True if the document state allows removing a request that the Payment Request be canceled.
     */
    protected boolean canRemoveRequestCancel(PaymentRequestDocument paymentRequestDocument) {
        return paymentRequestDocument.isPaymentRequestedCancelIndicator();
    }

    protected boolean canEditPreExtraction(PaymentRequestDocument paymentRequestDocument) {
        if (canEditPreExtraction == null) {
            boolean can = (!paymentRequestDocument.isExtracted() &&
                !SpringContext.getBean(FinancialSystemWorkflowHelperService.class).isAdhocApprovalRequestedForPrincipal(paymentRequestDocument.getFinancialSystemDocumentHeader().getWorkflowDocument(), GlobalVariables.getUserSession().getPrincipalId()) &&
                !PurapConstants.PaymentRequestStatuses.CANCELLED_STATUSES.contains(paymentRequestDocument.getApplicationDocumentStatus()));
            canEditPreExtraction = can;
        }
        return canEditPreExtraction;
    }

}
