/*
 * 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.validation.impl;

import org.apache.commons.lang.StringUtils;
import org.kuali.kfs.krad.util.GlobalVariables;
import org.kuali.kfs.krad.util.ObjectUtils;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.KFSKeyConstants;
import org.kuali.kfs.sys.KFSPropertyConstants;
import org.kuali.kfs.sys.businessobject.AccountingLine;
import org.kuali.kfs.sys.document.validation.AccountingDocumentValidationBase;
import org.kuali.kfs.sys.document.validation.event.AttributedDocumentEvent;
import org.kuali.kfs.sys.service.impl.KfsParameterConstants;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;

/**
 * Validates that, for accounting lines which are identified as capital asset accounting lines,
 * they are unique so they can be handled individually by the Capital Asset Builder module.
 */
public class CapitalAssetAccountingLineUniquenessEnforcementValidation extends AccountingDocumentValidationBase {

    @Override
    public boolean validate(AttributedDocumentEvent event) {
        Collection<AccountingLine> capitalAssetLines = getCapitalAssetAccountingLines(getAllAccountingLines());
        // no matching lines, nothing to validate
        if (capitalAssetLines.isEmpty()) {
            return true;
        }
        return validateLineUniqueness(capitalAssetLines);
    }


    protected Collection<String> getCapitalAssetObjectSubTypes() {
        return getParameterService().getParameterValuesAsString(
            KfsParameterConstants.CAPITAL_ASSET_BUILDER_DOCUMENT.class,
            "FINANCIAL_PROCESSING_CAPITAL_OBJECT_SUB_TYPES");
        // JHK: I don't like the use of the constant above - but this parameter exists in the CAB module
        // Fortunately, since Rice 2.0 - the statement above will not blow up if CAB is not installed.
        // It will just return an empty list which will short-circuit the validation.
    }

    protected Collection<AccountingLine> getCapitalAssetAccountingLines(Collection<AccountingLine> allLines) {
        Collection<AccountingLine> capitalAssetAccountingLines = new ArrayList<AccountingLine>(allLines.size());
        Collection<String> capitalAssetObjectSubTypes = getCapitalAssetObjectSubTypes();
        // if none defined, exit
        if (capitalAssetObjectSubTypes.isEmpty()) {
            return capitalAssetAccountingLines;
        }
        for (AccountingLine line : allLines) {
            // we only care about expenses (not encumbrance or budget)
            // many documents leave the balance type blank when going to use the "default" of "AC"
            if (StringUtils.isNotBlank(line.getBalanceTypeCode()) && !StringUtils.equals(line.getBalanceTypeCode(), KFSConstants.BALANCE_TYPE_ACTUAL)) {
                continue;
            }
            // need to ensure that we have a financial object to test the sub type against - only refresh if necessary
            if (StringUtils.isBlank(line.getFinancialObjectCode())) {
                continue; // no object code
            }
            if (ObjectUtils.isNull(line.getObjectCode())
                || !StringUtils.equals(line.getChartOfAccountsCode(), line.getObjectCode().getChartOfAccountsCode())
                || !StringUtils.equals(line.getFinancialObjectCode(), line.getObjectCode().getFinancialObjectCode())) {
                line.refreshReferenceObject(KFSPropertyConstants.OBJECT_CODE);
            }
            if (ObjectUtils.isNull(line.getObjectCode())) {
                continue; // invalid object code
            }
            if (capitalAssetObjectSubTypes.contains(line.getObjectCode().getFinancialObjectSubTypeCode())) {
                capitalAssetAccountingLines.add(line);
            }
        }
        return capitalAssetAccountingLines;
    }

    protected boolean validateLineUniqueness(Collection<AccountingLine> capitalAssetAccountingLines) {
        HashSet<String> uniqueLineKeys = new HashSet<String>();
        for (AccountingLine line : capitalAssetAccountingLines) {
            if (!uniqueLineKeys.add(getLineUniquenessKey(line))) {
                GlobalVariables.getMessageMap().putError("sourceAccountingLines", KFSKeyConstants.ERROR_NONUNIQUE_CAPITAL_ASSET_ACCOUNTING_LINE);
                return false;
            }
        }
        return true;
    }

    protected String getLineUniquenessKey(AccountingLine line) {
        StringBuilder sb = new StringBuilder();
        sb.append(line.getChartOfAccountsCode());
        sb.append(line.getAccountNumber());
        sb.append(line.getSubAccountNumber());
        sb.append(line.getFinancialObjectCode());
        sb.append(line.getFinancialSubObjectCode());
        sb.append(line.getProjectCode());
        sb.append(line.getOrganizationReferenceId());
        return sb.toString();
    }

}
