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

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.kuali.kfs.kns.util.KNSGlobalVariables;
import org.kuali.kfs.kns.web.struts.form.KualiForm;
import org.kuali.kfs.krad.document.Document;
import org.kuali.kfs.krad.service.BusinessObjectService;
import org.kuali.kfs.krad.util.GlobalVariables;
import org.kuali.kfs.krad.util.ObjectUtils;
import org.kuali.kfs.module.tem.TemConstants;
import org.kuali.kfs.module.tem.TemConstants.TravelDocTypes;
import org.kuali.kfs.module.tem.TemConstants.TravelReimbursementStatusCodeKeys;
import org.kuali.kfs.module.tem.TemKeyConstants;
import org.kuali.kfs.module.tem.TemPropertyConstants;
import org.kuali.kfs.module.tem.TemWorkflowConstants;
import org.kuali.kfs.module.tem.document.TravelAuthorizationDocument;
import org.kuali.kfs.module.tem.document.TravelReimbursementDocument;
import org.kuali.kfs.module.tem.document.service.TravelDocumentService;
import org.kuali.kfs.module.tem.document.service.TravelReimbursementService;
import org.kuali.kfs.module.tem.document.web.struts.TravelReimbursementForm;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.rice.kim.api.identity.Person;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Travel Reimbursement Document Presentation Controller
 */
public class TravelReimbursementDocumentPresentationController extends TravelDocumentPresentationController {

    public static Logger LOG = Logger.getLogger(TravelReimbursementDocumentPresentationController.class);

    protected volatile static BusinessObjectService businessObjectService;

    /**
     * @see org.kuali.kfs.sys.document.authorization.FinancialSystemTransactionalDocumentPresentationControllerBase#getEditModes(org.kuali.rice.kns.document.Document)
     */
    @Override
    public Set<String> getEditModes(Document document) {
        Set<String> editModes = super.getEditModes(document);
        addFullEntryEditMode(document, editModes);
        editModes.remove(TemConstants.EditModes.CHECK_AMOUNT_ENTRY);  // the check amount cannot be edited on travel reimbursements
        editModes.remove(TemConstants.EditModes.BLANKET_TRAVEL_ENTRY);
        editModes.remove(TemConstants.EditModes.BLANKET_TRAVEL_VIEW);
        final Set<String> nodeNames = document.getDocumentHeader().getWorkflowDocument().getNodeNames();
        if (document.getDocumentHeader().getWorkflowDocument().isInitiated() || document.getDocumentHeader().getWorkflowDocument().isSaved() || (nodeNames != null && !nodeNames.isEmpty() && (nodeNames.contains(TemWorkflowConstants.RouteNodeNames.TAX) || nodeNames.contains(TemWorkflowConstants.RouteNodeNames.AP_TRAVEL)))) {
            editModes.add(TemConstants.EditModes.EXPENSE_TAXABLE_MODE);
        }

        if (document.getDocumentHeader().getWorkflowDocument().isInitiated() || document.getDocumentHeader().getWorkflowDocument().isSaved() || (nodeNames != null && !nodeNames.isEmpty() && nodeNames.contains(TemWorkflowConstants.RouteNodeNames.AP_TRAVEL))) {
            editModes.add(TemConstants.EditModes.EXPENSE_LIMIT_ENTRY);
        }

        return editModes;
    }

    /**
     * @see org.kuali.kfs.sys.document.authorization.FinancialSystemTransactionalDocumentPresentationControllerBase#getDocumentActions(org.kuali.rice.kns.document.Document)
     */
    @Override
    public Set<String> getDocumentActions(Document document) {
        TravelReimbursementDocument tr = (TravelReimbursementDocument) document;
        TravelAuthorizationDocument ta = null;
        ta = getTravelDocumentService().findCurrentTravelAuthorization(tr);

        if (ta != null) {
            if (ta.getDelinquentAction() != null && ta.getDelinquentAction().equals(TemConstants.DELINQUENT_STOP) && !ta.getDelinquentTRException()) {
                throw new DocumentInitiationException(TemKeyConstants.ERROR_AUTHORIZATION_TR_DELINQUENT, new String[]{TravelDocTypes.TRAVEL_REIMBURSEMENT_DOCUMENT}, true);
            }
        }

        Set<String> actions = super.getDocumentActions(document);
        if (canNewReimbursement(tr)) {
            actions.add(TemConstants.TravelAuthorizationActions.CAN_NEW_REIMBURSEMENT);
        }

        return actions;
    }

