/*
 * 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.coa.identity;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.kuali.kfs.krad.util.GlobalVariables;
import org.kuali.kfs.krad.util.KRADUtils;
import org.kuali.kfs.sys.identity.KfsKimAttributes;
import org.kuali.rice.core.api.uif.RemotableAttributeError;
import org.kuali.rice.core.api.uif.RemotableAttributeError.Builder;
import org.kuali.rice.core.api.util.RiceKeyConstants;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.kim.api.common.attribute.KimAttribute;
import org.kuali.rice.kim.api.type.KimType;
import org.kuali.rice.kim.api.type.KimTypeAttribute;

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

public class AccountingOrganizationHierarchyReviewRoleTypeServiceImpl extends OrganizationHierarchyReviewRoleTypeServiceImpl {
    private static final Logger LOG = Logger.getLogger(AccountingOrganizationHierarchyReviewRoleTypeServiceImpl.class);

    /**
     * Create role type service - org.kuali.kfs.coa.identity.AccountingOrganizationHierarchyReviewRoleTypeService for
     * KFS-COA/"Organization: Always Hierarchical, Document Type & Accounting" Attributes: Chart Code (required) Organization Code
     * Document Type Name From Amount To Amount Override Code - total amount will be passed in as qualification and role type
     * service will need to compare it to from and to amount qualifier values for assignees Requirements: - Traverse the org
     * hierarchy but not the document type hierarchy - from amount must be null or <= total amount supplied / to amount must be null
     * or >= total amount supplied, and null override code on assignment matches all override codes
     *
     * @see org.kuali.kfs.coa.identity.OrganizationOptionalHierarchyRoleTypeServiceImpl#performMatch(org.kuali.rice.kim.bo.types.dto.AttributeSet,
     * org.kuali.rice.kim.bo.types.dto.AttributeSet)
     */
    @Override
    public boolean performMatch(Map<String, String> qualification, Map<String, String> roleQualifier) {
        return doesOverrideCodeMatch(qualification, roleQualifier)
            && isValidTotalAmount(qualification, roleQualifier)
            && super.performMatch(qualification, roleQualifier);
    }

    protected boolean doesOverrideCodeMatch(Map<String, String> qualification, Map<String, String> roleQualifier) {
        return qualification == null
            || roleQualifier == null
            || StringUtils.isBlank(qualification.get(KfsKimAttributes.ACCOUNTING_LINE_OVERRIDE_CODE))
            || StringUtils.isBlank(roleQualifier.get(KfsKimAttributes.ACCOUNTING_LINE_OVERRIDE_CODE))
            || qualification.get(KfsKimAttributes.ACCOUNTING_LINE_OVERRIDE_CODE)
            .equals(roleQualifier.get(KfsKimAttributes.ACCOUNTING_LINE_OVERRIDE_CODE));
    }

    protected boolean isValidTotalAmount(Map<String, String> qualification, Map<String, String> roleQualifier) {
        boolean isValidTotalAmount = false;
        if (qualification == null || roleQualifier == null) {
            return false;
        }
        String totalAmountStr = qualification.get(KfsKimAttributes.FINANCIAL_DOCUMENT_TOTAL_AMOUNT);
        if (StringUtils.isBlank(totalAmountStr)) {
            return false;
        }
        try {
            KualiDecimal totalAmount = new KualiDecimal(totalAmountStr);
            String toAmountStr = roleQualifier.get(KfsKimAttributes.TO_AMOUNT);
            String fromAmountStr = roleQualifier.get(KfsKimAttributes.FROM_AMOUNT);
            if ((StringUtils.isBlank(toAmountStr) || new KualiDecimal(toAmountStr).isGreaterEqual(totalAmount))
                && (StringUtils.isBlank(fromAmountStr) || new KualiDecimal(fromAmountStr).isLessEqual(totalAmount))) {
                isValidTotalAmount = true;
            }
        } catch (Exception ex) {
            isValidTotalAmount = false;
            LOG.error("Exception comparing document amount to role qualifiers.", ex);
        }
        return isValidTotalAmount;
    }

    @Override
    public List<RemotableAttributeError> validateUnmodifiableAttributes(
        String kimTypeId, Map<String, String> originalAttributeSet, Map<String, String> newAttributeSet) {
        List<RemotableAttributeError> validationErrors = super.validateUnmodifiableAttributes(kimTypeId, originalAttributeSet, newAttributeSet);
        List<String> attributeErrors = null;
        KimAttribute attributeImpl;

        String fromAmountRoleMember = getAttributeValue(originalAttributeSet, KfsKimAttributes.FROM_AMOUNT);
        String fromAmountDelegationMember = getAttributeValue(newAttributeSet, KfsKimAttributes.FROM_AMOUNT);
        KimType kimType = getTypeInfoService().getKimType(kimTypeId);
        KimTypeAttribute attributeInfo;
        if (isLesserNumber(fromAmountDelegationMember, fromAmountRoleMember)) {
            attributeInfo = kimType.getAttributeDefinitionById(KfsKimAttributes.FROM_AMOUNT);
            GlobalVariables.getMessageMap().putError(
                KfsKimAttributes.FROM_AMOUNT, RiceKeyConstants.ERROR_DELEGATION_FROM_AMOUNT_LESSER,
                getDataDictionaryService().getAttributeLabel(attributeInfo.getKimAttribute().getComponentName(), KfsKimAttributes.FROM_AMOUNT));
            attributeErrors = extractErrorsFromGlobalVariablesErrorMap(KfsKimAttributes.FROM_AMOUNT);
        }

        Builder fromBuilder = RemotableAttributeError.Builder.create(KfsKimAttributes.FROM_AMOUNT);

        if (attributeErrors != null) {
            for (String err : attributeErrors) {
                fromBuilder.getErrors().add(err);
            }

            validationErrors.add(fromBuilder.build());

            attributeErrors = null;
        }

        String toAmountRoleMember = getAttributeValue(originalAttributeSet, KfsKimAttributes.TO_AMOUNT);
        String toAmountDelegationMember = getAttributeValue(newAttributeSet, KfsKimAttributes.TO_AMOUNT);
        if (StringUtils.isNotEmpty(toAmountRoleMember) && isGreaterNumber(toAmountDelegationMember, toAmountRoleMember)) {
            attributeInfo = kimType.getAttributeDefinitionById(KfsKimAttributes.TO_AMOUNT);
            GlobalVariables.getMessageMap().putError(
                KfsKimAttributes.TO_AMOUNT, RiceKeyConstants.ERROR_DELEGATION_TO_AMOUNT_GREATER,
                getDataDictionaryService().getAttributeLabel(attributeInfo.getKimAttribute().getComponentName(), KfsKimAttributes.TO_AMOUNT));
            attributeErrors = extractErrorsFromGlobalVariablesErrorMap(KfsKimAttributes.TO_AMOUNT);
        }

        Builder toBuilder = RemotableAttributeError.Builder.create(KfsKimAttributes.TO_AMOUNT);

        if (attributeErrors != null) {


            for (String err : attributeErrors) {
                toBuilder.getErrors().add(err);
            }
            validationErrors.add(toBuilder.build());

            attributeErrors = null;
        }

        return validationErrors;
    }

    protected boolean isLesserNumber(String numberStr1, String numberStr2) {
        if (StringUtils.isBlank(numberStr1)) {
            numberStr1 = "0";
        }
        if (StringUtils.isBlank(numberStr2)) {
            numberStr2 = "0";
        }
        int number1 = KRADUtils.getIntegerValue(numberStr1);
        int number2 = KRADUtils.getIntegerValue(numberStr2);
        return number1 < number2;
    }

    protected boolean isGreaterNumber(String numberStr1, String numberStr2) {
        if (StringUtils.isBlank(numberStr1)) {
            numberStr1 = "0";
        }
        if (StringUtils.isBlank(numberStr2)) {
            numberStr2 = "0";
        }
        int number1 = KRADUtils.getIntegerValue(numberStr1);
        int number2 = KRADUtils.getIntegerValue(numberStr2);
        return number1 > number2;
    }

}
