/*
 * 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.service.impl;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.kuali.kfs.krad.exception.ValidationException;
import org.kuali.kfs.krad.service.DocumentService;
import org.kuali.kfs.krad.util.GlobalVariables;
import org.kuali.kfs.krad.util.ObjectUtils;
import org.kuali.kfs.krad.workflow.service.WorkflowDocumentService;
import org.kuali.kfs.module.purap.PurapConstants;
import org.kuali.kfs.module.purap.PurapKeyConstants;
import org.kuali.kfs.module.purap.document.BulkReceivingDocument;
import org.kuali.kfs.module.purap.document.PurchaseOrderDocument;
import org.kuali.kfs.module.purap.document.dataaccess.BulkReceivingDao;
import org.kuali.kfs.module.purap.document.service.BulkReceivingService;
import org.kuali.kfs.module.purap.document.service.PrintService;
import org.kuali.kfs.module.purap.document.service.PurchaseOrderService;
import org.kuali.kfs.module.purap.document.validation.event.AttributedContinuePurapEvent;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.KFSPropertyConstants;
import org.kuali.rice.core.api.config.property.ConfigurationService;
import org.kuali.rice.kew.api.WorkflowDocument;
import org.kuali.rice.kew.api.exception.WorkflowException;
import org.springframework.transaction.annotation.Transactional;

import java.io.ByteArrayOutputStream;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;

@Transactional
public class BulkReceivingServiceImpl implements BulkReceivingService {

    private static final Logger LOG = Logger.getLogger(BulkReceivingServiceImpl.class);

    protected PurchaseOrderService purchaseOrderService;
    protected BulkReceivingDao bulkReceivingDao;
    protected DocumentService documentService;
    protected WorkflowDocumentService workflowDocumentService;
    protected ConfigurationService configurationService;
    protected PrintService printService;

    public boolean canPrintReceivingTicket(BulkReceivingDocument blkRecDoc) {

        boolean canCreate = false;
        WorkflowDocument workflowDocument = null;

        try {
            workflowDocument = workflowDocumentService.createWorkflowDocument(blkRecDoc.getDocumentHeader().getWorkflowDocument().getDocumentTypeName(), GlobalVariables.getUserSession().getPerson());
        } catch (WorkflowException we) {
            throw new RuntimeException(we);
        }

        if (workflowDocument.isFinal()) {
            canCreate = true;
        }

        return canCreate;
    }

    public void populateAndSaveBulkReceivingDocument(BulkReceivingDocument blkRecDoc)
        throws WorkflowException {
        try {
            documentService.saveDocument(blkRecDoc, AttributedContinuePurapEvent.class);
        } catch (WorkflowException we) {
            String errorMsg = "Error saving document # " + blkRecDoc.getDocumentHeader().getDocumentNumber() + " " + we.getMessage();
            throw new RuntimeException(errorMsg, we);
        }
    }

    public HashMap<String, String> bulkReceivingDuplicateMessages(BulkReceivingDocument blkRecDoc) {
        HashMap<String, String> msgs;
        msgs = new HashMap<String, String>();
        Integer poId = blkRecDoc.getPurchaseOrderIdentifier();
        StringBuffer currentMessage = new StringBuffer("");
        List<String> docNumbers = null;

        //check vendor date for duplicates
        if (blkRecDoc.getShipmentReceivedDate() != null) {
            docNumbers = bulkReceivingDao.duplicateVendorDate(poId, blkRecDoc.getShipmentReceivedDate());
            if (hasDuplicateEntry(docNumbers)) {
                appendDuplicateMessage(currentMessage, PurapKeyConstants.MESSAGE_DUPLICATE_RECEIVING_LINE_VENDOR_DATE, blkRecDoc.getPurchaseOrderIdentifier());
            }
        }

        //check packing slip number for duplicates
        if (!StringUtils.isEmpty(blkRecDoc.getShipmentPackingSlipNumber())) {
            docNumbers = bulkReceivingDao.duplicatePackingSlipNumber(poId, blkRecDoc.getShipmentPackingSlipNumber());
            if (hasDuplicateEntry(docNumbers)) {
                appendDuplicateMessage(currentMessage, PurapKeyConstants.MESSAGE_DUPLICATE_RECEIVING_LINE_PACKING_SLIP_NUMBER, blkRecDoc.getPurchaseOrderIdentifier());
            }
        }

        //check bill of lading number for duplicates
        if (!StringUtils.isEmpty(blkRecDoc.getShipmentBillOfLadingNumber())) {
            docNumbers = bulkReceivingDao.duplicateBillOfLadingNumber(poId, blkRecDoc.getShipmentBillOfLadingNumber());
            if (hasDuplicateEntry(docNumbers)) {
                appendDuplicateMessage(currentMessage, PurapKeyConstants.MESSAGE_DUPLICATE_RECEIVING_LINE_BILL_OF_LADING_NUMBER, blkRecDoc.getPurchaseOrderIdentifier());
            }
        }

        //add message if one exists
        if (currentMessage.length() > 0) {
            //add suffix
            appendDuplicateMessage(currentMessage, PurapKeyConstants.MESSAGE_DUPLICATE_RECEIVING_LINE_SUFFIX, blkRecDoc.getPurchaseOrderIdentifier());

            //add msg to map
            msgs.put(PurapConstants.BulkReceivingDocumentStrings.DUPLICATE_BULK_RECEIVING_DOCUMENT_QUESTION, currentMessage.toString());
        }

        return msgs;
    }

    /**
     * Looks at a list of doc numbers, but only considers an entry duplicate
     * if the document is in a Final status.
     *
     * @param docNumbers
     * @return
     */
    protected boolean hasDuplicateEntry(List<String> docNumbers) {

        boolean isDuplicate = false;
        WorkflowDocument workflowDocument = null;

        for (String docNumber : docNumbers) {

            try {
                workflowDocument = workflowDocumentService.loadWorkflowDocument(docNumber, GlobalVariables.getUserSession().getPerson());
            } catch (WorkflowException we) {
                throw new RuntimeException(we);
            }

            //if the doc number exists, and is in final status, consider this a dupe and return
            if (workflowDocument.isFinal()) {
                isDuplicate = true;
                break;
            }
        }

        return isDuplicate;

    }

    protected void appendDuplicateMessage(StringBuffer currentMessage,
                                          String duplicateMessageKey,
                                          Integer poId) {

        //append prefix if this is first call
        if (currentMessage.length() == 0) {
            String messageText = configurationService.getPropertyValueAsString(PurapKeyConstants.MESSAGE_BULK_RECEIVING_DUPLICATE_PREFIX);
            String prefix = MessageFormat.format(messageText, poId.toString());

            currentMessage.append(prefix);
        }

        //append message
        currentMessage.append(configurationService.getPropertyValueAsString(duplicateMessageKey));
    }

    public String getBulkReceivingDocumentNumberInProcessForPurchaseOrder(Integer poId,
                                                                          String bulkReceivingDocumentNumber) {

        String docNumberInProcess = StringUtils.EMPTY;

        List<String> docNumbers = bulkReceivingDao.getDocumentNumbersByPurchaseOrderId(poId);
        WorkflowDocument workflowDocument = null;

        for (String docNumber : docNumbers) {

            try {
                workflowDocument = workflowDocumentService.loadWorkflowDocument(docNumber,
                    GlobalVariables.getUserSession().getPerson());
            } catch (WorkflowException we) {
                throw new RuntimeException(we);
            }

            if (!(workflowDocument.isCanceled() ||
                workflowDocument.isException() ||
                workflowDocument.isFinal()) &&
                !docNumber.equals(bulkReceivingDocumentNumber)) {

                docNumberInProcess = docNumber;
                break;
            }
        }

        return docNumberInProcess;
    }

    public void populateBulkReceivingFromPurchaseOrder(BulkReceivingDocument blkRecDoc) {

        if (blkRecDoc != null) {
            PurchaseOrderDocument poDoc = purchaseOrderService.getCurrentPurchaseOrder(blkRecDoc.getPurchaseOrderIdentifier());
            if (poDoc != null) {
                blkRecDoc.populateBulkReceivingFromPurchaseOrder(poDoc);
            }
        }

    }

    public BulkReceivingDocument getBulkReceivingByDocumentNumber(String documentNumber) {

        if (ObjectUtils.isNotNull(documentNumber)) {
            try {
                BulkReceivingDocument doc = (BulkReceivingDocument) documentService.getByDocumentHeaderId(documentNumber);
                if (ObjectUtils.isNotNull(doc)) {
                    WorkflowDocument workflowDocument = doc.getDocumentHeader().getWorkflowDocument();
                    doc.refreshReferenceObject(KFSPropertyConstants.DOCUMENT_HEADER);
                    doc.getDocumentHeader().setWorkflowDocument(workflowDocument);
                }
                return doc;
            } catch (WorkflowException e) {
                String errorMessage = "Error getting bulk receiving document from document service";
                throw new RuntimeException(errorMessage, e);
            }
        }
        return null;
    }

    public void performPrintReceivingTicketPDF(String blkDocId,
                                               ByteArrayOutputStream baosPDF) {

        BulkReceivingDocument blkRecDoc = getBulkReceivingByDocumentNumber(blkDocId);
        Collection<String> generatePDFErrors = printService.generateBulkReceivingPDF(blkRecDoc, baosPDF);

        if (!generatePDFErrors.isEmpty()) {
            addStringErrorMessagesToMessageMap(PurapKeyConstants.ERROR_BULK_RECEIVING_PDF, generatePDFErrors);
            throw new ValidationException("printing bulk receiving ticket failed");
        }

    }

    protected void addStringErrorMessagesToMessageMap(String errorKey,
                                                      Collection<String> errors) {

        if (ObjectUtils.isNotNull(errors)) {
            for (String error : errors) {
                LOG.error("Adding error message using error key '" + errorKey + "' with text '" + error + "'");
                GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_ERRORS, errorKey, error);
            }
        }

    }

    public void setPrintService(PrintService printService) {
        this.printService = printService;
    }

    public void setPurchaseOrderService(PurchaseOrderService purchaseOrderService) {
        this.purchaseOrderService = purchaseOrderService;
    }

    public void setBulkReceivingDao(BulkReceivingDao bulkReceivingDao) {
        this.bulkReceivingDao = bulkReceivingDao;
    }

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

    public void setWorkflowDocumentService(WorkflowDocumentService workflowDocumentService) {
        this.workflowDocumentService = workflowDocumentService;
    }

    public void setConfigurationService(ConfigurationService configurationService) {
        this.configurationService = configurationService;
    }
}