    /**
     * Determines if a reimbursement can be initiated for this document. This is done for {@link TravelReimbursementDocument} instances
     * that have a workflow document status of FINAL or PROCESSED and on documents that have a workflow
     * App Doc Status of DEPT_APPROVED. Also checks if the person has permission to initiate the target documents.
     * Will not show the new Reimbursement link if the TR already has a TR enroute.
     * Will not show the new Reimbursement link if the TR is not the root document.
     * Will not show the new Reimbursement link if a TA is required to initiate a TR.
     * Will not show the new Reimbursement link if all the Trip Types require a TA to initiate a TR.
     *
     * @param document
     * @return
     */
    public boolean canNewReimbursement(TravelReimbursementDocument document) {
        final String documentType = document.getDocumentTypeName();

        final String appDocStatus = document.getApplicationDocumentStatus();
        boolean statusCheck = (document.getDocumentHeader().getWorkflowDocument().isProcessed() || document.getDocumentHeader().getWorkflowDocument().isFinal())
            && (appDocStatus.equals(TravelReimbursementStatusCodeKeys.DEPT_APPROVED));

        Person user = GlobalVariables.getUserSession().getPerson();
        boolean hasInitAccess = false;
        if (getTemRoleService().canAccessTravelDocument(document, user) && !ObjectUtils.isNull(document.getTraveler()) && document.getTemProfileId() != null && !ObjectUtils.isNull(document.getTemProfile())) {
            //check if user also can init other docs
            hasInitAccess = user.getPrincipalId().equals(document.getTraveler().getPrincipalId()) || getTemRoleService().isTravelDocumentArrangerForProfile(documentType, user.getPrincipalId(), document.getTemProfileId()) || getTemRoleService().isTravelArranger(user, document.getTemProfile().getHomeDepartment(), document.getTemProfileId().toString(), documentType);
        }

        boolean checkRelatedDocs = true;
        boolean originalDocumentCheck = true;
        boolean initiateReimbursementWithoutAuthorization = true;

        if (documentType.equals(TravelDocTypes.TRAVEL_REIMBURSEMENT_DOCUMENT)) {
            //don't display link if there are enroute TRs
            List<Document> docs = getTravelDocumentService().getDocumentsRelatedTo(document, documentType);
            for (Document doc : docs) {
                TravelReimbursementDocument trDoc = (TravelReimbursementDocument) doc;
                if (trDoc.getDocumentHeader().getWorkflowDocument().isEnroute()) {
                    checkRelatedDocs &= false;
                }
            }

            //only allow the link to display if the TR is the root document
            originalDocumentCheck = document.isTripProgenitor();

            //only allow the link to display if a TR can be initiated without a TA
            initiateReimbursementWithoutAuthorization = getConfigurationService().getPropertyValueAsBoolean(TemKeyConstants.CONFIG_PROPERTY_REIMBURSEMENT_INITIATELINK_ENABLED);

            //check Trip Types to verify at least one type can initiate TR without TA
            initiateReimbursementWithoutAuthorization &= !getTravelReimbursementService().doAllReimbursementTripTypesRequireTravelAuthorization();
        }

        return statusCheck && hasInitAccess && checkRelatedDocs && originalDocumentCheck && initiateReimbursementWithoutAuthorization;
    }

