/*
 * 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.kuali.kfs.kns.document.authorization.DocumentAuthorizerBase;
import org.kuali.kfs.krad.document.Document;
import org.kuali.kfs.krad.util.KRADConstants;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.businessobject.AccountingLine;
import org.kuali.kfs.sys.document.AccountingDocument;
import org.kuali.kfs.sys.document.authorization.AccountingDocumentAuthorizerBase;
import org.kuali.kfs.sys.identity.KfsKimAttributes;
import org.kuali.rice.kim.api.KimConstants;
import org.kuali.rice.kim.api.identity.Person;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * The customized document authorizer for the Service Billing document
 */
public class ServiceBillingDocumentAuthorizer extends AccountingDocumentAuthorizerBase {
    protected static String serviceBillingDocumentTypeName;

    /**
     * Overridden to only allow error correction and copy actions if the current user has Modify Accounting Document permission on every accounting line on the document
     *
     * @see org.kuali.kfs.sys.document.authorization.FinancialSystemTransactionalDocumentAuthorizerBase#getDocumentActions(org.kuali.rice.krad.document.Document, org.kuali.rice.kim.api.identity.Person, java.util.Set)
     */
    @Override
    public Set<String> getDocumentActions(Document document, Person user, Set<String> documentActionsFromPresentationController) {
        Set<String> documentActions = super.getDocumentActions(document, user, documentActionsFromPresentationController);

        boolean canCopyOrErrorCorrect = (documentActions.contains(KRADConstants.KUALI_ACTION_CAN_COPY) || documentActions.contains(KFSConstants.KFS_ACTION_CAN_ERROR_CORRECT)) ? canModifyAllSourceAccountingLines(document, user) : true;

        if (documentActions.contains(KRADConstants.KUALI_ACTION_CAN_COPY)) {
            if (!canCopyOrErrorCorrect) {
                documentActions.remove(KRADConstants.KUALI_ACTION_CAN_COPY);
            }
        }
        if (documentActions.contains(KFSConstants.KFS_ACTION_CAN_ERROR_CORRECT)) {
            if (!canCopyOrErrorCorrect) {
                documentActions.remove(KFSConstants.KFS_ACTION_CAN_ERROR_CORRECT);
            }
        }
        return documentActions;
    }

    /**
     * Determines if the given user has permission to modify all accounting lines on the document
     *
     * @param document the document with source accounting lines to check
     * @param user     the user to check
     * @return true if the user can modify all the accounting lines, false otherwise
     */
    protected boolean canModifyAllSourceAccountingLines(Document document, Person user) {
        for (Object accountingLineAsObject : ((AccountingDocument) document).getSourceAccountingLines()) {
            if (!canModifyAccountingLine(document, ((AccountingLine) accountingLineAsObject), user)) return false;
        }
        return true;
    }

    /**
     * Determines if the given user can modify the given accounting line, which is a source line on the given document
     *
     * @param document       a document with source accounting lines
     * @param accountingLine the accounting line to check the modifyability of
     * @param user           the user being checked
     * @return true if the user can modify the given accounting line, false otherwise
     */
    public boolean canModifyAccountingLine(Document document, AccountingLine accountingLine, Person user) {
        return isAuthorizedByTemplate(document, KFSConstants.CoreModuleNamespaces.KFS, KFSConstants.PermissionTemplate.MODIFY_ACCOUNTING_LINES.name, user.getPrincipalId(), buildPermissionDetails(document), buildRoleQualifiers(accountingLine));
    }

    /**
     * Builds the permission details map for permission check
     *
     * @param document the document, which is used to find the real document type name
     * @return a Map of permissionDetail values
     */
    protected Map<String, String> buildPermissionDetails(Document document) {
        Map<String, String> permissionDetails = new HashMap<String, String>();
        permissionDetails.put(KimConstants.AttributeConstants.DOCUMENT_TYPE_NAME, getDocumentTypeName(document)); // document type name
        permissionDetails.put(KfsKimAttributes.ROUTE_NODE_NAME, DocumentAuthorizerBase.PRE_ROUTING_ROUTE_NAME); // route node = PreRoute
        permissionDetails.put(KimConstants.AttributeConstants.PROPERTY_NAME, "sourceAccountingLines"); // property = sourceAccountingLines
        return permissionDetails;
    }

    /**
     * Looks up in the data dictionary the document type name
     *
     * @param document the document to find a document type name for
     * @return the document type name
     */
    protected String getDocumentTypeName(Document document) {
        if (serviceBillingDocumentTypeName == null) {
            serviceBillingDocumentTypeName = getDataDictionaryService().getDocumentTypeNameByClass(document.getClass());
        }
        return serviceBillingDocumentTypeName;
    }

    /**
     * Builds a map of role qualifiers, each containing the chart and account of the given accounting line
     *
     * @param accountingLine the accounting line to build role qualifiers for
     * @return the Map of role qualifiers
     */
    protected Map<String, String> buildRoleQualifiers(AccountingLine accountingLine) {
        Map<String, String> roleQualifiers = new HashMap<String, String>();
        roleQualifiers.put(KfsKimAttributes.CHART_OF_ACCOUNTS_CODE, accountingLine.getChartOfAccountsCode());
        roleQualifiers.put(KfsKimAttributes.ACCOUNT_NUMBER, accountingLine.getAccountNumber());
        return roleQualifiers;
    }
}
