/*
 * 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.kuali.kfs.kns.kim.role.RoleTypeServiceBase;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.KFSKeyConstants;
import org.kuali.kfs.sys.identity.KfsKimAttributes;
import org.kuali.rice.core.api.uif.RemotableAttributeError;
import org.kuali.rice.kim.api.KimConstants;
import org.kuali.rice.kim.api.role.RoleMembership;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;


/**
 * This role type service was designed to be attached to the KFS-SYS User role
 */
public class FinancialSystemUserRoleTypeServiceImpl extends RoleTypeServiceBase {
    public static final String PERFORM_QUALIFIER_MATCH = "performQualifierMatch";


    @Override
    protected boolean performMatch(Map<String, String> qualification, Map<String, String> roleQualifier) {
        // if we can not find the qualifier which tells us to perform the match, just return true
        if (qualification == null || !Boolean.parseBoolean(qualification.get(PERFORM_QUALIFIER_MATCH))) {
            return true;
        }
        // if chart and org aren't on the assignment we don't care, since we are only matching to get the default chart/org for a
        // user in FinancialSystemUserServiceImpl
        if (StringUtils.isBlank(roleQualifier.get(KfsKimAttributes.CHART_OF_ACCOUNTS_CODE)) || StringUtils.isBlank(roleQualifier.get(KfsKimAttributes.ORGANIZATION_CODE))) {
            return false;
        }
        // exact match on namespace or qualifier namespace of kfs-sys works
        if (StringUtils.equals(roleQualifier.get(KimConstants.AttributeConstants.NAMESPACE_CODE), qualification.get(KimConstants.AttributeConstants.NAMESPACE_CODE))
            || StringUtils.equals(KFSConstants.CoreModuleNamespaces.KFS, roleQualifier.get(KimConstants.AttributeConstants.NAMESPACE_CODE))) {
            return true;
        }
        return false;
    }

    @Override
    public List<RoleMembership> getMatchingRoleMemberships(Map<String, String> qualification, List<RoleMembership> roleMemberList) {
        Map<String, String> translatedQualification = translateInputAttributes(qualification);
        validateRequiredAttributesAgainstReceived(translatedQualification);
        // if we can not find the qualifier which tells us to perform the match, just return all rows
        if (translatedQualification == null || translatedQualification.isEmpty() || !Boolean.parseBoolean(translatedQualification.get(PERFORM_QUALIFIER_MATCH))) {
            return roleMemberList;
        }
        // perform special matching to make a match that includes the namespace take priority
        RoleMembership namespaceMatch = null;
        RoleMembership nonNamespaceMatch = null;
        // look for match
        for (RoleMembership rmi : roleMemberList) {
            // if chart and org aren't on the assignment we don't care, since we are only matching to get the default chart/org for
            // a user in FinancialSystemUserServiceImpl
            if (!(StringUtils.isBlank(rmi.getQualifier().get(KfsKimAttributes.CHART_OF_ACCOUNTS_CODE)) || StringUtils.isBlank(rmi.getQualifier().get(KfsKimAttributes.ORGANIZATION_CODE)))) {
                if (StringUtils.equals(translatedQualification.get(KimConstants.AttributeConstants.NAMESPACE_CODE), rmi.getQualifier().get(KimConstants.AttributeConstants.NAMESPACE_CODE))) {
                    namespaceMatch = rmi;
                } else if (StringUtils.equals(KFSConstants.ParameterNamespaces.KFS, rmi.getQualifier().get(KimConstants.AttributeConstants.NAMESPACE_CODE))) {
                    nonNamespaceMatch = rmi;
                }
            }
        }
        List<RoleMembership> matchingMemberships = new ArrayList<RoleMembership>(1);
        if (nonNamespaceMatch != null || namespaceMatch != null) {
            matchingMemberships.add((namespaceMatch != null) ? namespaceMatch : nonNamespaceMatch);
        }
        return matchingMemberships;
    }

    /**
     * note: for validating KFS-SYS User membership
     * - if chart or org are specified, chart, org, and namespace are all required
     * - none are required if not
     * - can only have one assignment to the role for a given namespace - including no namespace
     *
     * @see org.kuali.rice.kim.service.support.impl.KimTypeInfoServiceBase#validateAttributes(org.kuali.rice.kim.bo.types.dto.AttributeSet)
     */
    @Override
    public List<RemotableAttributeError> validateAttributes(String kimTypeId, Map<String, String> attributes) {
        List<RemotableAttributeError> errorList = new ArrayList<RemotableAttributeError>();
        errorList.addAll(super.validateAttributes(kimTypeId, attributes));
        String chartCode = attributes.get(KfsKimAttributes.CHART_OF_ACCOUNTS_CODE);
        String organizationCode = attributes.get(KfsKimAttributes.ORGANIZATION_CODE);
        String namespaceCode = attributes.get(KimConstants.AttributeConstants.NAMESPACE_CODE);
        if (StringUtils.isBlank(chartCode) && StringUtils.isBlank(organizationCode)) {
            //remove chartofAccountCode, organizationCode and namespaceCode errors
            //Object results = GlobalVariables.getMessageMap().getErrorMessagesForProperty(attributeName);
            //RiceKeyConstants.ERROR_REQUIRED
            removeError(KfsKimAttributes.CHART_OF_ACCOUNTS_CODE, errorList);
            removeError(KfsKimAttributes.ORGANIZATION_CODE, errorList);
            if (StringUtils.isBlank(namespaceCode)) {
                removeError(KimConstants.AttributeConstants.NAMESPACE_CODE, errorList);
            }
        } //- if chart or org are specified, chart, org, and namespace are all required
        //- none are required if not
        else if (StringUtils.isNotEmpty(chartCode) || StringUtils.isNotEmpty(organizationCode)) {
            if (StringUtils.isBlank(chartCode) || StringUtils.isBlank(organizationCode) || StringUtils.isBlank(namespaceCode)) {
                errorList.add(RemotableAttributeError.Builder.create(KimConstants.AttributeConstants.NAMESPACE_CODE, KFSKeyConstants.ERROR_CHART_OR_ORG_NOTEMPTY_ALL_REQUIRED).build());
            }
        }

        return errorList;
    }

    protected void removeError(String attributeName, Collection<RemotableAttributeError> errorList) {
        Iterator<RemotableAttributeError> iter = errorList.iterator();
        while (iter.hasNext()) {
            if (StringUtils.equals(iter.next().getAttributeName(), attributeName)) {
                iter.remove();
            }
        }
    }

    @Override
    public List<RemotableAttributeError> validateUniqueAttributes(String kimTypeId, Map<String, String> newAttributes, Map<String, String> oldAttributes) {

        if (areAllAttributeValuesEmpty(newAttributes)) {
            return Collections.emptyList();
        } else
            return super.validateUniqueAttributes(kimTypeId, newAttributes, oldAttributes);
    }

    protected boolean areAllAttributeValuesEmpty(Map<String, String> attributes) {
        boolean areAllAttributesEmpty = true;
        if (attributes != null)
            for (String attributeNameKey : attributes.keySet()) {
                if (StringUtils.isNotBlank(attributes.get(attributeNameKey))) {
                    areAllAttributesEmpty = false;
                    break;
                }
            }
        return areAllAttributesEmpty;
    }

    @Override
    public List<String> getUniqueAttributes(String kimTypeId) {
        return Collections.singletonList(KimConstants.AttributeConstants.NAMESPACE_CODE);
    }

}