    /**
     * @see org.kuali.rice.krad.document.DocumentPresentationControllerBase#canInitiate(java.lang.String)
     */
    @Override
    public boolean canInitiate(String documentTypeName) {
        //only allow if a TR can be initiated without a TA
        boolean initiateReimbursementWithoutAuthorization = getConfigurationService().getPropertyValueAsBoolean(TemKeyConstants.CONFIG_PROPERTY_REIMBURSEMENT_INITIATELINK_ENABLED);
        //check Trip Types to verify at least one type can initiate TR without TA
        initiateReimbursementWithoutAuthorization &= !getTravelReimbursementService().doAllReimbursementTripTypesRequireTravelAuthorization();
        if (!initiateReimbursementWithoutAuthorization) {
            throw new DocumentInitiationException(TemKeyConstants.ERROR_TA_REQUIRED_FOR_TR_INIT, new String[]{}, true);
        }

        KualiForm form = KNSGlobalVariables.getKualiForm();
        if (form instanceof TravelReimbursementForm) {
            final TravelReimbursementForm reimbForm = (TravelReimbursementForm) form;
            if (!StringUtils.isBlank(reimbForm.getTravelDocumentIdentifier())) {
                // we're basing this document off of another document; let's look for any other TR's in the trip to verify they are not enroute
                final List<TravelReimbursementDocument> trDocsInTrip = getTravelReimbursementsInTrip(reimbForm.getTravelDocumentIdentifier());
                if (!trDocsInTrip.isEmpty()) {
                    for (TravelReimbursementDocument trDoc : trDocsInTrip) {
                        if (StringUtils.equals(trDoc.getFinancialSystemDocumentHeader().getFinancialDocumentStatusCode(), KFSConstants.DocumentStatusCodes.ENROUTE)) {
                            throw new DocumentInitiationException(TemKeyConstants.ERROR_TR_ENROUTE_DURING_TR_INIT, new String[]{reimbForm.getTravelDocumentIdentifier(), trDoc.getDocumentNumber()}, true);
                        }
                    }
                }
            }
        }

        return super.canInitiate(documentTypeName);
    }

    /**
     * Look up any TravelReimbursementDocuments associated with the given trip
     *
     * @param tripId the travel document identifier for the trip we're thinking about initiating a new TR for
     * @return a List of any existing TravelReimbursementDocuments in that trip
     */
    protected List<TravelReimbursementDocument> getTravelReimbursementsInTrip(String tripId) {
        Map<String, Object> fieldValues = new HashMap<String, Object>();
        fieldValues.put(TemPropertyConstants.TRAVEL_DOCUMENT_IDENTIFIER, tripId);
        Collection<TravelReimbursementDocument> trDocs = getBusinessObjectService().findMatching(TravelReimbursementDocument.class, fieldValues);
        List<TravelReimbursementDocument> trDocsList = new ArrayList<TravelReimbursementDocument>();
        if (!trDocs.isEmpty()) {
            trDocsList.addAll(trDocs);
        }
        return trDocsList;
    }

    /**
     * @see org.kuali.rice.krad.document.DocumentPresentationControllerBase#canCopy(org.kuali.rice.krad.document.Document)
     */
    @Override
    public boolean canCopy(Document document) {
        boolean canCopy = super.canCopy(document);

        //1. TR has been saved
        if (canCopy) {
            canCopy = !isDocumentInitiated(document);
        }

        //2. TR was not created from a TA
        if (canCopy) {
            canCopy = !isReimbursementChildOfAuthorization(document);
        }

        return canCopy;
    }

    protected boolean isDocumentInitiated(Document document) {
        return document.getDocumentHeader().getWorkflowDocument().isInitiated();
    }

    protected boolean isReimbursementChildOfAuthorization(Document document) {
        TravelReimbursementDocument tr = (TravelReimbursementDocument) document;
        List<TravelAuthorizationDocument> travelAuthorizations = getTravelDocumentService().findAuthorizationDocuments(tr.getTravelDocumentIdentifier());

        if (ObjectUtils.isNotNull(travelAuthorizations) && !travelAuthorizations.isEmpty()) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    protected TravelDocumentService getTravelDocumentService() {
        return SpringContext.getBean(TravelDocumentService.class);
    }

    protected TravelReimbursementService getTravelReimbursementService() {
        return SpringContext.getBean(TravelReimbursementService.class);
    }

    protected BusinessObjectService getBusinessObjectService() {
        if (businessObjectService == null) {
            businessObjectService = SpringContext.getBean(BusinessObjectService.class);
        }
        return businessObjectService;
    }
}
