/*
 *************************************************************************
 * The contents of this file are subject to the Openbravo  Public  License
 * Version  1.0  (the  "License"),  being   the  Mozilla   Public  License
 * Version 1.1  with a permitted attribution clause; you may not  use this
 * file except in compliance with the License. You  may  obtain  a copy of
 * the License at http://www.openbravo.com/legal/license.html
 * Software distributed under the License  is  distributed  on  an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific  language  governing  rights  and  limitations
 * under the License.
 ************************************************************************
 */

package com.everis.budget.conf.erpCommon.businessUtility;

import java.math.BigDecimal;
import java.util.List;

import org.hibernate.criterion.Expression;
import org.openbravo.dal.core.OBContext;
import org.openbravo.dal.service.OBCriteria;
import org.openbravo.dal.service.OBDal;
import org.openbravo.erpCommon.utility.OBError;

import com.everis.budget.conf.BUDGET;
import com.everis.budget.conf.BUDGETLINE;
import com.everis.budget.conf.BUDGETRULES;
import com.everis.budget.conf.ECOCLASS;
import com.everis.budget.conf.FUNCCLASS;

public class BudgetConfigurationUtility {

  public static OBError validateBudget(BUDGET budget) {
    OBError myMessage = null;
    myMessage = new OBError();

    final OBCriteria<BUDGETLINE> budgetLine = OBDal.getInstance().createCriteria(BUDGETLINE.class);
    budgetLine.add(Expression.eq(BUDGETLINE.PROPERTY_EVEBUCOBUDGET, budget));

    // Check that for each functional classification initial balance plus income amount equals
    // expense amount
    final List<BUDGETLINE> budgetLines = budgetLine.list();
    if (budgetLines.isEmpty()) {
      myMessage.setType("Error");
      myMessage.setTitle("@Error@");
      myMessage.setMessage("@EVEBUCO_BudgetNoLines@");
    }
    try {
      OBContext.setAdminMode();
      for (BUDGETLINE budgLines : budgetLines) {
        if (!validateFuncClass(budget, budgLines.getEvebucoFuncclass())) {
          myMessage.setType("Error");
          myMessage.setTitle("@Error@");
          myMessage.setMessage("@EVEBUCO_FuncClassNoValid@" + " '"
              + budgLines.getEvebucoFuncclass().getIdentifier() + "'.");
          return myMessage;
        }
      }
    } finally {
      OBContext.restorePreviousMode();
    }

    // Check that budget rules are complied
    myMessage = validateBudgetRules(budget);

    if (!"Error".equals(myMessage.getType())) {
      myMessage.setType("Success");
      myMessage.setTitle("@Success@");
      myMessage.setMessage("@EVEBUCO_BudgetCompletedSuccess@");
    }

    return myMessage;
  }

  public static boolean validateFuncClass(BUDGET budget, FUNCCLASS funcClass) {

    if (getBudgetLinesTotal(budget, funcClass, null, "EXP").compareTo(
        getBudgetLinesTotal(budget, funcClass, null, "INC")) == 0)
      return true;
    else
      return false;
  }

