/********************************************************************
 * Copyright © 2004 EDS. All rights reserved
 ********************************************************************/
package gov.va.med.esr.common.persistent.hibernate;

import gov.va.med.esr.common.model.financials.Asset;
import gov.va.med.esr.common.model.financials.Debt;
import gov.va.med.esr.common.model.financials.DependentFinancials;
import gov.va.med.esr.common.model.financials.Expense;
import gov.va.med.esr.common.model.financials.FinancialStatement;
import gov.va.med.esr.common.model.financials.Income;
import gov.va.med.esr.common.model.financials.RelationFinancials;
import gov.va.med.esr.common.model.financials.SpouseFinancials;
import gov.va.med.esr.common.model.lookup.AssetType;
import gov.va.med.esr.common.model.lookup.ExpenseType;
import gov.va.med.esr.common.model.lookup.IncomeType;
import gov.va.med.esr.common.model.person.Dependent;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.model.person.Spouse;
import gov.va.med.fw.model.AbstractKeyedEntity;

import java.math.BigDecimal;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * 
 * @author DNS   MANSOG
 * @version 1.0
 */
public class FinancialStatementTest
    extends AbstractKeyedEntityDAOTestCase {

    private static final int DF_COUNT     = 2;
    private static final int ASSET_COUNT  = 1;
    private static final int EXP_COUNT    = 1;
    private static final int INCOME_COUNT = 1;
    private Integer          incomeYear   = new Integer(2000);
    private Person           person;

    protected boolean shouldCommitCreate() {
        return true;
    }

    protected DAOTestMode[] setUpTests() {
        return new DAOTestMode[] { TEST_UPDATE_COMMIT };
    }

    protected void customSetUp() throws Exception {
        person = buildSimplePerson();
        super.customSetUp();
    }

    /* (non-Javadoc)
     * @see gov.va.med.esr.common.persistent.hibernate.AbstractKeyedEntityDAOTestCase#setUpTestObject()
     */
    protected AbstractKeyedEntity setUpTestObject() throws Exception {
        FinancialStatement stmt = buildFinancialStatements(true, DF_COUNT);
        person.setFinancialStatement(incomeYear, stmt);
        return person;
    }

    private FinancialStatement buildFinancialStatements(boolean buildSpouse,
            int dfCount) throws Exception {
        FinancialStatement stmt = createFinancialStatement();
        if (buildSpouse) {
            Spouse spouse = createSpouse();
            SpouseFinancials spouseFinancials = createSpouseFinancials(spouse,
                    getRandomAssetType(), getRandomIncomeType(),
                    getRandomExpenseType());            
            stmt.addSpouseFinancials(spouseFinancials);
            stmt.setIsPost2005Format(Boolean.FALSE);
        }
        for (int i = 0; i < dfCount; i++) {
            Dependent dependent = createDependent();
            DependentFinancials dependentFinancials = createDependentFinancials(
                    dependent, getRandomAssetType(), getRandomIncomeType(),
                    getRandomExpenseType());
            stmt.addDependentFinancials(dependentFinancials);
            stmt.setIsPost2005Format(Boolean.FALSE);
        }
        return stmt;
    }

    /* (non-Javadoc)
     * @see gov.va.med.esr.common.persistent.hibernate.AbstractKeyedEntityDAOTestCase#modifyTestObject(gov.va.med.fw.model.AbstractKeyedEntity)
     */
    protected void modifyTestObject(AbstractKeyedEntity obj) throws Exception {                
        Person p = (Person) obj;
        FinancialStatement fStmt = p.getFinancialStatement(incomeYear);
        validateFinancialStatement(fStmt);
        validateSpouseFinancials(fStmt.getActiveSpouseFinancials());
        validateDependentFinancials(fStmt, DF_COUNT);

        removeFinancialFromRelation(fStmt.getActiveSpouseFinancials());

        Set dependentFinancials = fStmt.getDependentFinancials();
        Iterator iterator = dependentFinancials.iterator();
        if (iterator.hasNext()) {
            DependentFinancials df = (DependentFinancials) iterator.next();
            removeFinancialFromRelation(df);
        }
        if (iterator.hasNext()) {
            DependentFinancials df = (DependentFinancials) iterator.next();
            createFinancialForRelation(df);
        }

        fStmt.removeDependentFinancials((DependentFinancials) fStmt.getDependentFinancials().iterator().next());
        fStmt.setMarriedLastCalendarYear(Boolean.TRUE);
        fStmt.setIsPost2005Format(Boolean.FALSE);
        
    }

    public void testSpouseFinancials() throws Exception {
        FinancialStatement stmt = buildFinancialStatements(true, 0);
        person.setFinancialStatement(incomeYear, stmt);

        Person savedPerson = (Person) saveAndLoadInTransaction(person);
        FinancialStatement fStmt = savedPerson.getFinancialStatement(incomeYear);
        validateFinancialStatement(fStmt);        
        validateSpouseFinancials(fStmt.getActiveSpouseFinancials());
    }

    public void testDependentFinancials() throws Exception {
        int dfCount = 2;
        FinancialStatement stmt = buildFinancialStatements(false, dfCount);
        person.setFinancialStatement(incomeYear, stmt);

        Person savedPerson = (Person) saveAndLoadInTransaction(person);
        FinancialStatement fStmt = savedPerson.getFinancialStatement(incomeYear);
        validateFinancialStatement(fStmt);
        validateDependentFinancials(fStmt, dfCount);
    }

    private void removeFinancialFromRelation(RelationFinancials rf) {
        rf.setDebt(null);
        AssetType assetType = (AssetType) rf.getAssets().keySet().iterator().next();
        rf.setAsset(assetType, null);
        IncomeType incomeType = (IncomeType) rf.getIncome().keySet().iterator().next();
        rf.setIncome(incomeType, null);
        ExpenseType expenseType = (ExpenseType) rf.getExpenses().keySet().iterator().next();
        rf.setExpense(expenseType, null);
    }

    private void updateFinancialFromRelation(RelationFinancials rf) {
        Debt debt = rf.getDebt();
        debt.setAmount(new BigDecimal(555));
        AssetType assetType = (AssetType) rf.getAssets().keySet().iterator().next();
        rf.getAsset(assetType).setAmount(new BigDecimal(444));
        IncomeType incomeType = (IncomeType) rf.getIncome().keySet().iterator().next();
        rf.getIncome(incomeType).setAmount(new BigDecimal(5465));
        ExpenseType expenseType = (ExpenseType) rf.getExpenses().keySet().iterator().next();
        rf.getExpense(expenseType).setAmount(new BigDecimal(3444));
    }

    private void createFinancialForRelation(RelationFinancials rf) {
        rf.setDebt(createDebt(555));
        AssetType assetType = (AssetType) rf.getAssets().keySet().iterator().next();
        rf.setAsset(assetType, createAsset(444));
        IncomeType incomeType = (IncomeType) rf.getIncome().keySet().iterator().next();
        rf.setIncome(incomeType, createIncome(58756));
        ExpenseType expenseType = (ExpenseType) rf.getExpenses().keySet().iterator().next();
        rf.setExpense(expenseType, createExpense(54));
    }

    private void validateFinancialStatement(FinancialStatement fStmt) {
        assertNotNull(
                "Must have a financial statement for the given year as it was just persisted.",
                fStmt);
        assertEquals("Income year is not same as the saved one.", incomeYear,
                fStmt.getIncomeYear());
    }

    private void validateSpouseFinancials(SpouseFinancials sf) {
        validateDebt(sf.getDebt());
        validateAsset(sf, ASSET_COUNT);
        validateIncome(sf, INCOME_COUNT);
        validateExpense(sf, EXP_COUNT);
    }

    private void validateDependentFinancials(FinancialStatement fStmt,
            int expectedCount) {
        Set dfSet = fStmt.getDependentFinancials();
        assertNotNull("Dependent financials collection must not be null.",
                dfSet);
        assertTrue("Must have " + expectedCount
                + " dependent financials object.",
                dfSet.size() == expectedCount);
        for (Iterator iter = dfSet.iterator(); iter.hasNext();) {
            DependentFinancials df = (DependentFinancials) iter.next();
            validateDebt(df.getDebt());
            validateAsset(df, ASSET_COUNT);
            validateIncome(df, INCOME_COUNT);
            validateExpense(df, EXP_COUNT);
        }
    }

    private void validateIncome(RelationFinancials rf, int expectedCount) {
        Map incomes = rf.getIncome();
        assertTrue("Must have atleast one income.",
                incomes.size() == expectedCount);
        IncomeType incomeType = (IncomeType) incomes.keySet().iterator().next();
        Income income = rf.getIncome(incomeType);
        assertNotNull("Income must not be null", income);
    }

    private void validateExpense(RelationFinancials rf, int expectedCount) {
        Map expenses = rf.getExpenses();
        assertTrue("Must have atleast one Expense.",
                expenses.size() == expectedCount);
        ExpenseType expenseType = (ExpenseType) expenses.keySet().iterator().next();
        Expense expense = rf.getExpense(expenseType);
        assertNotNull("Expense must not be null", expense);
    }

    private void validateAsset(RelationFinancials rf, int expectedCount) {
        Map assets = rf.getAssets();
        assertTrue("Must have atleast one asset.",
                assets.size() == expectedCount);
        AssetType assetType = (AssetType) assets.keySet().iterator().next();
        Asset asset = rf.getAsset(assetType);
        assertNotNull("Asset must not be null", asset);
    }

    /**
     * @param debt
     */
    private void validateDebt(Debt debt) {
        assertNotNull("Debt must not be null.", debt);
    }

}