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

import org.apache.commons.lang.StringUtils;
import org.kuali.kfs.fp.document.DisbursementVoucherConstants;
import org.kuali.kfs.fp.document.DisbursementVoucherDocument;
import org.kuali.kfs.krad.util.GlobalVariables;
import org.kuali.kfs.krad.util.ObjectUtils;
import org.kuali.kfs.sys.businessobject.AccountingLine;
import org.kuali.kfs.sys.document.authorization.AccountingDocumentAuthorizerBase;
import org.kuali.kfs.sys.identity.KfsKimAttributes;
import org.kuali.rice.kew.api.WorkflowDocument;
import org.kuali.rice.kim.api.KimConstants;
import org.kuali.rice.kim.api.identity.Person;

import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Adds extra role qualifiers for funky travel edit mode permission
 */
public class DisbursementVoucherDocumentAuthorizer extends AccountingDocumentAuthorizerBase {

    /**
     * Adds chart codes and account numbers for accounting lines if we're at Account level, so that the fiscal officer gets travel edit mode
     *
     * @see org.kuali.kfs.sys.document.authorization.AccountingDocumentAuthorizerBase#addRoleQualification(org.kuali.rice.krad.bo.BusinessObject, java.util.Map)
     */
    @Override
    protected void addRoleQualification(Object dataObject, Map<String, String> attributes) {
        super.addRoleQualification(dataObject, attributes);
        final DisbursementVoucherDocument disbursementVoucherDocument = (DisbursementVoucherDocument) dataObject;

        // are we add Account level?  Then let's add our qualifiers
        if (isAtAccountLevel(disbursementVoucherDocument)) {
            addAccountQualification(getAccountingLines(disbursementVoucherDocument), attributes);
        }

        // add campus code if we have one
        if (!StringUtils.isBlank(disbursementVoucherDocument.getCampusCode())) {
            attributes.put(KimConstants.AttributeConstants.CAMPUS_CODE, disbursementVoucherDocument.getCampusCode());
        }
    }

    /**
     * Finds the source accounting lines in the given business object
     *
     * @param disbursementVoucherDocument a document to get accounting lines from
     * @return a List of accounting lines
     */
    protected List<? extends AccountingLine> getAccountingLines(DisbursementVoucherDocument disbursementVoucherDocument) {
        return disbursementVoucherDocument.getSourceAccountingLines();
    }

    /**
     * Goes through the given List of accounting lines and fines one line where the current user is the fiscal officer; it uses that line to put chart of accounts
     * code and account number qualifications into the given Map of attributes for role qualification
     *
     * @param accountingLines a List of AccountingLines
     * @param attributes      a Map of role qualification attributes
     */
    protected void addAccountQualification(List<? extends AccountingLine> accountingLines, Map<String, String> attributes) {
        final Person currentUser = GlobalVariables.getUserSession().getPerson();
        boolean foundQualification = false;
        int count = 0;
        while (!foundQualification && count < accountingLines.size()) {
            AccountingLine accountingLine = accountingLines.get(count);
            if (ObjectUtils.isNull(accountingLine.getAccount())) {
                accountingLine.refreshReferenceObject("account");
            }
            if (!ObjectUtils.isNull(accountingLine.getAccount()) && currentUser.getPrincipalId().equalsIgnoreCase(accountingLine.getAccount().getAccountFiscalOfficerSystemIdentifier())) {
                attributes.put(KfsKimAttributes.CHART_OF_ACCOUNTS_CODE, accountingLine.getChartOfAccountsCode());
                attributes.put(KfsKimAttributes.ACCOUNT_NUMBER, accountingLine.getAccountNumber());
                foundQualification = true;
            }
            count += 1;
        }
    }

    /**
     * A helper method for determining the route levels for a given document.
     *
     * @param workflowDocument
     * @return List
     */
    protected Set<String> getCurrentRouteLevels(WorkflowDocument workflowDocument) {
        return workflowDocument.getCurrentNodeNames();
    }

    /**
     * Determines if the document is at the Account route level
     *
     * @param disbursementVoucherDocument the Disbursement Voucher document to determine the account level of
     * @return true if the document is at the account level, false otherwise
     */
    protected boolean isAtAccountLevel(DisbursementVoucherDocument disbursementVoucherDocument) {
        final WorkflowDocument workflowDocument = disbursementVoucherDocument.getDocumentHeader().getWorkflowDocument();

        return getCurrentRouteLevels(workflowDocument).contains(DisbursementVoucherConstants.RouteLevelNames.ACCOUNT);
    }
}