  public static OBError validateBudgetRules(BUDGET budget) {
    OBError myMessage = null;
    myMessage = new OBError();
    try {
      OBContext.setAdminMode();
      // Get all the budget rules that apply to the organization type of the budget
      final OBCriteria<BUDGETRULES> budgetRule = OBDal.getInstance().createCriteria(
          BUDGETRULES.class);
      budgetRule.add(Expression.eq(BUDGETRULES.PROPERTY_ORGANIZATIONTYPE, budget.getOrganization()
          .getOrganizationType()));

      // Check every budget rule
      final List<BUDGETRULES> budgetRules = budgetRule.list();
      for (BUDGETRULES budgRules : budgetRules) {

        // Sum initial balance, income amount and modified amount of the functional classification
        BigDecimal totalIncome = BigDecimal.ZERO;
        totalIncome = getBudgetLinesTotal(budget, budgRules.getEvebucoFuncclass(), null, "INC");

        // Sum expense amount and modified amount of the article's subconcepts
        BigDecimal totalExpense = BigDecimal.ZERO;
        final OBCriteria<ECOCLASS> ecoClassConcept = OBDal.getInstance().createCriteria(
            ECOCLASS.class);
        ecoClassConcept
            .add(Expression.eq(ECOCLASS.PROPERTY_PARENT, budgRules.getEvebucoEcoclass()));
        // List of concepts of this article
        final List<ECOCLASS> ecoClassConcepts = ecoClassConcept.list();
        for (ECOCLASS eClassConcepts : ecoClassConcepts) {
          final OBCriteria<ECOCLASS> ecoClassSubConcept = OBDal.getInstance().createCriteria(
              ECOCLASS.class);
          ecoClassSubConcept.add(Expression.eq(ECOCLASS.PROPERTY_PARENT, eClassConcepts));
          // List of subconcepts of this concept
          final List<ECOCLASS> ecoClassSubConcepts = ecoClassSubConcept.list();
          for (ECOCLASS eClassSubConcepts : ecoClassSubConcepts) {
            totalExpense = totalExpense.add(getBudgetLinesTotal(budget, budgRules
                .getEvebucoFuncclass(), eClassSubConcepts, "EXP"));
          }
        }

        // Multiply incomes per minimum percentage
        BigDecimal minLimit = BigDecimal.ZERO;
        minLimit = budgRules.getMinpercentage().divide(new BigDecimal("100"));
        minLimit = totalIncome.multiply(minLimit);

        // Expense must be equal or greater than minimum expected
        if (totalExpense.compareTo(minLimit) == -1) {
          myMessage.setType("Error");
          myMessage.setTitle("@Error@");
          myMessage.setMessage("@EVEBUCO_BudgetRuleNoValid@" + " '" + budgRules.getIdentifier()
              + "' " + "@EVEBUCO_AssignAtLeast@" + " " + budgRules.getMinpercentage() + "%" + " "
              + "@EVEBUCO_OfProgram@" + " '" + budgRules.getEvebucoFuncclass().getIdentifier()
              + "'," + " " + "@EVEBUCO_InOtherWords@" + ", " + "@EVEBUCO_AtLeast@" + " " + minLimit
              + budget.getClient().getCurrency().getSymbol() + " "
              + "@EVEBUCO_ForSubconceptsOfArticle@" + " '"
              + budgRules.getEvebucoEcoclass().getIdentifier() + "' " + "("
              + "@EVEBUCO_AssignedByNow@" + " " + totalExpense
              + budget.getClient().getCurrency().getSymbol() + ")" + ".");
          return myMessage;
        }
      }
    } finally {
      OBContext.restorePreviousMode();
    }
    return myMessage;
  }

  public static BigDecimal getBudgetLinesTotal(BUDGET budget, FUNCCLASS funcClass,
      ECOCLASS ecoClass, String type) {
    BigDecimal budgetLineTotal = BigDecimal.ZERO;
    BigDecimal initialBalance = BigDecimal.ZERO;
    BigDecimal incomeAmount = BigDecimal.ZERO;
    BigDecimal modifiedIncomeAmount = BigDecimal.ZERO;
    BigDecimal expenseAmount = BigDecimal.ZERO;
    BigDecimal modifiedExpenseAmount = BigDecimal.ZERO;

    final OBCriteria<BUDGETLINE> budgetLine = OBDal.getInstance().createCriteria(BUDGETLINE.class);
    budgetLine.add(Expression.eq(BUDGETLINE.PROPERTY_EVEBUCOBUDGET, budget));
    if (funcClass != null)
      budgetLine.add(Expression.eq(BUDGETLINE.PROPERTY_EVEBUCOFUNCCLASS, funcClass));
    if (ecoClass != null)
      budgetLine.add(Expression.eq(BUDGETLINE.PROPERTY_EVEBUCOECOCLASS, ecoClass));
    budgetLine.add(Expression.eq(BUDGETLINE.PROPERTY_TYPE, type));

    final List<BUDGETLINE> budgetLines = budgetLine.list();
    for (BUDGETLINE budgLines : budgetLines) {
      if ("INC".equals(type)) {
        initialBalance = initialBalance.add(budgLines.getInitialbalance());
        incomeAmount = incomeAmount.add(budgLines.getIncomeamount());
        modifiedIncomeAmount = modifiedIncomeAmount.add(budgLines.getModifiedamount());
      } else if ("EXP".equals(type)) {
        expenseAmount = expenseAmount.add(budgLines.getExpenseamount());
        modifiedExpenseAmount = modifiedExpenseAmount.add(budgLines.getModifiedamount());
      }
    }
    budgetLineTotal = initialBalance.add(incomeAmount).add(modifiedIncomeAmount).add(expenseAmount)
        .add(modifiedExpenseAmount);

    return budgetLineTotal;
  }
}