/*
 * 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.web.struts;

import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.kuali.kfs.kns.question.ConfirmationQuestion;
import org.kuali.kfs.kns.service.DocumentHelperService;
import org.kuali.kfs.kns.web.struts.form.KualiDocumentFormBase;
import org.kuali.kfs.krad.service.KualiRuleService;
import org.kuali.kfs.krad.util.GlobalVariables;
import org.kuali.kfs.krad.util.KRADConstants;
import org.kuali.kfs.krad.util.ObjectUtils;
import org.kuali.kfs.krad.util.UrlFactory;
import org.kuali.kfs.module.purap.PurapConstants;
import org.kuali.kfs.module.purap.PurapConstants.CorrectionReceivingDocumentStrings;
import org.kuali.kfs.module.purap.PurapConstants.PREQDocumentsStrings;
import org.kuali.kfs.module.purap.PurapKeyConstants;
import org.kuali.kfs.module.purap.PurapPropertyConstants;
import org.kuali.kfs.module.purap.businessobject.LineItemReceivingItem;
import org.kuali.kfs.module.purap.document.LineItemReceivingDocument;
import org.kuali.kfs.module.purap.document.PurchaseOrderDocument;
import org.kuali.kfs.module.purap.document.ReceivingDocument;
import org.kuali.kfs.module.purap.document.service.PurchaseOrderService;
import org.kuali.kfs.module.purap.document.service.ReceivingService;
import org.kuali.kfs.module.purap.document.validation.event.AddReceivingItemEvent;
import org.kuali.kfs.module.purap.util.ReceivingQuestionCallback;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.KFSKeyConstants;
import org.kuali.kfs.sys.KFSPropertyConstants;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.rice.core.api.config.property.ConfigurationService;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.kew.api.exception.WorkflowException;
import org.kuali.rice.kim.api.KimConstants;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.List;
import java.util.Properties;

public class LineItemReceivingAction extends ReceivingBaseAction {

    @Override
    protected void createDocument(KualiDocumentFormBase kualiDocumentFormBase) throws WorkflowException {

        super.createDocument(kualiDocumentFormBase);

        LineItemReceivingForm rlf = (LineItemReceivingForm) kualiDocumentFormBase;
        LineItemReceivingDocument rlDoc = (LineItemReceivingDocument) rlf.getDocument();

        //set identifier from form value
        rlDoc.setPurchaseOrderIdentifier(rlf.getPurchaseOrderId());

        rlDoc.initiateDocument();

    }

    public ActionForward continueReceivingLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
        LineItemReceivingForm rlf = (LineItemReceivingForm) form;
        LineItemReceivingDocument rlDoc = (LineItemReceivingDocument) rlf.getDocument();

        GlobalVariables.getMessageMap().clearErrorPath();
        GlobalVariables.getMessageMap().addToErrorPath(KFSPropertyConstants.DOCUMENT);
        boolean valid = true;
        boolean poNotNull = true;

        //check for a po id
        if (ObjectUtils.isNull(rlDoc.getPurchaseOrderIdentifier())) {
            GlobalVariables.getMessageMap().putError(PurapPropertyConstants.PURCHASE_ORDER_IDENTIFIER, KFSKeyConstants.ERROR_REQUIRED, PREQDocumentsStrings.PURCHASE_ORDER_ID);
            poNotNull = false;
        }

        if (ObjectUtils.isNull(rlDoc.getShipmentReceivedDate())) {
            GlobalVariables.getMessageMap().putError(PurapPropertyConstants.SHIPMENT_RECEIVED_DATE, KFSKeyConstants.ERROR_REQUIRED, PurapConstants.LineItemReceivingDocumentStrings.VENDOR_DATE);
        }

        //exit early as the po is null, no need to proceed further until this is taken care of
        if (poNotNull == false) {
            return mapping.findForward(KFSConstants.MAPPING_BASIC);
        }

        PurchaseOrderDocument po = SpringContext.getBean(PurchaseOrderService.class).getCurrentPurchaseOrder(rlDoc.getPurchaseOrderIdentifier());
        if (ObjectUtils.isNotNull(po)) {
            // TODO figure out a more straightforward way to do this.  ailish put this in so the link id would be set and the perm check would work
            rlDoc.setAccountsPayablePurchasingDocumentLinkIdentifier(po.getAccountsPayablePurchasingDocumentLinkIdentifier());

            //TODO hjs-check to see if user is allowed to initiate doc based on PO sensitive data (add this to all other docs except acm doc)
            if (!SpringContext.getBean(DocumentHelperService.class).getDocumentAuthorizer(rlDoc).isAuthorizedByTemplate(rlDoc, KRADConstants.KNS_NAMESPACE, KimConstants.PermissionTemplateNames.OPEN_DOCUMENT, GlobalVariables.getUserSession().getPrincipalId())) {
                throw buildAuthorizationException("initiate document", rlDoc);
            }
        } else {
            GlobalVariables.getMessageMap().putError(PurapPropertyConstants.PURCHASE_ORDER_IDENTIFIER, PurapKeyConstants.ERROR_PURCHASE_ORDER_NOT_EXIST);
            return mapping.findForward(KFSConstants.MAPPING_BASIC);
        }

        //perform duplicate check
        ActionForward forward = performDuplicateReceivingLineCheck(mapping, form, request, response, rlDoc);
        if (forward != null) {
            return forward;
        }

        if (!SpringContext.getBean(ReceivingService.class).isPurchaseOrderActiveForLineItemReceivingDocumentCreation(rlDoc.getPurchaseOrderIdentifier())) {
            GlobalVariables.getMessageMap().putError(PurapPropertyConstants.PURCHASE_ORDER_IDENTIFIER, PurapKeyConstants.ERROR_RECEIVING_LINE_DOCUMENT_PO_NOT_ACTIVE, rlDoc.getPurchaseOrderIdentifier().toString());
            valid &= false;
        }

        if (SpringContext.getBean(ReceivingService.class).canCreateLineItemReceivingDocument(rlDoc.getPurchaseOrderIdentifier(), rlDoc.getDocumentNumber()) == false) {
            String inProcessDocNum = "";
            List<String> inProcessDocNumbers = SpringContext.getBean(ReceivingService.class).getLineItemReceivingDocumentNumbersInProcessForPurchaseOrder(rlDoc.getPurchaseOrderIdentifier(), rlDoc.getDocumentNumber());
            if (!inProcessDocNumbers.isEmpty()) {    // should not be empty if we reach this point
                inProcessDocNum = inProcessDocNumbers.get(0);
            }
            GlobalVariables.getMessageMap().putError(PurapPropertyConstants.PURCHASE_ORDER_IDENTIFIER, PurapKeyConstants.ERROR_RECEIVING_LINE_DOCUMENT_ACTIVE_FOR_PO, inProcessDocNum, rlDoc.getPurchaseOrderIdentifier().toString());
            valid &= false;
        }

        //populate and save Receiving Line Document from Purchase Order, only if we passed all the rules
        if (valid) {
            SpringContext.getBean(ReceivingService.class).populateAndSaveLineItemReceivingDocument(rlDoc);
        }

        return mapping.findForward(KFSConstants.MAPPING_BASIC);
    }

    public ActionForward createReceivingCorrection(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
        LineItemReceivingForm rlForm = (LineItemReceivingForm) form;
        LineItemReceivingDocument document = (LineItemReceivingDocument) rlForm.getDocument();

        String operation = "AddCorrectionNote ";

        ReceivingQuestionCallback callback = new ReceivingQuestionCallback() {
            public boolean questionComplete = false;
            protected String correctionDocumentnoteText;

            @Override
            public ReceivingDocument doPostQuestion(ReceivingDocument document, String noteText) throws Exception {
                //mark question completed
                this.setQuestionComplete(true);
                this.setCorrectionDocumentCreationNoteText(noteText);
                return document;
            }

            @Override
            public boolean isQuestionComplete() {
                return this.questionComplete;
            }

            @Override
            public void setQuestionComplete(boolean questionComplete) {
                this.questionComplete = questionComplete;
            }

            @Override
            public String getCorrectionDocumentCreationNoteText() {
                return correctionDocumentnoteText;
            }

            @Override
            public void setCorrectionDocumentCreationNoteText(String noteText) {
                correctionDocumentnoteText = noteText;
            }
        };

        //ask question
        ActionForward forward = askQuestionWithInput(mapping, form, request, response, CorrectionReceivingDocumentStrings.NOTE_QUESTION, CorrectionReceivingDocumentStrings.NOTE_PREFIX, operation, PurapKeyConstants.MESSAGE_RECEIVING_CORRECTION_NOTE, callback);

        //if question asked is complete, then route
        if (callback.isQuestionComplete()) {

            //set parameters
            String basePath = getApplicationBaseUrl();
            String methodToCallDocHandler = "docHandler";
            String methodToCallReceivingCorrection = "initiate";

            Properties parameters = new Properties();
            parameters.put(KFSConstants.DISPATCH_REQUEST_PARAMETER, methodToCallDocHandler);
            parameters.put(KFSConstants.PARAMETER_COMMAND, methodToCallReceivingCorrection);
            parameters.put(KFSConstants.DOCUMENT_TYPE_NAME, "RCVC");
            parameters.put("receivingLineDocId", document.getDocumentHeader().getDocumentNumber());
            parameters.put(PurapConstants.CorrectionReceivingDocumentStrings.CORRECTION_RECEIVING_CREATION_NOTE_PARAMETER, callback.getCorrectionDocumentCreationNoteText());

            //create url
            String receivingCorrectionUrl = UrlFactory.parameterizeUrl(basePath + "/" + "purapCorrectionReceiving.do", parameters);
            //create forward
            forward = new ActionForward(receivingCorrectionUrl, true);
        }

        return forward;

    }

    public ActionForward clearInitFields(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
        LineItemReceivingForm rlForm = (LineItemReceivingForm) form;
        LineItemReceivingDocument rlDocument = (LineItemReceivingDocument) rlForm.getDocument();
        rlDocument.clearInitFields(rlForm.isFromPurchaseOrder());

        return mapping.findForward(KFSConstants.MAPPING_BASIC);
    }

    protected ActionForward performDuplicateReceivingLineCheck(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, LineItemReceivingDocument lineItemReceivingDocument) throws Exception {
        ActionForward forward = null;
        HashMap<String, String> duplicateMessages = SpringContext.getBean(ReceivingService.class).receivingLineDuplicateMessages(lineItemReceivingDocument);
        if (duplicateMessages != null && !duplicateMessages.isEmpty()) {
            Object question = request.getParameter(KFSConstants.QUESTION_INST_ATTRIBUTE_NAME);
            if (question == null) {

                return this.performQuestionWithoutInput(mapping, form, request, response, PurapConstants.LineItemReceivingDocumentStrings.DUPLICATE_RECEIVING_LINE_QUESTION, duplicateMessages.get(PurapConstants.LineItemReceivingDocumentStrings.DUPLICATE_RECEIVING_LINE_QUESTION), KFSConstants.CONFIRMATION_QUESTION, KFSConstants.ROUTE_METHOD, "");
            }

            Object buttonClicked = request.getParameter(KFSConstants.QUESTION_CLICKED_BUTTON);
            if ((PurapConstants.LineItemReceivingDocumentStrings.DUPLICATE_RECEIVING_LINE_QUESTION.equals(question)) && ConfirmationQuestion.NO.equals(buttonClicked)) {
                forward = mapping.findForward(KFSConstants.MAPPING_BASIC);
            }
        }

        return forward;
    }

    /**
     * Add a new item to the document.
     *
     * @param mapping  An ActionMapping
     * @param form     An ActionForm
     * @param request  The HttpServletRequest
     * @param response The HttpServletResponse
     * @return An ActionForward
     * @throws Exception
     */
    public ActionForward addItem(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
        LineItemReceivingForm lineItemReceivingForm = (LineItemReceivingForm) form;
        LineItemReceivingItem item = lineItemReceivingForm.getNewLineItemReceivingItemLine();
        LineItemReceivingDocument lineItemReceivingDocument = (LineItemReceivingDocument) lineItemReceivingForm.getDocument();

        boolean rulePassed = SpringContext.getBean(KualiRuleService.class).applyRules(new AddReceivingItemEvent(PurapPropertyConstants.NEW_LINE_ITEM_RECEIVING_ITEM_LINE, lineItemReceivingDocument, item));
        if (rulePassed) {
            lineItemReceivingForm.setHideAddUnorderedItem(true); // hide the add unordered item line once an item is added
            item = lineItemReceivingForm.getAndResetNewReceivingItemLine();
            lineItemReceivingDocument.addItem(item);
            //TODO: we need to set the line number correctly to match up to PO
        }

        return mapping.findForward(KFSConstants.MAPPING_BASIC);
    }

    /**
     * For each item, it's quantity received value is set to zero.
     *
     * @param mapping
     * @param form
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    public ActionForward clearQty(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
        LineItemReceivingForm lineItemReceivingForm = (LineItemReceivingForm) form;

        LineItemReceivingDocument lineItemReceivingDocument = (LineItemReceivingDocument) lineItemReceivingForm.getDocument();

        for (LineItemReceivingItem item : (List<LineItemReceivingItem>) lineItemReceivingDocument.getItems()) {
            item.setItemReceivedTotalQuantity(KualiDecimal.ZERO);
        }

        return mapping.findForward(KFSConstants.MAPPING_BASIC);
    }

    /**
     * For each item, loads total order quantity minus prior received quantity into total received quantity.
     *
     * @param mapping
     * @param form
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    public ActionForward loadQty(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
        LineItemReceivingForm lineItemReceivingForm = (LineItemReceivingForm) form;

        LineItemReceivingDocument lineItemReceivingDocument = (LineItemReceivingDocument) lineItemReceivingForm.getDocument();

        for (LineItemReceivingItem item : (List<LineItemReceivingItem>) lineItemReceivingDocument.getItems()) {
            if (item.isOrderedItem()) {
                if (item.getItemOrderedQuantity().subtract(item.getItemReceivedPriorQuantity()).isGreaterEqual(KualiDecimal.ZERO)) {
                    item.setItemReceivedTotalQuantity(item.getItemOrderedQuantity().subtract(item.getItemReceivedPriorQuantity()));
                } else {
                    item.setItemReceivedTotalQuantity(KualiDecimal.ZERO);
                }
            }
        }

        return mapping.findForward(KFSConstants.MAPPING_BASIC);
    }

    /**
     * Gives a warning before showing the add new unordered line item; if the user confirms the action, proceeds;
     * otherwise cancels the action and returns to the current LineItemReceivingDocument.
     *
     * @param mapping  An ActionMapping
     * @param form     An ActionForm
     * @param request  The HttpServletRequest
     * @param response The HttpServletResponse
     * @return An ActionForward
     * @throws Exception
     */
    public ActionForward showAddUnorderedItem(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
        LineItemReceivingForm lineItemReceivingForm = (LineItemReceivingForm) form;

        boolean shouldGiveWarning = lineItemReceivingForm.shouldGiveAddUnorderedItemWarning();
        if (!shouldGiveWarning) {
            lineItemReceivingForm.setHideAddUnorderedItem(false);
            return mapping.findForward(KFSConstants.MAPPING_BASIC);
        }

        String msgkey = PurapKeyConstants.WARNING_RECEIVING_LINEITEM_ADD_UNORDERED;
        String msgtxt = SpringContext.getBean(ConfigurationService.class).getPropertyValueAsString(msgkey);
        Object question = request.getParameter(KFSConstants.QUESTION_INST_ATTRIBUTE_NAME);

        if (question == null) {
            return performQuestionWithoutInput(mapping, form, request, response, msgkey, msgtxt, KFSConstants.CONFIRMATION_QUESTION, "showAddUnorderedItem", "");
        }

        Object buttonClicked = request.getParameter(KFSConstants.QUESTION_CLICKED_BUTTON);
        if ((msgkey.equals(question)) && ConfirmationQuestion.YES.equals(buttonClicked)) {
            lineItemReceivingForm.setHideAddUnorderedItem(false);
        }

        return mapping.findForward(KFSConstants.MAPPING_BASIC);
    }

}
